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
69QT_BEGIN_NAMESPACE
70
71class QGLContext;
72class QGLOverlayWidget;
73class QPixmap;
74class QOpenGLExtensions;
75
76class QGLFormatPrivate
77{
78public:
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 }
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
127class Q_OPENGL_EXPORT QGLWidgetPrivate : public QWidgetPrivate
128{
129 Q_DECLARE_PUBLIC(QGLWidget)
130public:
131 QGLWidgetPrivate() : QWidgetPrivate()
132 , disable_clear_on_painter_begin(false)
133 , parent_changing(false)
134 {
135 }
136
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();
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.
163class QGLContextGroup
164{
165public:
166 ~QGLContextGroup();
167
168 const QGLContext *context() const {return m_context;}
169 bool isSharing() const { return m_shares.size() >= 2; }
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
175private:
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.
189const 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*/
203class QGLTemporaryContextPrivate;
204class QGLTemporaryContext {
205public:
206 explicit QGLTemporaryContext(bool directRendering = true, QWidget *parent = nullptr);
207 ~QGLTemporaryContext();
208
209private:
210 QScopedPointer<QGLTemporaryContextPrivate> d;
211};
212
213class QGLTexture;
214class 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
220class QGLContextResourceBase;
221
222class QGLContextPrivate
223{
224 Q_DECLARE_PUBLIC(QGLContext)
225public:
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
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.
301class Q_OPENGL_EXPORT QGLShareContextScope
302{
303public:
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
327 ~QGLShareContextScope()
328 {
329 if (m_oldContext)
330 m_oldContext->makeCurrent();
331 }
332
333private:
334 QGLContext *m_oldContext;
335 QGLContext *m_ctx;
336};
337
338QT_END_NAMESPACE
339Q_DECLARE_METATYPE(GLuint)
340QT_BEGIN_NAMESPACE
341
342class Q_OPENGL_EXPORT QGLTextureDestroyer
343{
344public:
345 void emitFreeTexture(QGLContext *context, QPlatformPixmap *, GLuint id) {
346 if (context->contextHandle())
347 (new QOpenGLSharedResourceGuard(context->contextHandle(), id, freeTextureFunc))->free();
348 }
349
350private:
351 static void freeTextureFunc(QOpenGLFunctions *, GLuint id) {
352 QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &id);
353 }
354};
355
356class Q_OPENGL_EXPORT QGLSignalProxy : public QObject
357{
358 Q_OBJECT
359public:
360 void emitAboutToDestroyContext(const QGLContext *context) {
361 emit aboutToDestroyContext(context);
362 }
363 static QGLSignalProxy *instance();
364Q_SIGNALS:
365 void aboutToDestroyContext(const QGLContext *context);
366};
367
368class QGLTexture {
369public:
370 explicit QGLTexture(QGLContext *ctx = nullptr, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D,
371 QGLContext::BindOptions opt = QGLContext::DefaultBindOption)
372 : context(ctx),
373 id(tx_id),
374 target(tx_target),
375 options(opt)
376 {}
377
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
402struct QGLTextureCacheKey {
403 qint64 key;
404 QGLContextGroup *group;
405};
406
407inline bool operator==(const QGLTextureCacheKey &a, const QGLTextureCacheKey &b)
408{
409 return a.key == b.key && a.group == b.group;
410}
411
412inline uint qHash(const QGLTextureCacheKey &key)
413{
414 return qHash(key.key) ^ qHash(key.group);
415}
416
417
418class Q_AUTOTEST_EXPORT QGLTextureCache {
419public:
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
437private:
438 QCache<QGLTextureCacheKey, QGLTexture> m_cache;
439 QReadWriteLock m_lock;
440};
441
442int QGLTextureCache::size() {
443 QReadLocker locker(&m_lock);
444 return m_cache.size();
445}
446
447void QGLTextureCache::setMaxCost(int newMax)
448{
449 QWriteLocker locker(&m_lock);
450 m_cache.setMaxCost(newMax);
451}
452
453int QGLTextureCache::maxCost()
454{
455 QReadLocker locker(&m_lock);
456 return m_cache.maxCost();
457}
458
459QGLTexture* 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
467Q_OPENGL_EXPORT extern QPaintEngine* qt_qgl_paint_engine();
468
469QOpenGLExtensions* qgl_extensions();
470bool qgl_hasFeature(QOpenGLFunctions::OpenGLFeature feature);
471bool 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.
478class QGLSharedResourceGuardBase : public QOpenGLSharedResource
479{
480public:
481 QGLSharedResourceGuardBase(QGLContext *context, GLuint id)
482 : QOpenGLSharedResource(context->contextHandle()->shareGroup())
483 , m_id(id)
484 {
485 }
486
487 GLuint id() const
488 {
489 return m_id;
490 }
491
492protected:
493 void invalidateResource() override
494 {
495 m_id = 0;
496 }
497
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
507private:
508 GLuint m_id;
509};
510
511template <typename Func>
512class QGLSharedResourceGuard : public QGLSharedResourceGuardBase
513{
514public:
515 QGLSharedResourceGuard(QGLContext *context, GLuint id, Func func)
516 : QGLSharedResourceGuardBase(context, id)
517 , m_func(func)
518 {
519 }
520
521protected:
522 void freeResource(QGLContext *ctx, GLuint id) override
523 {
524 m_func(ctx, id);
525 }
526
527private:
528 Func m_func;
529};
530
531template <typename Func>
532QGLSharedResourceGuardBase *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
540template <class T>
541class QGLEngineThreadStorage
542{
543public:
544 QPaintEngine *engine() {
545 QPaintEngine *&localEngine = storage.localData();
546 if (!localEngine)
547 localEngine = new T;
548 return localEngine;
549 }
550
551private:
552 QThreadStorage<QPaintEngine *> storage;
553};
554QT_END_NAMESPACE
555
556#endif // QGL_P_H
557