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 &region);
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