1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtOpenGL module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #ifndef QGL_P_H
41 #define QGL_P_H
42
43 //
44 // W A R N I N G
45 // -------------
46 //
47 // This file is not part of the Qt API. It exists for the convenience
48 // of the QGLWidget class. This header file may change from
49 // version to version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53
54 #include "QtOpenGL/qgl.h"
55 #include "QtOpenGL/qglcolormap.h"
56 #include "QtCore/qmap.h"
57 #include "QtCore/qthread.h"
58 #include "QtCore/qthreadstorage.h"
59 #include "QtCore/qhashfunctions.h"
60 #include "QtCore/qatomic.h"
61 #include "QtWidgets/private/qwidget_p.h"
62 #include "QtGui/private/qopenglcontext_p.h"
63 #include "QtGui/private/qopenglextensions_p.h"
64 #include "qcache.h"
65 #include "qglpaintdevice_p.h"
66
67 #include <QtGui/QOpenGLContext>
68
69 QT_BEGIN_NAMESPACE
70
71 class QGLContext;
72 class QGLOverlayWidget;
73 class QPixmap;
74 class QOpenGLExtensions;
75
76 class QGLFormatPrivate
77 {
78 public:
QGLFormatPrivate()79 QGLFormatPrivate()
80 : ref(1)
81 {
82 opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering
83 | QGL::StencilBuffer | QGL::DeprecatedFunctions;
84 pln = 0;
85 depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1;
86 numSamples = -1;
87 swapInterval = -1;
88 majorVersion = 2;
89 minorVersion = 0;
90 profile = QGLFormat::NoProfile;
91 }
QGLFormatPrivate(const QGLFormatPrivate * other)92 QGLFormatPrivate(const QGLFormatPrivate *other)
93 : ref(1),
94 opts(other->opts),
95 pln(other->pln),
96 depthSize(other->depthSize),
97 accumSize(other->accumSize),
98 stencilSize(other->stencilSize),
99 redSize(other->redSize),
100 greenSize(other->greenSize),
101 blueSize(other->blueSize),
102 alphaSize(other->alphaSize),
103 numSamples(other->numSamples),
104 swapInterval(other->swapInterval),
105 majorVersion(other->majorVersion),
106 minorVersion(other->minorVersion),
107 profile(other->profile)
108 {
109 }
110 QAtomicInt ref;
111 QGL::FormatOptions opts;
112 int pln;
113 int depthSize;
114 int accumSize;
115 int stencilSize;
116 int redSize;
117 int greenSize;
118 int blueSize;
119 int alphaSize;
120 int numSamples;
121 int swapInterval;
122 int majorVersion;
123 int minorVersion;
124 QGLFormat::OpenGLContextProfile profile;
125 };
126
127 class Q_OPENGL_EXPORT QGLWidgetPrivate : public QWidgetPrivate
128 {
Q_DECLARE_PUBLIC(QGLWidget)129 Q_DECLARE_PUBLIC(QGLWidget)
130 public:
131 QGLWidgetPrivate() : QWidgetPrivate()
132 , disable_clear_on_painter_begin(false)
133 , parent_changing(false)
134 {
135 }
136
~QGLWidgetPrivate()137 ~QGLWidgetPrivate() {}
138
139 void init(QGLContext *context, const QGLWidget* shareWidget);
140 void initContext(QGLContext *context, const QGLWidget* shareWidget);
141 bool renderCxPm(QPixmap *pixmap);
142 void cleanupColormaps();
aboutToDestroy()143 void aboutToDestroy() override {
144 if (glcx && !parent_changing)
145 glcx->reset();
146 }
147
148 bool makeCurrent();
149
150 QGLContext *glcx;
151 QGLWidgetGLPaintDevice glDevice;
152 bool autoSwap;
153
154 QGLColormap cmap;
155
156 bool disable_clear_on_painter_begin;
157 bool parent_changing;
158 };
159
160 // QGLContextPrivate has the responsibility of creating context groups.
161 // QGLContextPrivate maintains the reference counter and destroys
162 // context groups when needed.
163 class QGLContextGroup
164 {
165 public:
166 ~QGLContextGroup();
167
context()168 const QGLContext *context() const {return m_context;}
isSharing()169 bool isSharing() const { return m_shares.size() >= 2; }
shares()170 QList<const QGLContext *> shares() const { return m_shares; }
171
172 static void addShare(const QGLContext *context, const QGLContext *share);
173 static void removeShare(const QGLContext *context);
174
175 private:
176 QGLContextGroup(const QGLContext *context);
177
178 const QGLContext *m_context; // context group's representative
179 QList<const QGLContext *> m_shares;
180 QAtomicInt m_refs;
181
182 friend class QGLContext;
183 friend class QGLContextPrivate;
184 friend class QGLContextGroupResourceBase;
185 };
186
187 // Get the context that resources for "ctx" will transfer to once
188 // "ctx" is destroyed. Returns null if nothing is sharing with ctx.
189 const QGLContext *qt_gl_transfer_context(const QGLContext *);
190
191 /*
192 QGLTemporaryContext - the main objective of this class is to have a way of
193 creating a GL context and making it current, without going via QGLWidget
194 and friends. At certain points during GL initialization we need a current
195 context in order decide what GL features are available, and to resolve GL
196 extensions. Having a light-weight way of creating such a context saves
197 initial application startup time, and it doesn't wind up creating recursive
198 conflicts.
199 The class currently uses a private d pointer to hide the platform specific
200 types. This could possibly been done inline with #ifdef'ery, but it causes
201 major headaches on e.g. X11 due to namespace pollution.
202 */
203 class QGLTemporaryContextPrivate;
204 class QGLTemporaryContext {
205 public:
206 explicit QGLTemporaryContext(bool directRendering = true, QWidget *parent = nullptr);
207 ~QGLTemporaryContext();
208
209 private:
210 QScopedPointer<QGLTemporaryContextPrivate> d;
211 };
212
213 class QGLTexture;
214 class QGLTextureDestroyer;
215
216 // This probably needs to grow to GL_MAX_VERTEX_ATTRIBS, but 3 is ok for now as that's
217 // all the GL2 engine uses:
218 #define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3
219
220 class QGLContextResourceBase;
221
222 class QGLContextPrivate
223 {
224 Q_DECLARE_PUBLIC(QGLContext)
225 public:
226 explicit QGLContextPrivate(QGLContext *context);
227 ~QGLContextPrivate();
228 QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format,
229 QGLContext::BindOptions options);
230 QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key,
231 QGLContext::BindOptions options);
232 QGLTexture *bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
233 QGLContext::BindOptions options);
234 QGLTexture *textureCacheLookup(const qint64 key, GLenum target);
235 void init(QPaintDevice *dev, const QGLFormat &format);
236 int maxTextureSize();
237
238 void cleanup();
239
240 void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true);
241 void syncGlState(); // Makes sure the GL context's state is what we think it is
242 void swapRegion(const QRegion ®ion);
243
244 QOpenGLContext *guiGlContext;
245 // true if QGLContext owns the QOpenGLContext (for who deletes who)
246 bool ownContext;
247
248 void setupSharing();
249 void refreshCurrentFbo();
250 void setCurrentFbo(GLuint fbo);
251
252 QGLFormat glFormat;
253 QGLFormat reqFormat;
254 GLuint fbo;
255
256 uint valid : 1;
257 uint sharing : 1;
258 uint initDone : 1;
259 uint crWin : 1;
260 uint internal_context : 1;
261 uint version_flags_cached : 1;
262
263 // workarounds for driver/hw bugs on different platforms
264 uint workaround_needsFullClearOnEveryFrame : 1;
265 uint workaround_brokenFBOReadBack : 1;
266 uint workaround_brokenTexSubImage : 1;
267 uint workaroundsCached : 1;
268
269 uint workaround_brokenTextureFromPixmap : 1;
270 uint workaround_brokenTextureFromPixmap_init : 1;
271
272 uint workaround_brokenAlphaTexSubImage : 1;
273 uint workaround_brokenAlphaTexSubImage_init : 1;
274
275 QPaintDevice *paintDevice;
276 QSize readback_target_size;
277 QColor transpColor;
278 QGLContext *q_ptr;
279 QGLFormat::OpenGLVersionFlags version_flags;
280
281 QGLContextGroup *group;
282 GLint max_texture_size;
283
284 GLuint current_fbo;
285 GLuint default_fbo;
286 QPaintEngine *active_engine;
287 QGLTextureDestroyer *texture_destroyer;
288
289 QGLFunctions *functions;
290
291 bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT];
292
contextGroup(const QGLContext * ctx)293 static inline QGLContextGroup *contextGroup(const QGLContext *ctx) { return ctx->d_ptr->group; }
294
295 static void setCurrentContext(QGLContext *context);
296 };
297
298 // Temporarily make a context current if not already current or
299 // shared with the current contex. The previous context is made
300 // current when the object goes out of scope.
301 class Q_OPENGL_EXPORT QGLShareContextScope
302 {
303 public:
QGLShareContextScope(const QGLContext * ctx)304 QGLShareContextScope(const QGLContext *ctx)
305 : m_oldContext(nullptr)
306 {
307 QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
308 if (currentContext != ctx && !QGLContext::areSharing(ctx, currentContext)) {
309 m_oldContext = currentContext;
310 m_ctx = const_cast<QGLContext *>(ctx);
311 m_ctx->makeCurrent();
312 } else {
313 m_ctx = currentContext;
314 }
315 }
316
317 operator QGLContext *()
318 {
319 return m_ctx;
320 }
321
322 QGLContext *operator->()
323 {
324 return m_ctx;
325 }
326
~QGLShareContextScope()327 ~QGLShareContextScope()
328 {
329 if (m_oldContext)
330 m_oldContext->makeCurrent();
331 }
332
333 private:
334 QGLContext *m_oldContext;
335 QGLContext *m_ctx;
336 };
337
338 QT_END_NAMESPACE
Q_DECLARE_METATYPE(GLuint)339 Q_DECLARE_METATYPE(GLuint)
340 QT_BEGIN_NAMESPACE
341
342 class Q_OPENGL_EXPORT QGLTextureDestroyer
343 {
344 public:
345 void emitFreeTexture(QGLContext *context, QPlatformPixmap *, GLuint id) {
346 if (context->contextHandle())
347 (new QOpenGLSharedResourceGuard(context->contextHandle(), id, freeTextureFunc))->free();
348 }
349
350 private:
351 static void freeTextureFunc(QOpenGLFunctions *, GLuint id) {
352 QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &id);
353 }
354 };
355
356 class Q_OPENGL_EXPORT QGLSignalProxy : public QObject
357 {
358 Q_OBJECT
359 public:
emitAboutToDestroyContext(const QGLContext * context)360 void emitAboutToDestroyContext(const QGLContext *context) {
361 emit aboutToDestroyContext(context);
362 }
363 static QGLSignalProxy *instance();
364 Q_SIGNALS:
365 void aboutToDestroyContext(const QGLContext *context);
366 };
367
368 class QGLTexture {
369 public:
370 explicit QGLTexture(QGLContext *ctx = nullptr, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D,
371 QGLContext::BindOptions opt = QGLContext::DefaultBindOption)
context(ctx)372 : context(ctx),
373 id(tx_id),
374 target(tx_target),
375 options(opt)
376 {}
377
~QGLTexture()378 ~QGLTexture() {
379 if (options & QGLContext::MemoryManagedBindOption) {
380 Q_ASSERT(context);
381 QPlatformPixmap *boundPixmap = nullptr;
382 context->d_ptr->texture_destroyer->emitFreeTexture(context, boundPixmap, id);
383 }
384 }
385
386 QGLContext *context;
387 GLuint id;
388 GLenum target;
389
390 QGLContext::BindOptions options;
391
392 bool canBindCompressedTexture
393 (const char *buf, int len, const char *format, bool *hasAlpha);
394 QSize bindCompressedTexture
395 (const QString& fileName, const char *format = nullptr);
396 QSize bindCompressedTexture
397 (const char *buf, int len, const char *format = nullptr);
398 QSize bindCompressedTextureDDS(const char *buf, int len);
399 QSize bindCompressedTexturePVR(const char *buf, int len);
400 };
401
402 struct QGLTextureCacheKey {
403 qint64 key;
404 QGLContextGroup *group;
405 };
406
407 inline bool operator==(const QGLTextureCacheKey &a, const QGLTextureCacheKey &b)
408 {
409 return a.key == b.key && a.group == b.group;
410 }
411
qHash(const QGLTextureCacheKey & key)412 inline uint qHash(const QGLTextureCacheKey &key)
413 {
414 return qHash(key.key) ^ qHash(key.group);
415 }
416
417
418 class Q_AUTOTEST_EXPORT QGLTextureCache {
419 public:
420 QGLTextureCache();
421 ~QGLTextureCache();
422
423 void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost);
424 void remove(qint64 key);
425 inline int size();
426 inline void setMaxCost(int newMax);
427 inline int maxCost();
428 inline QGLTexture* getTexture(QGLContext *ctx, qint64 key);
429
430 bool remove(QGLContext *ctx, GLuint textureId);
431 void removeContextTextures(QGLContext *ctx);
432 static QGLTextureCache *instance();
433 static void cleanupTexturesForCacheKey(qint64 cacheKey);
434 static void cleanupTexturesForPixampData(QPlatformPixmap* pixmap);
435 static void cleanupBeforePixmapDestruction(QPlatformPixmap* pixmap);
436
437 private:
438 QCache<QGLTextureCacheKey, QGLTexture> m_cache;
439 QReadWriteLock m_lock;
440 };
441
size()442 int QGLTextureCache::size() {
443 QReadLocker locker(&m_lock);
444 return m_cache.size();
445 }
446
setMaxCost(int newMax)447 void QGLTextureCache::setMaxCost(int newMax)
448 {
449 QWriteLocker locker(&m_lock);
450 m_cache.setMaxCost(newMax);
451 }
452
maxCost()453 int QGLTextureCache::maxCost()
454 {
455 QReadLocker locker(&m_lock);
456 return m_cache.maxCost();
457 }
458
getTexture(QGLContext * ctx,qint64 key)459 QGLTexture* QGLTextureCache::getTexture(QGLContext *ctx, qint64 key)
460 {
461 // Can't be a QReadLocker since QCache::object() modifies the cache (reprioritizes the object)
462 QWriteLocker locker(&m_lock);
463 const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)};
464 return m_cache.object(cacheKey);
465 }
466
467 Q_OPENGL_EXPORT extern QPaintEngine* qt_qgl_paint_engine();
468
469 QOpenGLExtensions* qgl_extensions();
470 bool qgl_hasFeature(QOpenGLFunctions::OpenGLFeature feature);
471 bool qgl_hasExtension(QOpenGLExtensions::OpenGLExtension extension);
472
473 // Put a guard around a GL object identifier and its context.
474 // When the context goes away, a shared context will be used
475 // in its place. If there are no more shared contexts, then
476 // the identifier is returned as zero - it is assumed that the
477 // context destruction cleaned up the identifier in this case.
478 class QGLSharedResourceGuardBase : public QOpenGLSharedResource
479 {
480 public:
QGLSharedResourceGuardBase(QGLContext * context,GLuint id)481 QGLSharedResourceGuardBase(QGLContext *context, GLuint id)
482 : QOpenGLSharedResource(context->contextHandle()->shareGroup())
483 , m_id(id)
484 {
485 }
486
id()487 GLuint id() const
488 {
489 return m_id;
490 }
491
492 protected:
invalidateResource()493 void invalidateResource() override
494 {
495 m_id = 0;
496 }
497
freeResource(QOpenGLContext * context)498 void freeResource(QOpenGLContext *context) override
499 {
500 if (m_id) {
501 freeResource(QGLContext::fromOpenGLContext(context), m_id);
502 }
503 }
504
505 virtual void freeResource(QGLContext *ctx, GLuint id) = 0;
506
507 private:
508 GLuint m_id;
509 };
510
511 template <typename Func>
512 class QGLSharedResourceGuard : public QGLSharedResourceGuardBase
513 {
514 public:
QGLSharedResourceGuard(QGLContext * context,GLuint id,Func func)515 QGLSharedResourceGuard(QGLContext *context, GLuint id, Func func)
516 : QGLSharedResourceGuardBase(context, id)
517 , m_func(func)
518 {
519 }
520
521 protected:
freeResource(QGLContext * ctx,GLuint id)522 void freeResource(QGLContext *ctx, GLuint id) override
523 {
524 m_func(ctx, id);
525 }
526
527 private:
528 Func m_func;
529 };
530
531 template <typename Func>
createSharedResourceGuard(QGLContext * context,GLuint id,Func cleanupFunc)532 QGLSharedResourceGuardBase *createSharedResourceGuard(QGLContext *context, GLuint id, Func cleanupFunc)
533 {
534 return new QGLSharedResourceGuard<Func>(context, id, cleanupFunc);
535 }
536
537 // this is a class that wraps a QThreadStorage object for storing
538 // thread local instances of the GL 1 and GL 2 paint engines
539
540 template <class T>
541 class QGLEngineThreadStorage
542 {
543 public:
engine()544 QPaintEngine *engine() {
545 QPaintEngine *&localEngine = storage.localData();
546 if (!localEngine)
547 localEngine = new T;
548 return localEngine;
549 }
550
551 private:
552 QThreadStorage<QPaintEngine *> storage;
553 };
554 QT_END_NAMESPACE
555
556 #endif // QGL_P_H
557