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 QtGui 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#include <qpa/qplatformopenglcontext.h>
41#include <qpa/qplatformintegration.h>
42#include "qopenglcontext.h"
43#include "qopenglcontext_p.h"
44#include "qwindow.h"
45
46#include <QtCore/QThreadStorage>
47#include <QtCore/QThread>
48
49#include <QtGui/private/qguiapplication_p.h>
50#include <QtGui/private/qopengl_p.h>
51#include <QtGui/private/qwindow_p.h>
52#include <QtGui/QScreen>
53#include <qpa/qplatformnativeinterface.h>
54
55#include <private/qopenglextensions_p.h>
56#include <private/qopenglversionfunctionsfactory_p.h>
57
58#include <private/qopengltexturehelper_p.h>
59
60#include <QDebug>
61
62#ifndef QT_OPENGL_ES_2
63#include <QOpenGLFunctions_1_0>
64#include <QOpenGLFunctions_3_2_Core>
65#endif
66
67QT_BEGIN_NAMESPACE
68
69class QOpenGLVersionProfilePrivate
70{
71public:
72 QOpenGLVersionProfilePrivate()
73 : majorVersion(0),
74 minorVersion(0),
75 profile(QSurfaceFormat::NoProfile)
76 {}
77
78 int majorVersion;
79 int minorVersion;
80 QSurfaceFormat::OpenGLContextProfile profile;
81};
82
83
84/*!
85 \class QOpenGLVersionProfile
86 \inmodule QtGui
87 \since 5.1
88 \brief The QOpenGLVersionProfile class represents the version and if applicable
89 the profile of an OpenGL context.
90
91 An object of this class can be passed to QOpenGLContext::versionFunctions() to
92 request a functions object for a specific version and profile of OpenGL.
93
94 It also contains some helper functions to check if a version supports profiles
95 or is a legacy version.
96*/
97
98/*!
99 Creates a default invalid QOpenGLVersionProfile object.
100*/
101QOpenGLVersionProfile::QOpenGLVersionProfile()
102 : d(new QOpenGLVersionProfilePrivate)
103{
104}
105
106/*!
107 Creates a QOpenGLVersionProfile object initialised with the version and profile
108 from \a format.
109*/
110QOpenGLVersionProfile::QOpenGLVersionProfile(const QSurfaceFormat &format)
111 : d(new QOpenGLVersionProfilePrivate)
112{
113 d->majorVersion = format.majorVersion();
114 d->minorVersion = format.minorVersion();
115 d->profile = format.profile();
116}
117
118/*!
119 Constructs a copy of \a other.
120*/
121QOpenGLVersionProfile::QOpenGLVersionProfile(const QOpenGLVersionProfile &other)
122 : d(new QOpenGLVersionProfilePrivate)
123{
124 *d = *(other.d);
125}
126
127/*!
128 Destroys the QOpenGLVersionProfile object.
129*/
130QOpenGLVersionProfile::~QOpenGLVersionProfile()
131{
132 delete d;
133}
134
135/*!
136 Assigns the version and profile of \a rhs to this QOpenGLVersionProfile object.
137*/
138QOpenGLVersionProfile &QOpenGLVersionProfile::operator=(const QOpenGLVersionProfile &rhs)
139{
140 if (this == &rhs)
141 return *this;
142 *d = *(rhs.d);
143 return *this;
144}
145
146/*!
147 Returns a QPair<int,int> where the components represent the major and minor OpenGL
148 version numbers respectively.
149
150 \sa setVersion()
151*/
152QPair<int, int> QOpenGLVersionProfile::version() const
153{
154 return qMakePair( d->majorVersion, d->minorVersion);
155}
156
157/*!
158 Sets the major and minor version numbers to \a majorVersion and \a minorVersion respectively.
159
160 \sa version()
161*/
162void QOpenGLVersionProfile::setVersion(int majorVersion, int minorVersion)
163{
164 d->majorVersion = majorVersion;
165 d->minorVersion = minorVersion;
166}
167
168/*!
169 Returns the OpenGL profile. Only makes sense if profiles are supported by this version.
170
171 \sa setProfile()
172*/
173QSurfaceFormat::OpenGLContextProfile QOpenGLVersionProfile::profile() const
174{
175 return d->profile;
176}
177
178/*!
179 Sets the OpenGL profile \a profile. Only makes sense if profiles are supported by
180 this version.
181
182 \sa profile()
183*/
184void QOpenGLVersionProfile::setProfile(QSurfaceFormat::OpenGLContextProfile profile)
185{
186 d->profile = profile;
187}
188
189/*!
190 Returns \c true if profiles are supported by the OpenGL version returned by version(). Only
191 OpenGL versions >= 3.2 support profiles.
192
193 \sa profile(), version()
194*/
195bool QOpenGLVersionProfile::hasProfiles() const
196{
197 return ( d->majorVersion > 3
198 || (d->majorVersion == 3 && d->minorVersion > 1));
199}
200
201/*!
202 Returns \c true is the OpenGL version returned by version() contains deprecated functions
203 and does not support profiles i.e. if the OpenGL version is <= 3.1.
204*/
205bool QOpenGLVersionProfile::isLegacyVersion() const
206{
207 return (d->majorVersion < 3 || (d->majorVersion == 3 && d->minorVersion == 0));
208}
209
210/*!
211 Returns \c true if the version number is valid. Note that for a default constructed
212 QOpenGLVersionProfile object this function will return \c false.
213
214 \sa setVersion(), version()
215*/
216bool QOpenGLVersionProfile::isValid() const
217{
218 return d->majorVersion > 0 && d->minorVersion >= 0;
219}
220
221class QGuiGLThreadContext
222{
223public:
224 QGuiGLThreadContext()
225 : context(0)
226 {
227 }
228 ~QGuiGLThreadContext() {
229 if (context)
230 context->doneCurrent();
231 }
232 QOpenGLContext *context;
233};
234
235Q_GLOBAL_STATIC(QThreadStorage<QGuiGLThreadContext *>, qwindow_context_storage);
236static QOpenGLContext *global_share_context = 0;
237
238#ifndef QT_NO_DEBUG
239QHash<QOpenGLContext *, bool> QOpenGLContextPrivate::makeCurrentTracker;
240QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex;
241#endif
242
243/*!
244 \internal
245
246 This function is used by Qt::AA_ShareOpenGLContexts and the Qt
247 WebEngine to set up context sharing across multiple windows. Do
248 not use it for any other purpose.
249
250 Please maintain the binary compatibility of these functions.
251*/
252void qt_gl_set_global_share_context(QOpenGLContext *context)
253{
254 global_share_context = context;
255}
256
257/*!
258 \internal
259*/
260QOpenGLContext *qt_gl_global_share_context()
261{
262 return global_share_context;
263}
264
265/*!
266 \class QOpenGLContext
267 \inmodule QtGui
268 \since 5.0
269 \brief The QOpenGLContext class represents a native OpenGL context, enabling
270 OpenGL rendering on a QSurface.
271
272 QOpenGLContext represents the OpenGL state of an underlying OpenGL context.
273 To set up a context, set its screen and format such that they match those
274 of the surface or surfaces with which the context is meant to be used, if
275 necessary make it share resources with other contexts with
276 setShareContext(), and finally call create(). Use the return value or isValid()
277 to check if the context was successfully initialized.
278
279 A context can be made current against a given surface by calling
280 makeCurrent(). When OpenGL rendering is done, call swapBuffers() to swap
281 the front and back buffers of the surface, so that the newly rendered
282 content becomes visible. To be able to support certain platforms,
283 QOpenGLContext requires that you call makeCurrent() again before starting
284 rendering a new frame, after calling swapBuffers().
285
286 If the context is temporarily not needed, such as when the application is
287 not rendering, it can be useful to delete it in order to free resources.
288 You can connect to the aboutToBeDestroyed() signal to clean up any
289 resources that have been allocated with different ownership from the
290 QOpenGLContext itself.
291
292 Once a QOpenGLContext has been made current, you can render to it in a
293 platform independent way by using Qt's OpenGL enablers such as
294 QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, and
295 QOpenGLFramebufferObject. It is also possible to use the platform's OpenGL
296 API directly, without using the Qt enablers, although potentially at the
297 cost of portability. The latter is necessary when wanting to use OpenGL 1.x
298 or OpenGL ES 1.x.
299
300 For more information about the OpenGL API, refer to the official
301 \l{http://www.opengl.org}{OpenGL documentation}.
302
303 For an example of how to use QOpenGLContext see the
304 \l{OpenGL Window Example}{OpenGL Window} example.
305
306 \section1 Thread Affinity
307
308 QOpenGLContext can be moved to a different thread with moveToThread(). Do
309 not call makeCurrent() from a different thread than the one to which the
310 QOpenGLContext object belongs. A context can only be current in one thread
311 and against one surface at a time, and a thread only has one context
312 current at a time.
313
314 \section1 Context Resource Sharing
315
316 Resources such as textures and vertex buffer objects
317 can be shared between contexts. Use setShareContext() before calling
318 create() to specify that the contexts should share these resources.
319 QOpenGLContext internally keeps track of a QOpenGLContextGroup object which
320 can be accessed with shareGroup(), and which can be used to find all the
321 contexts in a given share group. A share group consists of all contexts that
322 have been successfully initialized and are sharing with an existing context in
323 the share group. A non-sharing context has a share group consisting of a
324 single context.
325
326 \section1 Default Framebuffer
327
328 On certain platforms, a framebuffer other than 0 might be the default frame
329 buffer depending on the current surface. Instead of calling
330 glBindFramebuffer(0), it is recommended that you use
331 glBindFramebuffer(ctx->defaultFramebufferObject()), to ensure that your
332 application is portable between different platforms. However, if you use
333 QOpenGLFunctions::glBindFramebuffer(), this is done automatically for you.
334
335 \sa QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, QOpenGLFramebufferObject
336*/
337
338/*!
339 \internal
340
341 Set the current context. Returns the previously current context.
342*/
343QOpenGLContext *QOpenGLContextPrivate::setCurrentContext(QOpenGLContext *context)
344{
345 QGuiGLThreadContext *threadContext = qwindow_context_storage()->localData();
346 if (!threadContext) {
347 if (!QThread::currentThread()) {
348 qWarning("No QTLS available. currentContext won't work");
349 return 0;
350 }
351 threadContext = new QGuiGLThreadContext;
352 qwindow_context_storage()->setLocalData(threadContext);
353 }
354 QOpenGLContext *previous = threadContext->context;
355 threadContext->context = context;
356 return previous;
357}
358
359int QOpenGLContextPrivate::maxTextureSize()
360{
361 if (max_texture_size != -1)
362 return max_texture_size;
363
364 Q_Q(QOpenGLContext);
365 QOpenGLFunctions *funcs = q->functions();
366 funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
367
368#ifndef QT_OPENGL_ES
369 if (!q->isOpenGLES()) {
370 GLenum proxy = GL_PROXY_TEXTURE_2D;
371
372 GLint size;
373 GLint next = 64;
374 funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
375
376 QOpenGLFunctions_1_0 *gl1funcs = 0;
377 QOpenGLFunctions_3_2_Core *gl3funcs = 0;
378
379 if (q->format().profile() == QSurfaceFormat::CoreProfile)
380 gl3funcs = q->versionFunctions<QOpenGLFunctions_3_2_Core>();
381 else
382 gl1funcs = q->versionFunctions<QOpenGLFunctions_1_0>();
383
384 Q_ASSERT(gl1funcs || gl3funcs);
385
386 if (gl1funcs)
387 gl1funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size);
388 else
389 gl3funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size);
390
391 if (size == 0) {
392 return max_texture_size;
393 }
394 do {
395 size = next;
396 next = size * 2;
397
398 if (next > max_texture_size)
399 break;
400 funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
401 if (gl1funcs)
402 gl1funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next);
403 else
404 gl3funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next);
405
406 } while (next > size);
407
408 max_texture_size = size;
409 }
410#endif // QT_OPENGL_ES
411
412 return max_texture_size;
413}
414
415/*!
416 Returns the last context which called makeCurrent in the current thread,
417 or \nullptr, if no context is current.
418*/
419QOpenGLContext* QOpenGLContext::currentContext()
420{
421 QGuiGLThreadContext *threadContext = qwindow_context_storage()->localData();
422 if (threadContext)
423 return threadContext->context;
424 return nullptr;
425}
426
427/*!
428 Returns \c true if the \a first and \a second contexts are sharing OpenGL resources.
429*/
430bool QOpenGLContext::areSharing(QOpenGLContext *first, QOpenGLContext *second)
431{
432 return first->shareGroup() == second->shareGroup();
433}
434
435/*!
436 Returns the underlying platform context.
437
438 \internal
439*/
440QPlatformOpenGLContext *QOpenGLContext::handle() const
441{
442 Q_D(const QOpenGLContext);
443 return d->platformGLContext;
444}
445
446/*!
447 Returns the underlying platform context with which this context is sharing.
448
449 \internal
450*/
451
452QPlatformOpenGLContext *QOpenGLContext::shareHandle() const
453{
454 Q_D(const QOpenGLContext);
455 if (d->shareContext)
456 return d->shareContext->handle();
457 return 0;
458}
459
460/*!
461 Creates a new OpenGL context instance with parent object \a parent.
462
463 Before it can be used you need to set the proper format and call create().
464
465 \sa create(), makeCurrent()
466*/
467QOpenGLContext::QOpenGLContext(QObject *parent)
468 : QObject(*new QOpenGLContextPrivate(), parent)
469{
470 setScreen(QGuiApplication::primaryScreen());
471}
472
473/*!
474 Sets the \a format the OpenGL context should be compatible with. You need
475 to call create() before it takes effect.
476
477 When the format is not explicitly set via this function, the format returned
478 by QSurfaceFormat::defaultFormat() will be used. This means that when having
479 multiple contexts, individual calls to this function can be replaced by one
480 single call to QSurfaceFormat::setDefaultFormat() before creating the first
481 context.
482*/
483void QOpenGLContext::setFormat(const QSurfaceFormat &format)
484{
485 Q_D(QOpenGLContext);
486 d->requestedFormat = format;
487}
488
489/*!
490 Makes this context share textures, shaders, and other OpenGL resources
491 with \a shareContext. You need to call create() before it takes effect.
492*/
493void QOpenGLContext::setShareContext(QOpenGLContext *shareContext)
494{
495 Q_D(QOpenGLContext);
496 d->shareContext = shareContext;
497}
498
499/*!
500 Sets the \a screen the OpenGL context should be valid for. You need to call
501 create() before it takes effect.
502*/
503void QOpenGLContext::setScreen(QScreen *screen)
504{
505 Q_D(QOpenGLContext);
506 if (d->screen)
507 disconnect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(_q_screenDestroyed(QObject*)));
508 d->screen = screen;
509 if (!d->screen)
510 d->screen = QGuiApplication::primaryScreen();
511 if (d->screen)
512 connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(_q_screenDestroyed(QObject*)));
513}
514
515void QOpenGLContextPrivate::_q_screenDestroyed(QObject *object)
516{
517 Q_Q(QOpenGLContext);
518 if (object == static_cast<QObject *>(screen)) {
519 screen = 0;
520 q->setScreen(0);
521 }
522}
523
524/*!
525 Set the native handles for this context. When create() is called and a
526 native handle is set, configuration settings, like format(), are ignored
527 since this QOpenGLContext will wrap an already created native context
528 instead of creating a new one from scratch.
529
530 On some platforms the native context handle is not sufficient and other
531 related handles (for example, for a window or display) have to be provided
532 in addition. Therefore \a handle is variant containing a platform-specific
533 value type. These classes can be found in the QtPlatformHeaders module.
534
535 When create() is called with native handles set, QOpenGLContext does not
536 take ownership of the handles, so destroying the QOpenGLContext does not
537 destroy the native context.
538
539 \note Some frameworks track the current context and surfaces internally.
540 Making the adopted QOpenGLContext current via Qt will have no effect on such
541 other frameworks' internal state. Therefore a subsequent makeCurrent done
542 via the other framework may have no effect. It is therefore advisable to
543 make explicit calls to make no context and surface current to reset the
544 other frameworks' internal state after performing OpenGL operations via Qt.
545
546 \note Using foreign contexts with Qt windows and Qt contexts with windows
547 and surfaces created by other frameworks may give unexpected results,
548 depending on the platform, due to potential mismatches in context and window
549 pixel formats. To make sure this does not happen, avoid making contexts and
550 surfaces from different frameworks current together. Instead, prefer
551 approaches based on context sharing where OpenGL resources like textures are
552 accessible both from Qt's and the foreign framework's contexts.
553
554 \since 5.4
555 \sa nativeHandle()
556*/
557void QOpenGLContext::setNativeHandle(const QVariant &handle)
558{
559 Q_D(QOpenGLContext);
560 d->nativeHandle = handle;
561}
562
563/*!
564 Returns the native handle for the context.
565
566 This function provides access to the QOpenGLContext's underlying native
567 context. The returned variant contains a platform-specific value type. These
568 classes can be found in the module QtPlatformHeaders.
569
570 On platforms where retrieving the native handle is not supported, or if
571 neither create() nor setNativeHandle() was called, a null variant is
572 returned.
573
574 \since 5.4
575 \sa setNativeHandle()
576 */
577QVariant QOpenGLContext::nativeHandle() const
578{
579 Q_D(const QOpenGLContext);
580 return d->nativeHandle;
581}
582
583/*!
584 Attempts to create the OpenGL context with the current configuration.
585
586 The current configuration includes the format, the share context, and the
587 screen.
588
589 If the OpenGL implementation on your system does not support the requested
590 version of OpenGL context, then QOpenGLContext will try to create the closest
591 matching version. The actual created context properties can be queried
592 using the QSurfaceFormat returned by the format() function. For example, if
593 you request a context that supports OpenGL 4.3 Core profile but the driver
594 and/or hardware only supports version 3.2 Core profile contexts then you will
595 get a 3.2 Core profile context.
596
597 Returns \c true if the native context was successfully created and is ready to
598 be used with makeCurrent(), swapBuffers(), etc.
599
600 \note If the context already exists, this function destroys the existing
601 context first, and then creates a new one.
602
603 \sa makeCurrent(), format()
604*/
605bool QOpenGLContext::create()
606{
607 Q_D(QOpenGLContext);
608 if (d->platformGLContext)
609 destroy();
610
611 d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformOpenGLContext(this);
612 if (!d->platformGLContext)
613 return false;
614 d->platformGLContext->setContext(this);
615 d->platformGLContext->initialize();
616 if (!d->platformGLContext->isSharing())
617 d->shareContext = 0;
618 d->shareGroup = d->shareContext ? d->shareContext->shareGroup() : new QOpenGLContextGroup;
619 d->shareGroup->d_func()->addContext(this);
620 return isValid();
621}
622
623/*!
624 \internal
625
626 Destroy the underlying platform context associated with this context.
627
628 If any other context is directly or indirectly sharing resources with this
629 context, the shared resources, which includes vertex buffer objects, shader
630 objects, textures, and framebuffer objects, are not freed. However,
631 destroying the underlying platform context frees any state associated with
632 the context.
633
634 After \c destroy() has been called, you must call create() if you wish to
635 use the context again.
636
637 \note This implicitly calls doneCurrent() if the context is current.
638
639 \sa create()
640*/
641void QOpenGLContext::destroy()
642{
643 deleteQGLContext();
644 Q_D(QOpenGLContext);
645 if (d->platformGLContext)
646 emit aboutToBeDestroyed();
647 if (QOpenGLContext::currentContext() == this)
648 doneCurrent();
649 if (d->shareGroup)
650 d->shareGroup->d_func()->removeContext(this);
651 d->shareGroup = 0;
652 delete d->platformGLContext;
653 d->platformGLContext = 0;
654 delete d->functions;
655 d->functions = 0;
656
657 for (QAbstractOpenGLFunctions *func : qAsConst(d->externalVersionFunctions)) {
658 QAbstractOpenGLFunctionsPrivate *func_d = QAbstractOpenGLFunctionsPrivate::get(func);
659 func_d->owningContext = 0;
660 func_d->initialized = false;
661 }
662 d->externalVersionFunctions.clear();
663 qDeleteAll(d->versionFunctions);
664 d->versionFunctions.clear();
665
666 delete d->textureFunctions;
667 d->textureFunctions = 0;
668
669 d->nativeHandle = QVariant();
670}
671
672/*!
673 \fn void QOpenGLContext::aboutToBeDestroyed()
674
675 This signal is emitted before the underlying native OpenGL context is
676 destroyed, such that users may clean up OpenGL resources that might
677 otherwise be left dangling in the case of shared OpenGL contexts.
678
679 If you wish to make the context current in order to do clean-up, make sure
680 to only connect to the signal using a direct connection.
681*/
682
683/*!
684 Destroys the QOpenGLContext object.
685
686 If this is the current context for the thread, doneCurrent() is also called.
687*/
688QOpenGLContext::~QOpenGLContext()
689{
690 destroy();
691
692#ifndef QT_NO_DEBUG
693 QOpenGLContextPrivate::cleanMakeCurrentTracker(this);
694#endif
695}
696
697/*!
698 Returns if this context is valid, i.e. has been successfully created.
699
700 On some platforms the return value of \c false for a context that was
701 successfully created previously indicates that the OpenGL context was lost.
702
703 The typical way to handle context loss scenarios in applications is to
704 check via this function whenever makeCurrent() fails and returns \c false.
705 If this function then returns \c false, recreate the underlying native
706 OpenGL context by calling create(), call makeCurrent() again and then
707 reinitialize all OpenGL resources.
708
709 \sa create()
710*/
711bool QOpenGLContext::isValid() const
712{
713 Q_D(const QOpenGLContext);
714 return d->platformGLContext && d->platformGLContext->isValid();
715}
716
717/*!
718 Get the QOpenGLFunctions instance for this context.
719
720 QOpenGLContext offers this as a convenient way to access QOpenGLFunctions
721 without having to manage it manually.
722
723 The context or a sharing context must be current.
724
725 The returned QOpenGLFunctions instance is ready to be used and it
726 does not need initializeOpenGLFunctions() to be called.
727*/
728QOpenGLFunctions *QOpenGLContext::functions() const
729{
730 Q_D(const QOpenGLContext);
731 if (!d->functions)
732 const_cast<QOpenGLFunctions *&>(d->functions) = new QOpenGLExtensions(QOpenGLContext::currentContext());
733 return d->functions;
734}
735
736/*!
737 Get the QOpenGLExtraFunctions instance for this context.
738
739 QOpenGLContext offers this as a convenient way to access QOpenGLExtraFunctions
740 without having to manage it manually.
741
742 The context or a sharing context must be current.
743
744 The returned QOpenGLExtraFunctions instance is ready to be used and it
745 does not need initializeOpenGLFunctions() to be called.
746
747 \note QOpenGLExtraFunctions contains functionality that is not guaranteed to
748 be available at runtime. Runtime availability depends on the platform,
749 graphics driver, and the OpenGL version requested by the application.
750
751 \sa QOpenGLFunctions, QOpenGLExtraFunctions
752*/
753QOpenGLExtraFunctions *QOpenGLContext::extraFunctions() const
754{
755 return static_cast<QOpenGLExtraFunctions *>(functions());
756}
757
758/*!
759 \fn T *QOpenGLContext::versionFunctions() const
760
761 \overload versionFunctions()
762
763 Returns a pointer to an object that provides access to all functions for
764 the version and profile of this context. There is no need to call
765 QAbstractOpenGLFunctions::initializeOpenGLFunctions() as long as this context
766 is current. It is also possible to call this function when the context is not
767 current, but in that case it is the caller's responsibility to ensure proper
768 initialization by calling QAbstractOpenGLFunctions::initializeOpenGLFunctions()
769 afterwards.
770
771 Usually one would use the template version of this function to automatically
772 have the result cast to the correct type.
773
774 \code
775 QOpenGLFunctions_3_3_Core* funcs = 0;
776 funcs = context->versionFunctions<QOpenGLFunctions_3_3_Core>();
777 if (!funcs) {
778 qWarning() << "Could not obtain required OpenGL context version";
779 exit(1);
780 }
781 \endcode
782
783 It is possible to request a functions object for a different version and profile
784 than that for which the context was created. To do this either use the template
785 version of this function specifying the desired functions object type as the
786 template parameter or by passing in a QOpenGLVersionProfile object as an argument
787 to the non-template function.
788
789 Note that requests for function objects of other versions or profiles can fail and
790 in doing so will return \nullptr. Situations in which creation of the functions
791 object can fail are if the request cannot be satisfied due to asking for functions
792 that are not in the version or profile of this context. For example:
793
794 \list
795 \li Requesting a 3.3 core profile functions object would succeed.
796 \li Requesting a 3.3 compatibility profile functions object would fail. We would fail
797 to resolve the deprecated functions.
798 \li Requesting a 4.3 core profile functions object would fail. We would fail to resolve
799 the new core functions introduced in versions 4.0-4.3.
800 \li Requesting a 3.1 functions object would succeed. There is nothing in 3.1 that is not
801 also in 3.3 core.
802 \endlist
803
804 Note that if creating a functions object via this method that the QOpenGLContext
805 retains ownership of the object. This is to allow the object to be cached and shared.
806*/
807
808/*!
809 Returns a pointer to an object that provides access to all functions for the
810 \a versionProfile of this context. There is no need to call
811 QAbstractOpenGLFunctions::initializeOpenGLFunctions() as long as this context
812 is current. It is also possible to call this function when the context is not
813 current, but in that case it is the caller's responsibility to ensure proper
814 initialization by calling QAbstractOpenGLFunctions::initializeOpenGLFunctions()
815 afterwards.
816
817 Usually one would use the template version of this function to automatically
818 have the result cast to the correct type.
819*/
820QAbstractOpenGLFunctions *QOpenGLContext::versionFunctions(const QOpenGLVersionProfile &versionProfile) const
821{
822#ifndef QT_OPENGL_ES_2
823 if (isOpenGLES()) {
824 qWarning("versionFunctions: Not supported on OpenGL ES");
825 return 0;
826 }
827#endif // QT_OPENGL_ES_2
828
829 Q_D(const QOpenGLContext);
830 const QSurfaceFormat f = format();
831
832 // Ensure we have a valid version and profile. Default to context's if none specified
833 QOpenGLVersionProfile vp = versionProfile;
834 if (!vp.isValid())
835 vp = QOpenGLVersionProfile(f);
836
837 // Check that context is compatible with requested version
838 const QPair<int, int> v = qMakePair(f.majorVersion(), f.minorVersion());
839 if (v < vp.version())
840 return 0;
841
842 // If this context only offers core profile functions then we can't create
843 // function objects for legacy or compatibility profile requests
844 if (((vp.hasProfiles() && vp.profile() != QSurfaceFormat::CoreProfile) || vp.isLegacyVersion())
845 && f.profile() == QSurfaceFormat::CoreProfile)
846 return 0;
847
848 // Create object if suitable one not cached
849 QAbstractOpenGLFunctions* funcs = 0;
850 auto it = d->versionFunctions.constFind(vp);
851 if (it == d->versionFunctions.constEnd()) {
852 funcs = QOpenGLVersionFunctionsFactory::create(vp);
853 if (funcs) {
854 funcs->setOwningContext(this);
855 d->versionFunctions.insert(vp, funcs);
856 }
857 } else {
858 funcs = it.value();
859 }
860
861 if (funcs && QOpenGLContext::currentContext() == this)
862 funcs->initializeOpenGLFunctions();
863
864 return funcs;
865}
866
867/*!
868 Returns the set of OpenGL extensions supported by this context.
869
870 The context or a sharing context must be current.
871
872 \sa hasExtension()
873*/
874QSet<QByteArray> QOpenGLContext::extensions() const
875{
876 Q_D(const QOpenGLContext);
877 if (d->extensionNames.isEmpty()) {
878 QOpenGLExtensionMatcher matcher;
879 d->extensionNames = matcher.extensions();
880 }
881
882 return d->extensionNames;
883}
884
885/*!
886 Returns \c true if this OpenGL context supports the specified OpenGL
887 \a extension, \c false otherwise.
888
889 The context or a sharing context must be current.
890
891 \sa extensions()
892*/
893bool QOpenGLContext::hasExtension(const QByteArray &extension) const
894{
895 return extensions().contains(extension);
896}
897
898/*!
899 Call this to get the default framebuffer object for the current surface.
900
901 On some platforms (for instance, iOS) the default framebuffer object depends
902 on the surface being rendered to, and might be different from 0. Thus,
903 instead of calling glBindFramebuffer(0), you should call
904 glBindFramebuffer(ctx->defaultFramebufferObject()) if you want your
905 application to work across different Qt platforms.
906
907 If you use the glBindFramebuffer() in QOpenGLFunctions you do not have to
908 worry about this, as it automatically binds the current context's
909 defaultFramebufferObject() when 0 is passed.
910
911 \note Widgets that render via framebuffer objects, like QOpenGLWidget and
912 QQuickWidget, will override the value returned from this function when
913 painting is active, because at that time the correct "default" framebuffer
914 is the widget's associated backing framebuffer, not the platform-specific
915 one belonging to the top-level window's surface. This ensures the expected
916 behavior for this function and other classes relying on it (for example,
917 QOpenGLFramebufferObject::bindDefault() or
918 QOpenGLFramebufferObject::release()).
919
920 \sa QOpenGLFramebufferObject
921*/
922GLuint QOpenGLContext::defaultFramebufferObject() const
923{
924 if (!isValid())
925 return 0;
926
927 Q_D(const QOpenGLContext);
928 if (!d->surface || !d->surface->surfaceHandle())
929 return 0;
930
931 if (d->defaultFboRedirect)
932 return d->defaultFboRedirect;
933
934 return d->platformGLContext->defaultFramebufferObject(d->surface->surfaceHandle());
935}
936
937/*!
938 Makes the context current in the current thread, against the given
939 \a surface. Returns \c true if successful; otherwise returns \c false.
940 The latter may happen if the surface is not exposed, or the graphics
941 hardware is not available due to e.g. the application being suspended.
942
943 If \a surface is 0 this is equivalent to calling doneCurrent().
944
945 Avoid calling this function from a different thread than the one the
946 QOpenGLContext instance lives in. If you wish to use QOpenGLContext from a
947 different thread you should first make sure it's not current in the
948 current thread, by calling doneCurrent() if necessary. Then call
949 moveToThread(otherThread) before using it in the other thread.
950
951 By default Qt employs a check that enforces the above condition on the
952 thread affinity. It is still possible to disable this check by setting the
953 \c{Qt::AA_DontCheckOpenGLContextThreadAffinity} application attribute. Be
954 sure to understand the consequences of using QObjects from outside
955 the thread they live in, as explained in the
956 \l{QObject#Thread Affinity}{QObject thread affinity} documentation.
957
958 \sa functions(), doneCurrent(), Qt::AA_DontCheckOpenGLContextThreadAffinity
959*/
960bool QOpenGLContext::makeCurrent(QSurface *surface)
961{
962 Q_D(QOpenGLContext);
963 if (!isValid())
964 return false;
965
966 if (Q_UNLIKELY(!qApp->testAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity)
967 && thread() != QThread::currentThread())) {
968 qFatal("Cannot make QOpenGLContext current in a different thread");
969 }
970
971 if (!surface) {
972 doneCurrent();
973 return true;
974 }
975
976 if (!surface->surfaceHandle())
977 return false;
978 if (!surface->supportsOpenGL()) {
979#ifndef Q_OS_WASM // ### work around the WASM platform plugin using QOpenGLContext with raster surfaces.
980 // see QTBUG-70076
981 qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface" << surface;
982 return false;
983#endif
984 }
985
986 if (!d->platformGLContext->makeCurrent(surface->surfaceHandle()))
987 return false;
988
989 QOpenGLContextPrivate::setCurrentContext(this);
990#ifndef QT_NO_DEBUG
991 QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true);
992#endif
993
994 d->surface = surface;
995
996 static bool needsWorkaroundSet = false;
997 static bool needsWorkaround = false;
998
999 if (!needsWorkaroundSet) {
1000 QByteArray env;
1001#ifdef Q_OS_ANDROID
1002 env = qgetenv(QByteArrayLiteral("QT_ANDROID_DISABLE_GLYPH_CACHE_WORKAROUND"));
1003 needsWorkaround = env.isEmpty() || env == QByteArrayLiteral("0") || env == QByteArrayLiteral("false");
1004#endif
1005 env = qgetenv(QByteArrayLiteral("QT_ENABLE_GLYPH_CACHE_WORKAROUND"));
1006 if (env == QByteArrayLiteral("1") || env == QByteArrayLiteral("true"))
1007 needsWorkaround = true;
1008
1009 if (!needsWorkaround) {
1010 const char *rendererString = reinterpret_cast<const char *>(functions()->glGetString(GL_RENDERER));
1011 if (rendererString)
1012 needsWorkaround =
1013 qstrncmp(rendererString, "Mali-4xx", 6) == 0 // Mali-400, Mali-450
1014 || qstrcmp(rendererString, "Mali-T880") == 0
1015 || qstrncmp(rendererString, "Adreno (TM) 2xx", 13) == 0 // Adreno 200, 203, 205
1016 || qstrncmp(rendererString, "Adreno 2xx", 8) == 0 // Same as above but without the '(TM)'
1017 || qstrncmp(rendererString, "Adreno (TM) 3xx", 13) == 0 // Adreno 302, 305, 320, 330
1018 || qstrncmp(rendererString, "Adreno 3xx", 8) == 0 // Same as above but without the '(TM)'
1019 || qstrncmp(rendererString, "Adreno (TM) 4xx", 13) == 0 // Adreno 405, 418, 420, 430
1020 || qstrncmp(rendererString, "Adreno 4xx", 8) == 0 // Same as above but without the '(TM)'
1021 || qstrncmp(rendererString, "Adreno (TM) 5xx", 13) == 0 // Adreno 505, 506, 510, 530, 540
1022 || qstrncmp(rendererString, "Adreno 5xx", 8) == 0 // Same as above but without the '(TM)'
1023 || qstrncmp(rendererString, "Adreno (TM) 6xx", 13) == 0 // Adreno 610, 620, 630
1024 || qstrncmp(rendererString, "Adreno 6xx", 8) == 0 // Same as above but without the '(TM)'
1025 || qstrcmp(rendererString, "GC800 core") == 0
1026 || qstrcmp(rendererString, "GC1000 core") == 0
1027 || strstr(rendererString, "GC2000") != 0
1028 || qstrcmp(rendererString, "Immersion.16") == 0;
1029 }
1030 needsWorkaroundSet = true;
1031 }
1032
1033 if (needsWorkaround)
1034 d->workaround_brokenFBOReadBack = true;
1035
1036 d->shareGroup->d_func()->deletePendingResources(this);
1037
1038 return true;
1039}
1040
1041/*!
1042 Convenience function for calling makeCurrent with a 0 surface.
1043
1044 This results in no context being current in the current thread.
1045
1046 \sa makeCurrent(), currentContext()
1047*/
1048void QOpenGLContext::doneCurrent()
1049{
1050 Q_D(QOpenGLContext);
1051 if (!isValid())
1052 return;
1053
1054 if (QOpenGLContext::currentContext() == this)
1055 d->shareGroup->d_func()->deletePendingResources(this);
1056
1057 d->platformGLContext->doneCurrent();
1058 QOpenGLContextPrivate::setCurrentContext(0);
1059
1060 d->surface = 0;
1061}
1062
1063/*!
1064 Returns the surface the context has been made current with.
1065
1066 This is the surface passed as an argument to makeCurrent().
1067*/
1068QSurface *QOpenGLContext::surface() const
1069{
1070 Q_D(const QOpenGLContext);
1071 return d->surface;
1072}
1073
1074
1075/*!
1076 Swap the back and front buffers of \a surface.
1077
1078 Call this to finish a frame of OpenGL rendering, and make sure to
1079 call makeCurrent() again before issuing any further OpenGL commands,
1080 for example as part of a new frame.
1081*/
1082void QOpenGLContext::swapBuffers(QSurface *surface)
1083{
1084 Q_D(QOpenGLContext);
1085 if (!isValid())
1086 return;
1087
1088 if (!surface) {
1089 qWarning("QOpenGLContext::swapBuffers() called with null argument");
1090 return;
1091 }
1092
1093 if (!surface->supportsOpenGL()) {
1094 qWarning("QOpenGLContext::swapBuffers() called with non-opengl surface");
1095 return;
1096 }
1097
1098 if (surface->surfaceClass() == QSurface::Window
1099 && !qt_window_private(static_cast<QWindow *>(surface))->receivedExpose)
1100 {
1101 qWarning("QOpenGLContext::swapBuffers() called with non-exposed window, behavior is undefined");
1102 }
1103
1104 QPlatformSurface *surfaceHandle = surface->surfaceHandle();
1105 if (!surfaceHandle)
1106 return;
1107
1108#if !defined(QT_NO_DEBUG)
1109 if (!QOpenGLContextPrivate::toggleMakeCurrentTracker(this, false))
1110 qWarning("QOpenGLContext::swapBuffers() called without corresponding makeCurrent()");
1111#endif
1112 if (surface->format().swapBehavior() == QSurfaceFormat::SingleBuffer)
1113 functions()->glFlush();
1114 d->platformGLContext->swapBuffers(surfaceHandle);
1115}
1116
1117/*!
1118 Resolves the function pointer to an OpenGL extension function, identified by \a procName
1119
1120 Returns \nullptr if no such function can be found.
1121*/
1122QFunctionPointer QOpenGLContext::getProcAddress(const QByteArray &procName) const
1123{
1124 return getProcAddress(procName.constData());
1125}
1126
1127/*!
1128 \overload
1129 \since 5.8
1130 */
1131QFunctionPointer QOpenGLContext::getProcAddress(const char *procName) const
1132{
1133 Q_D(const QOpenGLContext);
1134 if (!d->platformGLContext)
1135 return nullptr;
1136 return d->platformGLContext->getProcAddress(procName);
1137}
1138
1139/*!
1140 Returns the format of the underlying platform context, if create() has been called.
1141
1142 Otherwise, returns the requested format.
1143
1144 The requested and the actual format may differ. Requesting a given OpenGL version does
1145 not mean the resulting context will target exactly the requested version. It is only
1146 guaranteed that the version/profile/options combination for the created context is
1147 compatible with the request, as long as the driver is able to provide such a context.
1148
1149 For example, requesting an OpenGL version 3.x core profile context may result in an
1150 OpenGL 4.x core profile context. Similarly, a request for OpenGL 2.1 may result in an
1151 OpenGL 3.0 context with deprecated functions enabled. Finally, depending on the
1152 driver, unsupported versions may result in either a context creation failure or in a
1153 context for the highest supported version.
1154
1155 Similar differences are possible in the buffer sizes, for example, the resulting
1156 context may have a larger depth buffer than requested. This is perfectly normal.
1157*/
1158QSurfaceFormat QOpenGLContext::format() const
1159{
1160 Q_D(const QOpenGLContext);
1161 if (!d->platformGLContext)
1162 return d->requestedFormat;
1163 return d->platformGLContext->format();
1164}
1165
1166/*!
1167 Returns the share group this context belongs to.
1168*/
1169QOpenGLContextGroup *QOpenGLContext::shareGroup() const
1170{
1171 Q_D(const QOpenGLContext);
1172 return d->shareGroup;
1173}
1174
1175/*!
1176 Returns the share context this context was created with.
1177
1178 If the underlying platform was not able to support the requested
1179 sharing, this will return 0.
1180*/
1181QOpenGLContext *QOpenGLContext::shareContext() const
1182{
1183 Q_D(const QOpenGLContext);
1184 return d->shareContext;
1185}
1186
1187/*!
1188 Returns the screen the context was created for.
1189*/
1190QScreen *QOpenGLContext::screen() const
1191{
1192 Q_D(const QOpenGLContext);
1193 return d->screen;
1194}
1195
1196/*!
1197 internal: Needs to have a pointer to qGLContext. But since this is in Qt GUI we can't
1198 have any type information.
1199
1200 \internal
1201*/
1202void *QOpenGLContext::qGLContextHandle() const
1203{
1204 Q_D(const QOpenGLContext);
1205 return d->qGLContextHandle;
1206}
1207
1208/*!
1209 internal: If the delete function is specified QOpenGLContext "owns"
1210 the passed context handle and will use the delete function to destroy it.
1211
1212 \internal
1213*/
1214void QOpenGLContext::setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *))
1215{
1216 Q_D(QOpenGLContext);
1217 d->qGLContextHandle = handle;
1218 d->qGLContextDeleteFunction = qGLContextDeleteFunction;
1219}
1220
1221/*!
1222 \internal
1223*/
1224void QOpenGLContext::deleteQGLContext()
1225{
1226 Q_D(QOpenGLContext);
1227 if (d->qGLContextDeleteFunction && d->qGLContextHandle) {
1228 d->qGLContextDeleteFunction(d->qGLContextHandle);
1229 d->qGLContextDeleteFunction = 0;
1230 d->qGLContextHandle = 0;
1231 }
1232}
1233
1234/*!
1235 Returns the platform-specific handle for the OpenGL implementation that
1236 is currently in use. (for example, a HMODULE on Windows)
1237
1238 On platforms that do not use dynamic GL switching, the return value
1239 is \nullptr.
1240
1241 The library might be GL-only, meaning that windowing system interface
1242 functions (for example EGL) may live in another, separate library.
1243
1244 \note This function requires that the QGuiApplication instance is already created.
1245
1246 \sa openGLModuleType()
1247
1248 \since 5.3
1249 */
1250void *QOpenGLContext::openGLModuleHandle()
1251{
1252#ifdef QT_OPENGL_DYNAMIC
1253 QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
1254 Q_ASSERT(ni);
1255 return ni->nativeResourceForIntegration(QByteArrayLiteral("glhandle"));
1256#else
1257 return 0;
1258#endif
1259}
1260
1261/*!
1262 \enum QOpenGLContext::OpenGLModuleType
1263 This enum defines the type of the underlying OpenGL implementation.
1264
1265 \value LibGL OpenGL
1266 \value LibGLES OpenGL ES 2.0 or higher
1267
1268 \since 5.3
1269*/
1270
1271/*!
1272 Returns the underlying OpenGL implementation type.
1273
1274 On platforms where the OpenGL implementation is not dynamically
1275 loaded, the return value is determined during compile time and never
1276 changes.
1277
1278 \note A desktop OpenGL implementation may be capable of creating
1279 ES-compatible contexts too. Therefore in most cases it is more
1280 appropriate to check QSurfaceFormat::renderableType() or use
1281 the convenience function isOpenGLES().
1282
1283 \note This function requires that the QGuiApplication instance is already created.
1284
1285 \since 5.3
1286 */
1287QOpenGLContext::OpenGLModuleType QOpenGLContext::openGLModuleType()
1288{
1289#if defined(QT_OPENGL_DYNAMIC)
1290 Q_ASSERT(qGuiApp);
1291 return QGuiApplicationPrivate::instance()->platformIntegration()->openGLModuleType();
1292#elif defined(QT_OPENGL_ES_2)
1293 return LibGLES;
1294#else
1295 return LibGL;
1296#endif
1297}
1298
1299/*!
1300 Returns true if the context is an OpenGL ES context.
1301
1302 If the context has not yet been created, the result is based on the
1303 requested format set via setFormat().
1304
1305 \sa create(), format(), setFormat()
1306
1307 \since 5.3
1308 */
1309bool QOpenGLContext::isOpenGLES() const
1310{
1311 return format().renderableType() == QSurfaceFormat::OpenGLES;
1312}
1313
1314/*!
1315 Returns \c true if the platform supports OpenGL rendering outside the main (gui)
1316 thread.
1317
1318 The value is controlled by the platform plugin in use and may also depend on the
1319 graphics drivers.
1320
1321 \since 5.5
1322 */
1323bool QOpenGLContext::supportsThreadedOpenGL()
1324{
1325 Q_ASSERT(qGuiApp);
1326 return QGuiApplicationPrivate::instance()->platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
1327}
1328
1329/*!
1330 \since 5.5
1331
1332 Returns the application-wide shared OpenGL context, if present.
1333 Otherwise, returns \nullptr.
1334
1335 This is useful if you need to upload OpenGL objects (buffers, textures,
1336 etc.) before creating or showing a QOpenGLWidget or QQuickWidget.
1337
1338 \note You must set the Qt::AA_ShareOpenGLContexts flag on QGuiApplication
1339 before creating the QGuiApplication object, otherwise Qt may not create a
1340 global shared context.
1341
1342 \warning Do not attempt to make the context returned by this function
1343 current on any surface. Instead, you can create a new context which shares
1344 with the global one, and then make the new context current.
1345
1346 \sa Qt::AA_ShareOpenGLContexts, setShareContext(), makeCurrent()
1347*/
1348QOpenGLContext *QOpenGLContext::globalShareContext()
1349{
1350 Q_ASSERT(qGuiApp);
1351 return qt_gl_global_share_context();
1352}
1353
1354/*!
1355 \internal
1356*/
1357QOpenGLVersionFunctionsStorage *QOpenGLContext::functionsBackendStorage() const
1358{
1359 Q_D(const QOpenGLContext);
1360 return &d->versionFunctionsStorage;
1361}
1362
1363/*!
1364 \internal
1365 */
1366void QOpenGLContext::insertExternalFunctions(QAbstractOpenGLFunctions *f)
1367{
1368 Q_D(QOpenGLContext);
1369 d->externalVersionFunctions.insert(f);
1370}
1371
1372/*!
1373 \internal
1374 */
1375void QOpenGLContext::removeExternalFunctions(QAbstractOpenGLFunctions *f)
1376{
1377 Q_D(QOpenGLContext);
1378 d->externalVersionFunctions.remove(f);
1379}
1380
1381/*!
1382 \internal
1383*/
1384QOpenGLTextureHelper* QOpenGLContext::textureFunctions() const
1385{
1386 Q_D(const QOpenGLContext);
1387 return d->textureFunctions;
1388}
1389
1390/*!
1391 \internal
1392*/
1393void QOpenGLContext::setTextureFunctions(QOpenGLTextureHelper* textureFuncs)
1394{
1395 Q_D(QOpenGLContext);
1396 d->textureFunctions = textureFuncs;
1397}
1398
1399/*!
1400 \class QOpenGLContextGroup
1401 \since 5.0
1402 \brief The QOpenGLContextGroup class represents a group of contexts sharing
1403 OpenGL resources.
1404 \inmodule QtGui
1405
1406 QOpenGLContextGroup is automatically created and managed by QOpenGLContext
1407 instances. Its purpose is to identify all the contexts that are sharing
1408 resources.
1409
1410 \sa QOpenGLContext::shareGroup()
1411*/
1412QOpenGLContextGroup::QOpenGLContextGroup()
1413 : QObject(*new QOpenGLContextGroupPrivate())
1414{
1415}
1416
1417/*!
1418 \internal
1419*/
1420QOpenGLContextGroup::~QOpenGLContextGroup()
1421{
1422 Q_D(QOpenGLContextGroup);
1423 d->cleanup();
1424}
1425
1426/*!
1427 Returns all the QOpenGLContext objects in this share group.
1428*/
1429QList<QOpenGLContext *> QOpenGLContextGroup::shares() const
1430{
1431 Q_D(const QOpenGLContextGroup);
1432 return d->m_shares;
1433}
1434
1435/*!
1436 Returns the QOpenGLContextGroup corresponding to the current context.
1437
1438 \sa QOpenGLContext::currentContext()
1439*/
1440QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup()
1441{
1442 QOpenGLContext *current = QOpenGLContext::currentContext();
1443 return current ? current->shareGroup() : 0;
1444}
1445
1446void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx)
1447{
1448 QMutexLocker locker(&m_mutex);
1449 m_refs.ref();
1450 m_shares << ctx;
1451}
1452
1453void QOpenGLContextGroupPrivate::removeContext(QOpenGLContext *ctx)
1454{
1455 Q_Q(QOpenGLContextGroup);
1456
1457 bool deleteObject = false;
1458
1459 {
1460 QMutexLocker locker(&m_mutex);
1461 m_shares.removeOne(ctx);
1462
1463 if (ctx == m_context && !m_shares.isEmpty())
1464 m_context = m_shares.constFirst();
1465
1466 if (!m_refs.deref()) {
1467 cleanup();
1468 deleteObject = true;
1469 }
1470 }
1471
1472 if (deleteObject) {
1473 if (q->thread() == QThread::currentThread())
1474 delete q; // Delete directly to prevent leak, refer to QTBUG-29056
1475 else
1476 q->deleteLater();
1477 }
1478}
1479
1480void QOpenGLContextGroupPrivate::cleanup()
1481{
1482 Q_Q(QOpenGLContextGroup);
1483 {
1484 QHash<QOpenGLMultiGroupSharedResource *, QOpenGLSharedResource *>::const_iterator it, end;
1485 end = m_resources.constEnd();
1486 for (it = m_resources.constBegin(); it != end; ++it)
1487 it.key()->cleanup(q, it.value());
1488 m_resources.clear();
1489 }
1490
1491 QList<QOpenGLSharedResource *>::iterator it = m_sharedResources.begin();
1492 QList<QOpenGLSharedResource *>::iterator end = m_sharedResources.end();
1493
1494 while (it != end) {
1495 (*it)->invalidateResource();
1496 (*it)->m_group = 0;
1497 ++it;
1498 }
1499
1500 m_sharedResources.clear();
1501
1502 qDeleteAll(m_pendingDeletion.begin(), m_pendingDeletion.end());
1503 m_pendingDeletion.clear();
1504}
1505
1506void QOpenGLContextGroupPrivate::deletePendingResources(QOpenGLContext *ctx)
1507{
1508 QMutexLocker locker(&m_mutex);
1509
1510 const QList<QOpenGLSharedResource *> pending = m_pendingDeletion;
1511 m_pendingDeletion.clear();
1512
1513 QList<QOpenGLSharedResource *>::const_iterator it = pending.begin();
1514 QList<QOpenGLSharedResource *>::const_iterator end = pending.end();
1515 while (it != end) {
1516 (*it)->freeResource(ctx);
1517 delete *it;
1518 ++it;
1519 }
1520}
1521
1522/*!
1523 \class QOpenGLSharedResource
1524 \internal
1525 \since 5.0
1526 \brief The QOpenGLSharedResource class is used to keep track of resources
1527 that are shared between OpenGL contexts (like textures, framebuffer
1528 objects, shader programs, etc), and clean them up in a safe way when
1529 they're no longer needed.
1530 \inmodule QtGui
1531
1532 The QOpenGLSharedResource instance should never be deleted, instead free()
1533 should be called when it's no longer needed. Thus it will be put on a queue
1534 and freed at an appropriate time (when a context in the share group becomes
1535 current).
1536
1537 The sub-class needs to implement two pure virtual functions. The first,
1538 freeResource() must be implemented to actually do the freeing, for example
1539 call glDeleteTextures() on a texture id. Qt makes sure a valid context in
1540 the resource's share group is current at the time. The other,
1541 invalidateResource(), is called by Qt in the circumstance when the last
1542 context in the share group is destroyed before free() has been called. The
1543 implementation of invalidateResource() should set any identifiers to 0 or
1544 set a flag to prevent them from being used later on.
1545*/
1546QOpenGLSharedResource::QOpenGLSharedResource(QOpenGLContextGroup *group)
1547 : m_group(group)
1548{
1549 QMutexLocker locker(&m_group->d_func()->m_mutex);
1550 m_group->d_func()->m_sharedResources << this;
1551}
1552
1553QOpenGLSharedResource::~QOpenGLSharedResource()
1554{
1555}
1556
1557// schedule the resource for deletion at an appropriate time
1558void QOpenGLSharedResource::free()
1559{
1560 if (!m_group) {
1561 delete this;
1562 return;
1563 }
1564
1565 QMutexLocker locker(&m_group->d_func()->m_mutex);
1566 m_group->d_func()->m_sharedResources.removeOne(this);
1567 m_group->d_func()->m_pendingDeletion << this;
1568
1569 // can we delete right away?
1570 QOpenGLContext *current = QOpenGLContext::currentContext();
1571 if (current && current->shareGroup() == m_group) {
1572 m_group->d_func()->deletePendingResources(current);
1573 }
1574}
1575
1576/*!
1577 \class QOpenGLSharedResourceGuard
1578 \internal
1579 \since 5.0
1580 \brief The QOpenGLSharedResourceGuard class is a convenience sub-class of
1581 QOpenGLSharedResource to be used to track a single OpenGL object with a
1582 GLuint identifier. The constructor takes a function pointer to a function
1583 that will be used to free the resource if and when necessary.
1584 \inmodule QtGui
1585
1586*/
1587void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context)
1588{
1589 if (m_id) {
1590 QOpenGLFunctions functions(context);
1591 m_func(&functions, m_id);
1592 m_id = 0;
1593 }
1594}
1595
1596/*!
1597 \class QOpenGLMultiGroupSharedResource
1598 \internal
1599 \since 5.0
1600 \brief The QOpenGLMultiGroupSharedResource keeps track of a shared resource
1601 that might be needed from multiple contexts, like a glyph cache or gradient
1602 cache. One instance of the object is created for each group when necessary.
1603 The shared resource instance should have a constructor that takes a
1604 QOpenGLContext *. To get an instance for a given context one calls
1605 T *QOpenGLMultiGroupSharedResource::value<T>(context), where T is a sub-class
1606 of QOpenGLSharedResource.
1607 \inmodule QtGui
1608
1609 You should not call free() on QOpenGLSharedResources owned by a
1610 QOpenGLMultiGroupSharedResource instance.
1611*/
1612QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource()
1613 : active(0)
1614{
1615#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1616 qDebug("Creating context group resource object %p.", this);
1617#endif
1618}
1619
1620QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource()
1621{
1622#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1623 qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size());
1624#endif
1625 for (int i = 0; i < m_groups.size(); ++i) {
1626 if (!m_groups.at(i)->shares().isEmpty()) {
1627 QOpenGLContext *context = m_groups.at(i)->shares().constFirst();
1628 QOpenGLSharedResource *resource = value(context);
1629 if (resource)
1630 resource->free();
1631 }
1632 m_groups.at(i)->d_func()->m_resources.remove(this);
1633 active.deref();
1634 }
1635#ifndef QT_NO_DEBUG
1636 if (active.loadRelaxed() != 0) {
1637 qWarning("QtGui: Resources are still available at program shutdown.\n"
1638 " This is possibly caused by a leaked QOpenGLWidget, \n"
1639 " QOpenGLFramebufferObject or QOpenGLPixelBuffer.");
1640 }
1641#endif
1642}
1643
1644void QOpenGLMultiGroupSharedResource::insert(QOpenGLContext *context, QOpenGLSharedResource *value)
1645{
1646#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1647 qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
1648#endif
1649 QOpenGLContextGroup *group = context->shareGroup();
1650 Q_ASSERT(!group->d_func()->m_resources.contains(this));
1651 group->d_func()->m_resources.insert(this, value);
1652 m_groups.append(group);
1653 active.ref();
1654}
1655
1656QOpenGLSharedResource *QOpenGLMultiGroupSharedResource::value(QOpenGLContext *context)
1657{
1658 QOpenGLContextGroup *group = context->shareGroup();
1659 return group->d_func()->m_resources.value(this, 0);
1660}
1661
1662QList<QOpenGLSharedResource *> QOpenGLMultiGroupSharedResource::resources() const
1663{
1664 QList<QOpenGLSharedResource *> result;
1665 for (QList<QOpenGLContextGroup *>::const_iterator it = m_groups.constBegin(); it != m_groups.constEnd(); ++it) {
1666 QOpenGLSharedResource *resource = (*it)->d_func()->m_resources.value(const_cast<QOpenGLMultiGroupSharedResource *>(this), 0);
1667 if (resource)
1668 result << resource;
1669 }
1670 return result;
1671}
1672
1673void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenGLSharedResource *value)
1674{
1675#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1676 qDebug("Cleaning up context group resource %p, for group %p in thread %p.", this, group, QThread::currentThread());
1677#endif
1678 value->invalidateResource();
1679 value->free();
1680 active.deref();
1681
1682 Q_ASSERT(m_groups.contains(group));
1683 m_groups.removeOne(group);
1684}
1685
1686#ifndef QT_NO_DEBUG_STREAM
1687QDebug operator<<(QDebug debug, const QOpenGLVersionProfile &vp)
1688{
1689 QDebugStateSaver saver(debug);
1690 debug.nospace();
1691 debug << "QOpenGLVersionProfile(";
1692 if (vp.isValid()) {
1693 debug << vp.version().first << '.' << vp.version().second
1694 << ", profile=" << vp.profile();
1695 } else {
1696 debug << "invalid";
1697 }
1698 debug << ')';
1699 return debug;
1700}
1701
1702QDebug operator<<(QDebug debug, const QOpenGLContext *ctx)
1703{
1704 QDebugStateSaver saver(debug);
1705 debug.nospace();
1706 debug.noquote();
1707 debug << "QOpenGLContext(";
1708 if (ctx) {
1709 debug << static_cast<const void *>(ctx);
1710 if (ctx->isValid()) {
1711 debug << ", nativeHandle=" << ctx->nativeHandle()
1712 << ", format=" << ctx->format();
1713 if (const QSurface *sf = ctx->surface())
1714 debug << ", surface=" << sf;
1715 if (const QScreen *s = ctx->screen())
1716 debug << ", screen=\"" << s->name() << '"';
1717 } else {
1718 debug << ", invalid";
1719 }
1720 } else {
1721 debug << '0';
1722 }
1723 debug << ')';
1724 return debug;
1725}
1726
1727QDebug operator<<(QDebug debug, const QOpenGLContextGroup *cg)
1728{
1729 QDebugStateSaver saver(debug);
1730 debug.nospace();
1731 debug << "QOpenGLContextGroup(";
1732 if (cg)
1733 debug << cg->shares();
1734 else
1735 debug << '0';
1736 debug << ')';
1737 return debug;
1738}
1739#endif // QT_NO_DEBUG_STREAM
1740
1741#include "moc_qopenglcontext.cpp"
1742
1743QT_END_NAMESPACE
1744