1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopengltexturecache_p.h"
5#include <private/qopengltextureuploader_p.h>
6#include <qmath.h>
7#include <qopenglfunctions.h>
8#include <private/qimagepixmapcleanuphooks_p.h>
9#include <qpa/qplatformpixmap.h>
10
11#include <qtopengl_tracepoints_p.h>
12
13QT_BEGIN_NAMESPACE
14
15class QOpenGLTextureCacheWrapper
16{
17public:
18 QOpenGLTextureCacheWrapper()
19 {
20 QImagePixmapCleanupHooks::instance()->addPlatformPixmapModificationHook(cleanupTexturesForPixmapData);
21 QImagePixmapCleanupHooks::instance()->addPlatformPixmapDestructionHook(cleanupTexturesForPixmapData);
22 QImagePixmapCleanupHooks::instance()->addImageHook(cleanupTexturesForCacheKey);
23 }
24
25 ~QOpenGLTextureCacheWrapper()
26 {
27 QImagePixmapCleanupHooks::instance()->removePlatformPixmapModificationHook(cleanupTexturesForPixmapData);
28 QImagePixmapCleanupHooks::instance()->removePlatformPixmapDestructionHook(cleanupTexturesForPixmapData);
29 QImagePixmapCleanupHooks::instance()->removeImageHook(cleanupTexturesForCacheKey);
30 }
31
32 QOpenGLTextureCache *cacheForContext(QOpenGLContext *context) {
33 QMutexLocker lock(&m_mutex);
34 return m_resource.value<QOpenGLTextureCache>(context);
35 }
36
37 static void cleanupTexturesForCacheKey(qint64 key);
38 static void cleanupTexturesForPixmapData(QPlatformPixmap *pmd);
39
40private:
41 QOpenGLMultiGroupSharedResource m_resource;
42 QMutex m_mutex;
43};
44
45Q_GLOBAL_STATIC(QOpenGLTextureCacheWrapper, qt_texture_caches)
46
47QOpenGLTextureCache *QOpenGLTextureCache::cacheForContext(QOpenGLContext *context)
48{
49 return qt_texture_caches()->cacheForContext(context);
50}
51
52void QOpenGLTextureCacheWrapper::cleanupTexturesForCacheKey(qint64 key)
53{
54 QList<QOpenGLSharedResource *> resources = qt_texture_caches()->m_resource.resources();
55 for (QList<QOpenGLSharedResource *>::iterator it = resources.begin(); it != resources.end(); ++it)
56 static_cast<QOpenGLTextureCache *>(*it)->invalidate(key);
57}
58
59void QOpenGLTextureCacheWrapper::cleanupTexturesForPixmapData(QPlatformPixmap *pmd)
60{
61 cleanupTexturesForCacheKey(key: pmd->cacheKey());
62}
63
64static quint64 cacheSize()
65{
66 bool ok = false;
67 const int envCacheSize = qEnvironmentVariableIntValue(varName: "QT_OPENGL_TEXTURE_CACHE_SIZE", ok: &ok);
68 if (ok)
69 return envCacheSize;
70
71 return 1024 * 1024; // 1024 MB cache default
72}
73
74QOpenGLTextureCache::QOpenGLTextureCache(QOpenGLContext *ctx)
75 : QOpenGLSharedResource(ctx->shareGroup())
76 , m_cache(cacheSize())
77{
78}
79
80QOpenGLTextureCache::~QOpenGLTextureCache()
81{
82}
83
84QOpenGLTextureCache::BindResult QOpenGLTextureCache::bindTexture(QOpenGLContext *context,
85 const QPixmap &pixmap,
86 QOpenGLTextureUploader::BindOptions options)
87{
88 if (pixmap.isNull())
89 return { .id: 0, .flags: {} };
90 QMutexLocker locker(&m_mutex);
91 qint64 key = pixmap.cacheKey();
92
93 // A QPainter is active on the image - take the safe route and replace the texture.
94 if (!pixmap.paintingActive()) {
95 QOpenGLCachedTexture *entry = m_cache.object(key);
96 if (entry && entry->options() == options) {
97 context->functions()->glBindTexture(GL_TEXTURE_2D, texture: entry->id());
98 return { .id: entry->id(), .flags: {} };
99 }
100 }
101
102 BindResult result = bindTexture(context, key, image: pixmap.toImage(), options);
103 if (result.id > 0)
104 QImagePixmapCleanupHooks::enableCleanupHooks(pixmap);
105
106 return result;
107}
108
109QOpenGLTextureCache::BindResult QOpenGLTextureCache::bindTexture(QOpenGLContext *context,
110 const QImage &image,
111 QOpenGLTextureUploader::BindOptions options)
112{
113 if (image.isNull())
114 return { .id: 0, .flags: {} };
115 QMutexLocker locker(&m_mutex);
116 qint64 key = image.cacheKey();
117
118 // A QPainter is active on the image - take the safe route and replace the texture.
119 if (!image.paintingActive()) {
120 QOpenGLCachedTexture *entry = m_cache.object(key);
121 if (entry && entry->options() == options) {
122 context->functions()->glBindTexture(GL_TEXTURE_2D, texture: entry->id());
123 return { .id: entry->id(), .flags: {} };
124 }
125 }
126
127 QImage img = image;
128 if (!context->functions()->hasOpenGLFeature(feature: QOpenGLFunctions::NPOTTextures))
129 options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
130
131 BindResult result = bindTexture(context, key, image: img, options);
132 if (result.id > 0)
133 QImagePixmapCleanupHooks::enableCleanupHooks(image);
134
135 return result;
136}
137
138Q_TRACE_POINT(qtopengl, QOpenGLTextureCache_bindTexture_entry, QOpenGLContext *context, qint64 key, const unsigned char *image, int options);
139Q_TRACE_POINT(qtopengl, QOpenGLTextureCache_bindTexture_exit);
140
141QOpenGLTextureCache::BindResult QOpenGLTextureCache::bindTexture(QOpenGLContext *context,
142 qint64 key,
143 const QImage &image,
144 QOpenGLTextureUploader::BindOptions options)
145{
146 Q_TRACE_SCOPE(QOpenGLTextureCache_bindTexture, context, key, image.bits(), options);
147
148 GLuint id;
149 QOpenGLFunctions *funcs = context->functions();
150 funcs->glGenTextures(n: 1, textures: &id);
151 funcs->glBindTexture(GL_TEXTURE_2D, texture: id);
152
153 int cost = QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, image, options);
154
155 m_cache.insert(key, object: new QOpenGLCachedTexture(id, options, context), cost: cost / 1024);
156
157 return { .id: id, .flags: BindResultFlag::NewTexture };
158}
159
160void QOpenGLTextureCache::invalidate(qint64 key)
161{
162 QMutexLocker locker(&m_mutex);
163 m_cache.remove(key);
164}
165
166void QOpenGLTextureCache::invalidateResource()
167{
168 m_cache.clear();
169}
170
171void QOpenGLTextureCache::freeResource(QOpenGLContext *)
172{
173 Q_ASSERT(false); // the texture cache lives until the context group disappears
174}
175
176static void freeTexture(QOpenGLFunctions *funcs, GLuint id)
177{
178 funcs->glDeleteTextures(n: 1, textures: &id);
179}
180
181QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context) : m_options(options)
182{
183 m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture);
184}
185
186QT_END_NAMESPACE
187

source code of qtbase/src/opengl/qopengltexturecache.cpp