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

source code of qtbase/src/gui/kernel/qopenglcontext.cpp