Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qopenglcontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qpa/qplatformopenglcontext.h>
5#include <qpa/qplatformintegration.h>
8#include "qwindow.h"
9
10#include <QtCore/QThreadStorage>
11#include <QtCore/QThread>
12#include <QtCore/private/qlocking_p.h>
13
14#include <QtGui/private/qguiapplication_p.h>
15#include <QtGui/private/qopengl_p.h>
16#include <QtGui/private/qwindow_p.h>
17#include <QtGui/QScreen>
18#include <qpa/qplatformnativeinterface.h>
19
20#include <private/qopenglextensions_p.h>
21
22#include <QDebug>
23
25
27{
28public:
30 : context(nullptr)
31 {
32 }
34 if (context)
35 context->doneCurrent();
36 }
37 QOpenGLContext *context;
38};
39
41static QOpenGLContext *global_share_context = nullptr;
42
43#ifndef QT_NO_DEBUG
44QHash<QOpenGLContext *, bool> QOpenGLContextPrivate::makeCurrentTracker;
45Q_CONSTINIT QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex;
46#endif
47
48/*!
49 \internal
50
51 This function is used by Qt::AA_ShareOpenGLContexts and the Qt
52 WebEngine to set up context sharing across multiple windows. Do
53 not use it for any other purpose.
54
55 Please maintain the binary compatibility of these functions.
56*/
57void qt_gl_set_global_share_context(QOpenGLContext *context)
58{
59 global_share_context = context;
60}
61
62/*!
63 \internal
64*/
66{
68}
69
70/*!
71 \class QOpenGLContext
72 \ingroup painting-3D
73 \inmodule QtGui
74 \since 5.0
75 \brief The QOpenGLContext class represents a native OpenGL context, enabling
76 OpenGL rendering on a QSurface.
77
78 QOpenGLContext represents the OpenGL state of an underlying OpenGL context.
79 To set up a context, set its screen and format such that they match those
80 of the surface or surfaces with which the context is meant to be used, if
81 necessary make it share resources with other contexts with
82 setShareContext(), and finally call create(). Use the return value or isValid()
83 to check if the context was successfully initialized.
84
85 A context can be made current against a given surface by calling
86 makeCurrent(). When OpenGL rendering is done, call swapBuffers() to swap
87 the front and back buffers of the surface, so that the newly rendered
88 content becomes visible. To be able to support certain platforms,
89 QOpenGLContext requires that you call makeCurrent() again before starting
90 rendering a new frame, after calling swapBuffers().
91
92 If the context is temporarily not needed, such as when the application is
93 not rendering, it can be useful to delete it in order to free resources.
94 You can connect to the aboutToBeDestroyed() signal to clean up any
95 resources that have been allocated with different ownership from the
96 QOpenGLContext itself.
97
98 Once a QOpenGLContext has been made current, you can render to it in a
99 platform independent way by using Qt's OpenGL enablers such as
100 QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, and
101 QOpenGLFramebufferObject. It is also possible to use the platform's OpenGL
102 API directly, without using the Qt enablers, although potentially at the
103 cost of portability. The latter is necessary when wanting to use OpenGL 1.x
104 or OpenGL ES 1.x.
105
106 For more information about the OpenGL API, refer to the official
107 \l{http://www.opengl.org}{OpenGL documentation}.
108
109 For an example of how to use QOpenGLContext see the
110 \l{OpenGL Window Example}{OpenGL Window} example.
111
112 \section1 Thread Affinity
113
114 QOpenGLContext can be moved to a different thread with moveToThread(). Do
115 not call makeCurrent() from a different thread than the one to which the
116 QOpenGLContext object belongs. A context can only be current in one thread
117 and against one surface at a time, and a thread only has one context
118 current at a time.
119
120 \section1 Context Resource Sharing
121
122 Resources such as textures and vertex buffer objects
123 can be shared between contexts. Use setShareContext() before calling
124 create() to specify that the contexts should share these resources.
125 QOpenGLContext internally keeps track of a QOpenGLContextGroup object which
126 can be accessed with shareGroup(), and which can be used to find all the
127 contexts in a given share group. A share group consists of all contexts that
128 have been successfully initialized and are sharing with an existing context in
129 the share group. A non-sharing context has a share group consisting of a
130 single context.
131
132 \section1 Default Framebuffer
133
134 On certain platforms, a framebuffer other than 0 might be the default frame
135 buffer depending on the current surface. Instead of calling
136 glBindFramebuffer(0), it is recommended that you use
137 glBindFramebuffer(ctx->defaultFramebufferObject()), to ensure that your
138 application is portable between different platforms. However, if you use
139 QOpenGLFunctions::glBindFramebuffer(), this is done automatically for you.
140
141 \warning WebAssembly
142
143 We recommend that only one QOpenGLContext is made current with a QSurface,
144 for the entire lifetime of the QSurface. Should more than once context be used,
145 it is important to understand that multiple QOpenGLContext instances may be
146 backed by the same native context underneath with the WebAssembly platform.
147 Therefore, calling makeCurrent() with the same QSurface on two QOpenGLContext
148 objects may not switch to a different native context in the second call. As
149 a result, any OpenGL state changes done after the second makeCurrent() may
150 alter the state of the first QOpenGLContext as well, as they are all backed
151 by the same native context.
152
153 \note This means that when targeting WebAssembly with existing OpenGL-based
154 Qt code, some porting may be required to cater to these limitations.
155
156
157 \sa QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, QOpenGLFramebufferObject
158*/
159
160/*!
161 \internal
162
163 Set the current context. Returns the previously current context.
164*/
165QOpenGLContext *QOpenGLContextPrivate::setCurrentContext(QOpenGLContext *context)
166{
167 QGuiGLThreadContext *threadContext = qwindow_context_storage()->localData();
168 if (!threadContext) {
169 if (!QThread::currentThread()) {
170 qWarning("No QTLS available. currentContext won't work");
171 return nullptr;
172 }
173 if (!context)
174 return nullptr;
175
176 threadContext = new QGuiGLThreadContext;
177 qwindow_context_storage()->setLocalData(threadContext);
178 }
179 QOpenGLContext *previous = threadContext->context;
180 threadContext->context = context;
181 return previous;
182}
183
184int QOpenGLContextPrivate::maxTextureSize()
185{
186 if (max_texture_size != -1)
187 return max_texture_size;
188
189 Q_Q(QOpenGLContext);
190 QOpenGLFunctions *funcs = q->functions();
191 funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
192
193#if !QT_CONFIG(opengles2)
194 if (!q->isOpenGLES()) {
195 GLenum proxy = GL_PROXY_TEXTURE_2D;
196
197 GLint size;
198 GLint next = 64;
199 funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
200
201 QOpenGLExtraFunctions *extraFuncs = q->extraFunctions();
202 extraFuncs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size);
203
204 if (size == 0) {
205 return max_texture_size;
206 }
207 do {
208 size = next;
209 next = size * 2;
210
211 if (next > max_texture_size)
212 break;
213 funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
214 extraFuncs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next);
215 } while (next > size);
216
217 max_texture_size = size;
218 }
219#endif // QT_CONFIG(opengles2)
220
221 return max_texture_size;
222}
223
224/*!
225 Returns the last context which called makeCurrent in the current thread,
226 or \nullptr, if no context is current.
227*/
228QOpenGLContext* QOpenGLContext::currentContext()
229{
230 QGuiGLThreadContext *threadContext = qwindow_context_storage()->localData();
231 if (threadContext)
232 return threadContext->context;
233 return nullptr;
234}
235
236/*!
237 Returns \c true if the \a first and \a second contexts are sharing OpenGL resources.
238*/
239bool QOpenGLContext::areSharing(QOpenGLContext *first, QOpenGLContext *second)
240{
241 return first->shareGroup() == second->shareGroup();
242}
243
244/*!
245 Returns the underlying platform context.
246
247 \internal
248*/
249QPlatformOpenGLContext *QOpenGLContext::handle() const
250{
251 Q_D(const QOpenGLContext);
252 return d->platformGLContext;
253}
254
255/*!
256 Returns the underlying platform context with which this context is sharing.
257
258 \internal
259*/
260
261QPlatformOpenGLContext *QOpenGLContext::shareHandle() const
262{
263 Q_D(const QOpenGLContext);
264 if (d->shareContext)
265 return d->shareContext->handle();
266 return nullptr;
267}
268
269/*!
270 Creates a new OpenGL context instance with parent object \a parent.
271
272 Before it can be used you need to set the proper format and call create().
273
274 \sa create(), makeCurrent()
275*/
276QOpenGLContext::QOpenGLContext(QObject *parent)
277 : QObject(*new QOpenGLContextPrivate(), parent)
278{
279 setScreen(QGuiApplication::primaryScreen());
280}
281
282/*!
283 Sets the \a format the OpenGL context should be compatible with. You need
284 to call create() before it takes effect.
285
286 When the format is not explicitly set via this function, the format returned
287 by QSurfaceFormat::defaultFormat() will be used. This means that when having
288 multiple contexts, individual calls to this function can be replaced by one
289 single call to QSurfaceFormat::setDefaultFormat() before creating the first
290 context.
291*/
292void QOpenGLContext::setFormat(const QSurfaceFormat &format)
293{
294 Q_D(QOpenGLContext);
295 d->requestedFormat = format;
296}
297
298/*!
299 Makes this context share textures, shaders, and other OpenGL resources
300 with \a shareContext. You need to call create() before it takes effect.
301*/
302void QOpenGLContext::setShareContext(QOpenGLContext *shareContext)
303{
304 Q_D(QOpenGLContext);
305 d->shareContext = shareContext;
306}
307
308/*!
309 Sets the \a screen the OpenGL context should be valid for. You need to call
310 create() before it takes effect.
311*/
312void QOpenGLContext::setScreen(QScreen *screen)
313{
314 Q_D(QOpenGLContext);
315 if (d->screen)
316 disconnect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(_q_screenDestroyed(QObject*)));
317 d->screen = screen;
318 if (!d->screen)
319 d->screen = QGuiApplication::primaryScreen();
320 if (d->screen)
321 connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(_q_screenDestroyed(QObject*)));
322}
323
324void QOpenGLContextPrivate::_q_screenDestroyed(QObject *object)
325{
326 Q_Q(QOpenGLContext);
327 if (object == static_cast<QObject *>(screen)) {
328 screen = nullptr;
329 q->setScreen(nullptr);
330 }
331}
332
333/*!
334 \fn template <typename QNativeInterface> QNativeInterface *QOpenGLContext::nativeInterface() const
335
336 Returns a native interface of the given type for the context.
337
338 This function provides access to platform specific functionality
339 of QOpenGLContext, as defined in the QNativeInterface namespace:
340
341 \annotatedlist native-interfaces-qopenglcontext
342
343 If the requested interface is not available a \nullptr is returned.
344 */
345
346/*!
347 Attempts to create the OpenGL context with the current configuration.
348
349 The current configuration includes the format, the share context, and the
350 screen.
351
352 If the OpenGL implementation on your system does not support the requested
353 version of OpenGL context, then QOpenGLContext will try to create the closest
354 matching version. The actual created context properties can be queried
355 using the QSurfaceFormat returned by the format() function. For example, if
356 you request a context that supports OpenGL 4.3 Core profile but the driver
357 and/or hardware only supports version 3.2 Core profile contexts then you will
358 get a 3.2 Core profile context.
359
360 Returns \c true if the native context was successfully created and is ready to
361 be used with makeCurrent(), swapBuffers(), etc.
362
363 \note If the context already exists, this function destroys the existing
364 context first, and then creates a new one.
365
366 \sa makeCurrent(), format()
367*/
368bool QOpenGLContext::create()
369{
370 Q_D(QOpenGLContext);
371 if (d->platformGLContext)
372 destroy();
373
374 auto *platformContext = QGuiApplicationPrivate::platformIntegration()->createPlatformOpenGLContext(this);
375 if (!platformContext)
376 return false;
377
378 d->adopt(platformContext);
379
380 return isValid();
381}
382
383QOpenGLContextPrivate::~QOpenGLContextPrivate()
384{
385}
386
387void QOpenGLContextPrivate::adopt(QPlatformOpenGLContext *context)
388{
389 Q_Q(QOpenGLContext);
390
391 platformGLContext = context;
392 platformGLContext->setContext(q);
393 platformGLContext->initialize();
394
395 if (!platformGLContext->isSharing())
396 shareContext = nullptr;
397 shareGroup = shareContext ? shareContext->shareGroup() : new QOpenGLContextGroup;
398 shareGroup->d_func()->addContext(q);
399}
400
401/*!
402 \internal
403
404 Destroy the underlying platform context associated with this context.
405
406 If any other context is directly or indirectly sharing resources with this
407 context, the shared resources, which includes vertex buffer objects, shader
408 objects, textures, and framebuffer objects, are not freed. However,
409 destroying the underlying platform context frees any state associated with
410 the context.
411
412 After \c destroy() has been called, you must call create() if you wish to
413 use the context again.
414
415 \note This implicitly calls doneCurrent() if the context is current.
416
417 \sa create()
418*/
419void QOpenGLContext::destroy()
420{
421 Q_D(QOpenGLContext);
422
423 // Notify that the native context and the QPlatformOpenGLContext are going
424 // to go away.
425 if (d->platformGLContext)
426 emit aboutToBeDestroyed();
427
428 // Invoke callbacks for helpers and invalidate.
429 if (d->textureFunctionsDestroyCallback) {
430 d->textureFunctionsDestroyCallback();
431 d->textureFunctionsDestroyCallback = nullptr;
432 }
433 d->textureFunctions = nullptr;
434
435 delete d->versionFunctions;
436 d->versionFunctions = nullptr;
437
438 if (d->vaoHelperDestroyCallback) {
439 Q_ASSERT(d->vaoHelper);
440 d->vaoHelperDestroyCallback(d->vaoHelper);
441 d->vaoHelperDestroyCallback = nullptr;
442 }
443 d->vaoHelper = nullptr;
444
445 // Tear down function wrappers.
446 delete d->versionFunctions;
447 d->versionFunctions = nullptr;
448
449 delete d->functions;
450 d->functions = nullptr;
451
452 // Clean up and destroy the native context machinery.
453 if (QOpenGLContext::currentContext() == this)
454 doneCurrent();
455
456 if (d->shareGroup)
457 d->shareGroup->d_func()->removeContext(this);
458
459 d->shareGroup = nullptr;
460
461 delete d->platformGLContext;
462 d->platformGLContext = nullptr;
463}
464
465/*!
466 \fn void QOpenGLContext::aboutToBeDestroyed()
467
468 This signal is emitted before the underlying native OpenGL context is
469 destroyed, such that users may clean up OpenGL resources that might
470 otherwise be left dangling in the case of shared OpenGL contexts.
471
472 If you wish to make the context current in order to do clean-up, make sure
473 to only connect to the signal using a direct connection.
474
475 \note In Qt for Python, this signal will not be received when emitted
476 from the destructor of QOpenGLWidget or QOpenGLWindow due to the Python
477 instance already being destroyed. We recommend doing cleanups
478 in QWidget::hideEvent() instead.
479*/
480
481/*!
482 Destroys the QOpenGLContext object.
483
484 If this is the current context for the thread, doneCurrent() is also called.
485*/
486QOpenGLContext::~QOpenGLContext()
487{
488 destroy();
489
490#ifndef QT_NO_DEBUG
491 QOpenGLContextPrivate::cleanMakeCurrentTracker(this);
492#endif
493}
494
495/*!
496 Returns if this context is valid, i.e. has been successfully created.
497
498 On some platforms the return value of \c false for a context that was
499 successfully created previously indicates that the OpenGL context was lost.
500
501 The typical way to handle context loss scenarios in applications is to
502 check via this function whenever makeCurrent() fails and returns \c false.
503 If this function then returns \c false, recreate the underlying native
504 OpenGL context by calling create(), call makeCurrent() again and then
505 reinitialize all OpenGL resources.
506
507 On some platforms context loss situations is not something that can
508 avoided. On others however, they may need to be opted-in to. This can be
509 done by enabling \l{QSurfaceFormat::ResetNotification}{ResetNotification} in
510 the QSurfaceFormat. This will lead to setting
511 \c{RESET_NOTIFICATION_STRATEGY_EXT} to \c{LOSE_CONTEXT_ON_RESET_EXT} in the
512 underlying native OpenGL context. QOpenGLContext will then monitor the
513 status via \c{glGetGraphicsResetStatusEXT()} in every makeCurrent().
514
515 \sa create()
516*/
517bool QOpenGLContext::isValid() const
518{
519 Q_D(const QOpenGLContext);
520 return d->platformGLContext && d->platformGLContext->isValid();
521}
522
523/*!
524 Get the QOpenGLFunctions instance for this context.
525
526 QOpenGLContext offers this as a convenient way to access QOpenGLFunctions
527 without having to manage it manually.
528
529 The context or a sharing context must be current.
530
531 The returned QOpenGLFunctions instance is ready to be used and it
532 does not need initializeOpenGLFunctions() to be called.
533*/
534QOpenGLFunctions *QOpenGLContext::functions() const
535{
536 Q_D(const QOpenGLContext);
537 if (!d->functions)
538 const_cast<QOpenGLFunctions *&>(d->functions) = new QOpenGLExtensions(QOpenGLContext::currentContext());
539 return d->functions;
540}
541
542/*!
543 Get the QOpenGLExtraFunctions instance for this context.
544
545 QOpenGLContext offers this as a convenient way to access QOpenGLExtraFunctions
546 without having to manage it manually.
547
548 The context or a sharing context must be current.
549
550 The returned QOpenGLExtraFunctions instance is ready to be used and it
551 does not need initializeOpenGLFunctions() to be called.
552
553 \note QOpenGLExtraFunctions contains functionality that is not guaranteed to
554 be available at runtime. Runtime availability depends on the platform,
555 graphics driver, and the OpenGL version requested by the application.
556
557 \sa QOpenGLFunctions, QOpenGLExtraFunctions
558*/
559QOpenGLExtraFunctions *QOpenGLContext::extraFunctions() const
560{
561 return static_cast<QOpenGLExtraFunctions *>(functions());
562}
563
564/*!
565 Returns the set of OpenGL extensions supported by this context.
566
567 The context or a sharing context must be current.
568
569 \sa hasExtension()
570*/
571QSet<QByteArray> QOpenGLContext::extensions() const
572{
573 Q_D(const QOpenGLContext);
574 if (d->extensionNames.isEmpty()) {
575 QOpenGLExtensionMatcher matcher;
576 d->extensionNames = matcher.extensions();
577 }
578
579 return d->extensionNames;
580}
581
582/*!
583 Returns \c true if this OpenGL context supports the specified OpenGL
584 \a extension, \c false otherwise.
585
586 The context or a sharing context must be current.
587
588 \sa extensions()
589*/
590bool QOpenGLContext::hasExtension(const QByteArray &extension) const
591{
592 return extensions().contains(extension);
593}
594
595/*!
596 Call this to get the default framebuffer object for the current surface.
597
598 On some platforms (for instance, iOS) the default framebuffer object depends
599 on the surface being rendered to, and might be different from 0. Thus,
600 instead of calling glBindFramebuffer(0), you should call
601 glBindFramebuffer(ctx->defaultFramebufferObject()) if you want your
602 application to work across different Qt platforms.
603
604 If you use the glBindFramebuffer() in QOpenGLFunctions you do not have to
605 worry about this, as it automatically binds the current context's
606 defaultFramebufferObject() when 0 is passed.
607
608 \note Widgets that render via framebuffer objects, like QOpenGLWidget and
609 QQuickWidget, will override the value returned from this function when
610 painting is active, because at that time the correct "default" framebuffer
611 is the widget's associated backing framebuffer, not the platform-specific
612 one belonging to the top-level window's surface. This ensures the expected
613 behavior for this function and other classes relying on it (for example,
614 QOpenGLFramebufferObject::bindDefault() or
615 QOpenGLFramebufferObject::release()).
616
617 \sa QOpenGLFramebufferObject
618*/
619GLuint QOpenGLContext::defaultFramebufferObject() const
620{
621 if (!isValid())
622 return 0;
623
624 Q_D(const QOpenGLContext);
625 if (!d->surface || !d->surface->surfaceHandle())
626 return 0;
627
628 if (d->defaultFboRedirect)
629 return d->defaultFboRedirect;
630
631 return d->platformGLContext->defaultFramebufferObject(d->surface->surfaceHandle());
632}
633
634/*!
635 Makes the context current in the current thread, against the given
636 \a surface. Returns \c true if successful; otherwise returns \c false.
637 The latter may happen if the surface is not exposed, or the graphics
638 hardware is not available due to e.g. the application being suspended.
639
640 If \a surface is \nullptr this is equivalent to calling doneCurrent().
641
642 Avoid calling this function from a different thread than the one the
643 QOpenGLContext instance lives in. If you wish to use QOpenGLContext from a
644 different thread you should first make sure it's not current in the
645 current thread, by calling doneCurrent() if necessary. Then call
646 moveToThread(otherThread) before using it in the other thread.
647
648 By default Qt employs a check that enforces the above condition on the
649 thread affinity. It is still possible to disable this check by setting the
650 \c{Qt::AA_DontCheckOpenGLContextThreadAffinity} application attribute. Be
651 sure to understand the consequences of using QObjects from outside
652 the thread they live in, as explained in the
653 \l{QObject#Thread Affinity}{QObject thread affinity} documentation.
654
655 \sa functions(), doneCurrent(), Qt::AA_DontCheckOpenGLContextThreadAffinity
656*/
657bool QOpenGLContext::makeCurrent(QSurface *surface)
658{
659 Q_D(QOpenGLContext);
660 if (!isValid())
661 return false;
662
663 if (Q_UNLIKELY(!qApp->testAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity)
664 && thread() != QThread::currentThread())) {
665 qFatal("Cannot make QOpenGLContext current in a different thread");
666 }
667
668 if (!surface) {
669 doneCurrent();
670 return true;
671 }
672
673 if (!surface->surfaceHandle())
674 return false;
675 if (!surface->supportsOpenGL()) {
676 qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface" << surface;
677 return false;
678 }
679
680 if (!d->platformGLContext->makeCurrent(surface->surfaceHandle()))
681 return false;
682
683 QOpenGLContextPrivate::setCurrentContext(this);
684#ifndef QT_NO_DEBUG
685 QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true);
686#endif
687
688 d->surface = surface;
689
690 static bool needsWorkaroundSet = false;
691 static bool needsWorkaround = false;
692
693 if (!needsWorkaroundSet) {
694 QByteArray env;
695#ifdef Q_OS_ANDROID
696 env = qgetenv(QByteArrayLiteral("QT_ANDROID_DISABLE_GLYPH_CACHE_WORKAROUND"));
697 needsWorkaround = env.isEmpty() || env == QByteArrayLiteral("0") || env == QByteArrayLiteral("false");
698#endif
699 env = qgetenv(QByteArrayLiteral("QT_ENABLE_GLYPH_CACHE_WORKAROUND"));
700 if (env == QByteArrayLiteral("1") || env == QByteArrayLiteral("true"))
701 needsWorkaround = true;
702
703 if (!needsWorkaround) {
704 const char *rendererString = reinterpret_cast<const char *>(functions()->glGetString(GL_RENDERER));
705 if (rendererString)
706 needsWorkaround =
707 qstrncmp(rendererString, "Mali-4xx", 6) == 0 // Mali-400, Mali-450
708 || qstrcmp(rendererString, "Mali-T880") == 0
709 || qstrncmp(rendererString, "Adreno (TM) 2xx", 13) == 0 // Adreno 200, 203, 205
710 || qstrncmp(rendererString, "Adreno 2xx", 8) == 0 // Same as above but without the '(TM)'
711 || qstrncmp(rendererString, "Adreno (TM) 3xx", 13) == 0 // Adreno 302, 305, 320, 330
712 || qstrncmp(rendererString, "Adreno 3xx", 8) == 0 // Same as above but without the '(TM)'
713 || qstrncmp(rendererString, "Adreno (TM) 4xx", 13) == 0 // Adreno 405, 418, 420, 430
714 || qstrncmp(rendererString, "Adreno 4xx", 8) == 0 // Same as above but without the '(TM)'
715 || qstrncmp(rendererString, "Adreno (TM) 5xx", 13) == 0 // Adreno 505, 506, 510, 530, 540
716 || qstrncmp(rendererString, "Adreno 5xx", 8) == 0 // Same as above but without the '(TM)'
717 || qstrncmp(rendererString, "Adreno (TM) 6xx", 13) == 0 // Adreno 610, 620, 630
718 || qstrncmp(rendererString, "Adreno 6xx", 8) == 0 // Same as above but without the '(TM)'
719 || qstrcmp(rendererString, "GC800 core") == 0
720 || qstrcmp(rendererString, "GC1000 core") == 0
721 || strstr(rendererString, "GC2000") != nullptr
722 || qstrcmp(rendererString, "Immersion.16") == 0
723 || qstrncmp(rendererString, "Apple Mx", 7) == 0;
724 }
725 needsWorkaroundSet = true;
726 }
727
728 if (needsWorkaround)
729 d->workaround_brokenFBOReadBack = true;
730
731 d->shareGroup->d_func()->deletePendingResources(this);
732
733 return true;
734}
735
736/*!
737 Convenience function for calling makeCurrent with a 0 surface.
738
739 This results in no context being current in the current thread.
740
741 \sa makeCurrent(), currentContext()
742*/
743void QOpenGLContext::doneCurrent()
744{
745 Q_D(QOpenGLContext);
746
747 if (isValid()) {
748 if (QOpenGLContext::currentContext() == this)
749 d->shareGroup->d_func()->deletePendingResources(this);
750 d->platformGLContext->doneCurrent();
751 }
752
753 QOpenGLContextPrivate::setCurrentContext(nullptr);
754
755 d->surface = nullptr;
756}
757
758/*!
759 Returns the surface the context has been made current with.
760
761 This is the surface passed as an argument to makeCurrent().
762*/
763QSurface *QOpenGLContext::surface() const
764{
765 Q_D(const QOpenGLContext);
766 return d->surface;
767}
768
769
770/*!
771 Swap the back and front buffers of \a surface.
772
773 Call this to finish a frame of OpenGL rendering, and make sure to
774 call makeCurrent() again before issuing any further OpenGL commands,
775 for example as part of a new frame.
776*/
777void QOpenGLContext::swapBuffers(QSurface *surface)
778{
779 Q_D(QOpenGLContext);
780 if (!isValid())
781 return;
782
783 if (!surface) {
784 qWarning("QOpenGLContext::swapBuffers() called with null argument");
785 return;
786 }
787
788 if (!surface->supportsOpenGL()) {
789 qWarning("QOpenGLContext::swapBuffers() called with non-opengl surface");
790 return;
791 }
792
793 QPlatformSurface *surfaceHandle = surface->surfaceHandle();
794 if (!surfaceHandle)
795 return;
796
797#if !defined(QT_NO_DEBUG)
798 if (!QOpenGLContextPrivate::toggleMakeCurrentTracker(this, false))
799 qWarning("QOpenGLContext::swapBuffers() called without corresponding makeCurrent()");
800#endif
801 if (surface->format().swapBehavior() == QSurfaceFormat::SingleBuffer)
802 functions()->glFlush();
803 d->platformGLContext->swapBuffers(surfaceHandle);
804}
805
806/*!
807 Resolves the function pointer to an OpenGL extension function, identified by \a procName
808
809 Returns \nullptr if no such function can be found.
810*/
811QFunctionPointer QOpenGLContext::getProcAddress(const QByteArray &procName) const
812{
813 return getProcAddress(procName.constData());
814}
815
816/*!
817 \overload
818 \since 5.8
819 */
820QFunctionPointer QOpenGLContext::getProcAddress(const char *procName) const
821{
822 Q_D(const QOpenGLContext);
823 if (!d->platformGLContext)
824 return nullptr;
825 return d->platformGLContext->getProcAddress(procName);
826}
827
828/*!
829 Returns the format of the underlying platform context, if create() has been called.
830
831 Otherwise, returns the requested format.
832
833 The requested and the actual format may differ. Requesting a given OpenGL version does
834 not mean the resulting context will target exactly the requested version. It is only
835 guaranteed that the version/profile/options combination for the created context is
836 compatible with the request, as long as the driver is able to provide such a context.
837
838 For example, requesting an OpenGL version 3.x core profile context may result in an
839 OpenGL 4.x core profile context. Similarly, a request for OpenGL 2.1 may result in an
840 OpenGL 3.0 context with deprecated functions enabled. Finally, depending on the
841 driver, unsupported versions may result in either a context creation failure or in a
842 context for the highest supported version.
843
844 Similar differences are possible in the buffer sizes, for example, the resulting
845 context may have a larger depth buffer than requested. This is perfectly normal.
846*/
847QSurfaceFormat QOpenGLContext::format() const
848{
849 Q_D(const QOpenGLContext);
850 if (!d->platformGLContext)
851 return d->requestedFormat;
852 return d->platformGLContext->format();
853}
854
855/*!
856 Returns the share group this context belongs to.
857*/
858QOpenGLContextGroup *QOpenGLContext::shareGroup() const
859{
860 Q_D(const QOpenGLContext);
861 return d->shareGroup;
862}
863
864/*!
865 Returns the share context this context was created with.
866
867 If the underlying platform was not able to support the requested
868 sharing, this will return 0.
869*/
870QOpenGLContext *QOpenGLContext::shareContext() const
871{
872 Q_D(const QOpenGLContext);
873 return d->shareContext;
874}
875
876/*!
877 Returns the screen the context was created for.
878*/
879QScreen *QOpenGLContext::screen() const
880{
881 Q_D(const QOpenGLContext);
882 return d->screen;
883}
884
885/*!
886 \enum QOpenGLContext::OpenGLModuleType
887 This enum defines the type of the underlying OpenGL implementation.
888
889 \value LibGL OpenGL
890 \value LibGLES OpenGL ES 2.0 or higher
891
892 \since 5.3
893*/
894
895/*!
896 Returns the underlying OpenGL implementation type.
897
898 On platforms where the OpenGL implementation is not dynamically
899 loaded, the return value is determined during compile time and never
900 changes.
901
902 \note A desktop OpenGL implementation may be capable of creating
903 ES-compatible contexts too. Therefore in most cases it is more
904 appropriate to check QSurfaceFormat::renderableType() or use
905 the convenience function isOpenGLES().
906
907 \note This function requires that the QGuiApplication instance is already created.
908
909 \since 5.3
910 */
911QOpenGLContext::OpenGLModuleType QOpenGLContext::openGLModuleType()
912{
913#if defined(QT_OPENGL_DYNAMIC)
914 Q_ASSERT(qGuiApp);
915 return QGuiApplicationPrivate::instance()->platformIntegration()->openGLModuleType();
916#elif QT_CONFIG(opengles2)
917 return LibGLES;
918#else
919 return LibGL;
920#endif
921}
922
923/*!
924 Returns true if the context is an OpenGL ES context.
925
926 If the context has not yet been created, the result is based on the
927 requested format set via setFormat().
928
929 \sa create(), format(), setFormat()
930
931 \since 5.3
932 */
933bool QOpenGLContext::isOpenGLES() const
934{
935 return format().renderableType() == QSurfaceFormat::OpenGLES;
936}
937
938/*!
939 Returns \c true if the platform supports OpenGL rendering outside the main (gui)
940 thread.
941
942 The value is controlled by the platform plugin in use and may also depend on the
943 graphics drivers.
944
945 \since 5.5
946 */
947bool QOpenGLContext::supportsThreadedOpenGL()
948{
949 Q_ASSERT(qGuiApp);
950 return QGuiApplicationPrivate::instance()->platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
951}
952
953/*!
954 \since 5.5
955
956 Returns the application-wide shared OpenGL context, if present.
957 Otherwise, returns \nullptr.
958
959 This is useful if you need to upload OpenGL objects (buffers, textures,
960 etc.) before creating or showing a QOpenGLWidget or QQuickWidget.
961
962 \note You must set the Qt::AA_ShareOpenGLContexts flag on QGuiApplication
963 before creating the QGuiApplication object, otherwise Qt may not create a
964 global shared context.
965
966 \warning Do not attempt to make the context returned by this function
967 current on any surface. Instead, you can create a new context which shares
968 with the global one, and then make the new context current.
969
970 \sa Qt::AA_ShareOpenGLContexts, setShareContext(), makeCurrent()
971*/
972QOpenGLContext *QOpenGLContext::globalShareContext()
973{
974 Q_ASSERT(qGuiApp);
975 return qt_gl_global_share_context();
976}
977
978/*!
979 \internal
980*/
981QOpenGLTextureHelper* QOpenGLContext::textureFunctions() const
982{
983 Q_D(const QOpenGLContext);
984 return d->textureFunctions;
985}
986
987/*!
988 \internal
989*/
990void QOpenGLContext::setTextureFunctions(QOpenGLTextureHelper* textureFuncs, std::function<void()> destroyCallback)
991{
992 Q_D(QOpenGLContext);
993 d->textureFunctions = textureFuncs;
994 d->textureFunctionsDestroyCallback = destroyCallback;
995}
996
997/*!
998 \class QOpenGLContextGroup
999 \since 5.0
1000 \brief The QOpenGLContextGroup class represents a group of contexts sharing
1001 OpenGL resources.
1002 \inmodule QtGui
1003
1004 QOpenGLContextGroup is automatically created and managed by QOpenGLContext
1005 instances. Its purpose is to identify all the contexts that are sharing
1006 resources.
1007
1008 \sa QOpenGLContext::shareGroup()
1009*/
1010QOpenGLContextGroup::QOpenGLContextGroup()
1011 : QObject(*new QOpenGLContextGroupPrivate())
1012{
1013}
1014
1015/*!
1016 \internal
1017*/
1018QOpenGLContextGroup::~QOpenGLContextGroup()
1019{
1020 Q_D(QOpenGLContextGroup);
1021 d->cleanup();
1022}
1023
1024/*!
1025 Returns all the QOpenGLContext objects in this share group.
1026*/
1027QList<QOpenGLContext *> QOpenGLContextGroup::shares() const
1028{
1029 Q_D(const QOpenGLContextGroup);
1030 return d->m_shares;
1031}
1032
1033/*!
1034 Returns the QOpenGLContextGroup corresponding to the current context.
1035
1036 \sa QOpenGLContext::currentContext()
1037*/
1038QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup()
1039{
1040 QOpenGLContext *current = QOpenGLContext::currentContext();
1041 return current ? current->shareGroup() : nullptr;
1042}
1043
1044QOpenGLContextGroupPrivate::~QOpenGLContextGroupPrivate()
1045 = default;
1046
1047void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx)
1048{
1049 const auto locker = qt_scoped_lock(m_mutex);
1050 m_refs.ref();
1051 m_shares << ctx;
1052}
1053
1054void QOpenGLContextGroupPrivate::removeContext(QOpenGLContext *ctx)
1055{
1056 Q_Q(QOpenGLContextGroup);
1057
1058 bool deleteObject = false;
1059
1060 {
1061 const auto locker = qt_scoped_lock(m_mutex);
1062 m_shares.removeOne(ctx);
1063
1064 if (ctx == m_context && !m_shares.isEmpty())
1065 m_context = m_shares.constFirst();
1066
1067 if (!m_refs.deref()) {
1068 cleanup();
1069 deleteObject = true;
1070 }
1071 }
1072
1073 if (deleteObject) {
1074 if (q->thread() == QThread::currentThread())
1075 delete q; // Delete directly to prevent leak, refer to QTBUG-29056
1076 else
1077 q->deleteLater();
1078 }
1079}
1080
1081void QOpenGLContextGroupPrivate::cleanup()
1082{
1083 Q_Q(QOpenGLContextGroup);
1084 {
1085 QHash<QOpenGLMultiGroupSharedResource *, QOpenGLSharedResource *>::const_iterator it, end;
1086 end = m_resources.constEnd();
1087 for (it = m_resources.constBegin(); it != end; ++it)
1088 it.key()->cleanup(q, it.value());
1089 m_resources.clear();
1090 }
1091
1092 QList<QOpenGLSharedResource *>::iterator it = m_sharedResources.begin();
1093 QList<QOpenGLSharedResource *>::iterator end = m_sharedResources.end();
1094
1095 while (it != end) {
1096 (*it)->invalidateResource();
1097 (*it)->m_group = nullptr;
1098 ++it;
1099 }
1100
1101 m_sharedResources.clear();
1102
1103 qDeleteAll(m_pendingDeletion.begin(), m_pendingDeletion.end());
1104 m_pendingDeletion.clear();
1105}
1106
1107void QOpenGLContextGroupPrivate::deletePendingResources(QOpenGLContext *ctx)
1108{
1109 const auto locker = qt_scoped_lock(m_mutex);
1110
1111 const QList<QOpenGLSharedResource *> pending = m_pendingDeletion;
1112 m_pendingDeletion.clear();
1113
1114 QList<QOpenGLSharedResource *>::const_iterator it = pending.begin();
1115 QList<QOpenGLSharedResource *>::const_iterator end = pending.end();
1116 while (it != end) {
1117 (*it)->freeResource(ctx);
1118 delete *it;
1119 ++it;
1120 }
1121}
1122
1123/*!
1124 \class QOpenGLSharedResource
1125 \internal
1126 \since 5.0
1127 \brief The QOpenGLSharedResource class is used to keep track of resources
1128 that are shared between OpenGL contexts (like textures, framebuffer
1129 objects, shader programs, etc), and clean them up in a safe way when
1130 they're no longer needed.
1131 \inmodule QtGui
1132
1133 The QOpenGLSharedResource instance should never be deleted, instead free()
1134 should be called when it's no longer needed. Thus it will be put on a queue
1135 and freed at an appropriate time (when a context in the share group becomes
1136 current).
1137
1138 The sub-class needs to implement two pure virtual functions. The first,
1139 freeResource() must be implemented to actually do the freeing, for example
1140 call glDeleteTextures() on a texture id. Qt makes sure a valid context in
1141 the resource's share group is current at the time. The other,
1142 invalidateResource(), is called by Qt in the circumstance when the last
1143 context in the share group is destroyed before free() has been called. The
1144 implementation of invalidateResource() should set any identifiers to 0 or
1145 set a flag to prevent them from being used later on.
1146*/
1147QOpenGLSharedResource::QOpenGLSharedResource(QOpenGLContextGroup *group)
1148 : m_group(group)
1149{
1150 const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex);
1151 m_group->d_func()->m_sharedResources << this;
1152}
1153
1154QOpenGLSharedResource::~QOpenGLSharedResource()
1155{
1156}
1157
1158// schedule the resource for deletion at an appropriate time
1159void QOpenGLSharedResource::free()
1160{
1161 if (!m_group) {
1162 delete this;
1163 return;
1164 }
1165
1166 const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex);
1167 m_group->d_func()->m_sharedResources.removeOne(this);
1168 m_group->d_func()->m_pendingDeletion << this;
1169
1170 // can we delete right away?
1171 QOpenGLContext *current = QOpenGLContext::currentContext();
1172 if (current && current->shareGroup() == m_group) {
1173 m_group->d_func()->deletePendingResources(current);
1174 }
1175}
1176
1177/*!
1178 \class QOpenGLSharedResourceGuard
1179 \internal
1180 \since 5.0
1181 \brief The QOpenGLSharedResourceGuard class is a convenience sub-class of
1182 QOpenGLSharedResource to be used to track a single OpenGL object with a
1183 GLuint identifier. The constructor takes a function pointer to a function
1184 that will be used to free the resource if and when necessary.
1185 \inmodule QtGui
1186
1187*/
1188
1189QOpenGLSharedResourceGuard::~QOpenGLSharedResourceGuard()
1190 = default;
1191
1192void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context)
1193{
1194 if (m_id) {
1195 QOpenGLFunctions functions(context);
1196 m_func(&functions, m_id);
1197 m_id = 0;
1198 }
1199}
1200
1201/*!
1202 \class QOpenGLMultiGroupSharedResource
1203 \internal
1204 \since 5.0
1205 \brief The QOpenGLMultiGroupSharedResource keeps track of a shared resource
1206 that might be needed from multiple contexts, like a glyph cache or gradient
1207 cache. One instance of the object is created for each group when necessary.
1208 The shared resource instance should have a constructor that takes a
1209 QOpenGLContext *. To get an instance for a given context one calls
1210 T *QOpenGLMultiGroupSharedResource::value<T>(context), where T is a sub-class
1211 of QOpenGLSharedResource.
1212 \inmodule QtGui
1213
1214 You should not call free() on QOpenGLSharedResources owned by a
1215 QOpenGLMultiGroupSharedResource instance.
1216*/
1217QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource()
1218 : active(0)
1219{
1220#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1221 qDebug("Creating context group resource object %p.", this);
1222#endif
1223}
1224
1225QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource()
1226{
1227#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1228 qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size());
1229#endif
1230 for (int i = 0; i < m_groups.size(); ++i) {
1231 if (!m_groups.at(i)->shares().isEmpty()) {
1232 QOpenGLContext *context = m_groups.at(i)->shares().constFirst();
1233 QOpenGLSharedResource *resource = value(context);
1234 if (resource)
1235 resource->free();
1236 }
1237 m_groups.at(i)->d_func()->m_resources.remove(this);
1238 active.deref();
1239 }
1240#ifndef QT_NO_DEBUG
1241 if (active.loadRelaxed() != 0) {
1242 qWarning("QtGui: Resources are still available at program shutdown.\n"
1243 " This is possibly caused by a leaked QOpenGLWidget, \n"
1244 " QOpenGLFramebufferObject or QOpenGLPixelBuffer.");
1245 }
1246#endif
1247}
1248
1249void QOpenGLMultiGroupSharedResource::insert(QOpenGLContext *context, QOpenGLSharedResource *value)
1250{
1251#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1252 qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
1253#endif
1254 QOpenGLContextGroup *group = context->shareGroup();
1255 Q_ASSERT(!group->d_func()->m_resources.contains(this));
1256 group->d_func()->m_resources.insert(this, value);
1257 m_groups.append(group);
1258 active.ref();
1259}
1260
1261QOpenGLSharedResource *QOpenGLMultiGroupSharedResource::value(QOpenGLContext *context)
1262{
1263 QOpenGLContextGroup *group = context->shareGroup();
1264 return group->d_func()->m_resources.value(this, nullptr);
1265}
1266
1267QList<QOpenGLSharedResource *> QOpenGLMultiGroupSharedResource::resources() const
1268{
1269 QList<QOpenGLSharedResource *> result;
1270 for (QList<QOpenGLContextGroup *>::const_iterator it = m_groups.constBegin(); it != m_groups.constEnd(); ++it) {
1271 QOpenGLSharedResource *resource = (*it)->d_func()->m_resources.value(const_cast<QOpenGLMultiGroupSharedResource *>(this), nullptr);
1272 if (resource)
1273 result << resource;
1274 }
1275 return result;
1276}
1277
1278void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenGLSharedResource *value)
1279{
1280#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1281 qDebug("Cleaning up context group resource %p, for group %p in thread %p.", this, group, QThread::currentThread());
1282#endif
1283 value->invalidateResource();
1284 value->free();
1285 active.deref();
1286
1287 Q_ASSERT(m_groups.contains(group));
1288 m_groups.removeOne(group);
1289}
1290
1291QOpenGLContextVersionFunctionHelper::~QOpenGLContextVersionFunctionHelper()
1292 = default;
1293
1294#ifndef QT_NO_DEBUG_STREAM
1295QDebug operator<<(QDebug debug, const QOpenGLContext *ctx)
1296{
1297 QDebugStateSaver saver(debug);
1298 debug.nospace();
1299 debug.noquote();
1300 debug << "QOpenGLContext(";
1301 if (ctx) {
1302 debug << static_cast<const void *>(ctx);
1303 if (ctx->isValid()) {
1304 debug << ", format=" << ctx->format();
1305 if (const QSurface *sf = ctx->surface())
1306 debug << ", surface=" << sf;
1307 if (const QScreen *s = ctx->screen())
1308 debug << ", screen=\"" << s->name() << '"';
1309 } else {
1310 debug << ", invalid";
1311 }
1312 } else {
1313 debug << '0';
1314 }
1315 debug << ')';
1316 return debug;
1317}
1318
1319QDebug operator<<(QDebug debug, const QOpenGLContextGroup *cg)
1320{
1321 QDebugStateSaver saver(debug);
1322 debug.nospace();
1323 debug << "QOpenGLContextGroup(";
1324 if (cg)
1325 debug << cg->shares();
1326 else
1327 debug << '0';
1328 debug << ')';
1329 return debug;
1330}
1331#endif // QT_NO_DEBUG_STREAM
1332
1333using namespace QNativeInterface;
1334
1335void *QOpenGLContext::resolveInterface(const char *name, int revision) const
1336{
1337 Q_UNUSED(name); Q_UNUSED(revision);
1338
1339 auto *platformContext = handle();
1340 Q_UNUSED(platformContext);
1341
1342#if defined(Q_OS_MACOS)
1343 QT_NATIVE_INTERFACE_RETURN_IF(QCocoaGLContext, platformContext);
1344#endif
1345#if defined(Q_OS_WIN)
1346 QT_NATIVE_INTERFACE_RETURN_IF(QWGLContext, platformContext);
1347#endif
1348#if QT_CONFIG(xcb_glx_plugin)
1349 QT_NATIVE_INTERFACE_RETURN_IF(QGLXContext, platformContext);
1350#endif
1351#if QT_CONFIG(egl)
1352 QT_NATIVE_INTERFACE_RETURN_IF(QEGLContext, platformContext);
1353#endif
1354
1355 return nullptr;
1356}
1357
1358QT_END_NAMESPACE
1359
1360#include "moc_qopenglcontext.cpp"
QOpenGLContext * context
Combined button and popup list for selecting options.
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
QOpenGLContext * qt_gl_global_share_context()
void qt_gl_set_global_share_context(QOpenGLContext *context)
static QOpenGLContext * global_share_context