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 \warning Do not attempt to make the context returned by this function
963 current on any surface. Instead, you can create a new context which shares
964 with the global one, and then make the new context current.
965
966 \sa Qt::AA_ShareOpenGLContexts, setShareContext(), makeCurrent()
967*/
968QOpenGLContext *QOpenGLContext::globalShareContext()
969{
970 Q_ASSERT(qGuiApp);
971
972 static QMutex mutex;
973 QMutexLocker locker(&mutex);
974
975 // Lazily create a global share context when enabled unless there is already one
976 if (!qt_gl_global_share_context() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
977 QOpenGLContext *ctx = new QOpenGLContext;
978 ctx->setFormat(QSurfaceFormat::defaultFormat());
979 ctx->create();
980 ctx->moveToThread(qGuiApp->thread());
981 qt_gl_set_global_share_context(ctx);
982 QGuiApplicationPrivate::instance()->ownGlobalShareContext = true;
983 }
984 return qt_gl_global_share_context();
985}
986
987/*!
988 \internal
989*/
990QOpenGLTextureHelper* QOpenGLContext::textureFunctions() const
991{
992 Q_D(const QOpenGLContext);
993 return d->textureFunctions;
994}
995
996/*!
997 \internal
998*/
999void QOpenGLContext::setTextureFunctions(QOpenGLTextureHelper* textureFuncs, std::function<void()> destroyCallback)
1000{
1001 Q_D(QOpenGLContext);
1002 d->textureFunctions = textureFuncs;
1003 d->textureFunctionsDestroyCallback = destroyCallback;
1004}
1005
1006/*!
1007 \class QOpenGLContextGroup
1008 \since 5.0
1009 \brief The QOpenGLContextGroup class represents a group of contexts sharing
1010 OpenGL resources.
1011 \inmodule QtGui
1012
1013 QOpenGLContextGroup is automatically created and managed by QOpenGLContext
1014 instances. Its purpose is to identify all the contexts that are sharing
1015 resources.
1016
1017 \sa QOpenGLContext::shareGroup()
1018*/
1019QOpenGLContextGroup::QOpenGLContextGroup()
1020 : QObject(*new QOpenGLContextGroupPrivate())
1021{
1022}
1023
1024/*!
1025 \internal
1026*/
1027QOpenGLContextGroup::~QOpenGLContextGroup()
1028{
1029 Q_D(QOpenGLContextGroup);
1030 d->cleanup();
1031}
1032
1033/*!
1034 Returns all the QOpenGLContext objects in this share group.
1035*/
1036QList<QOpenGLContext *> QOpenGLContextGroup::shares() const
1037{
1038 Q_D(const QOpenGLContextGroup);
1039 return d->m_shares;
1040}
1041
1042/*!
1043 Returns the QOpenGLContextGroup corresponding to the current context.
1044
1045 \sa QOpenGLContext::currentContext()
1046*/
1047QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup()
1048{
1049 QOpenGLContext *current = QOpenGLContext::currentContext();
1050 return current ? current->shareGroup() : nullptr;
1051}
1052
1053QOpenGLContextGroupPrivate::~QOpenGLContextGroupPrivate()
1054 = default;
1055
1056void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx)
1057{
1058 const auto locker = qt_scoped_lock(m_mutex);
1059 m_refs.ref();
1060 m_shares << ctx;
1061}
1062
1063void QOpenGLContextGroupPrivate::removeContext(QOpenGLContext *ctx)
1064{
1065 Q_Q(QOpenGLContextGroup);
1066
1067 bool deleteObject = false;
1068
1069 {
1070 const auto locker = qt_scoped_lock(m_mutex);
1071 m_shares.removeOne(ctx);
1072
1073 if (ctx == m_context && !m_shares.isEmpty())
1074 m_context = m_shares.constFirst();
1075
1076 if (!m_refs.deref()) {
1077 cleanup();
1078 deleteObject = true;
1079 }
1080 }
1081
1082 if (deleteObject) {
1083 if (q->thread() == QThread::currentThread())
1084 delete q; // Delete directly to prevent leak, refer to QTBUG-29056
1085 else
1086 q->deleteLater();
1087 }
1088}
1089
1090void QOpenGLContextGroupPrivate::cleanup()
1091{
1092 Q_Q(QOpenGLContextGroup);
1093 {
1094 QHash<QOpenGLMultiGroupSharedResource *, QOpenGLSharedResource *>::const_iterator it, end;
1095 end = m_resources.constEnd();
1096 for (it = m_resources.constBegin(); it != end; ++it)
1097 it.key()->cleanup(q, it.value());
1098 m_resources.clear();
1099 }
1100
1101 QList<QOpenGLSharedResource *>::iterator it = m_sharedResources.begin();
1102 QList<QOpenGLSharedResource *>::iterator end = m_sharedResources.end();
1103
1104 while (it != end) {
1105 (*it)->invalidateResource();
1106 (*it)->m_group = nullptr;
1107 ++it;
1108 }
1109
1110 m_sharedResources.clear();
1111
1112 qDeleteAll(m_pendingDeletion.begin(), m_pendingDeletion.end());
1113 m_pendingDeletion.clear();
1114}
1115
1116void QOpenGLContextGroupPrivate::deletePendingResources(QOpenGLContext *ctx)
1117{
1118 const auto locker = qt_scoped_lock(m_mutex);
1119
1120 const QList<QOpenGLSharedResource *> pending = m_pendingDeletion;
1121 m_pendingDeletion.clear();
1122
1123 QList<QOpenGLSharedResource *>::const_iterator it = pending.begin();
1124 QList<QOpenGLSharedResource *>::const_iterator end = pending.end();
1125 while (it != end) {
1126 (*it)->freeResource(ctx);
1127 delete *it;
1128 ++it;
1129 }
1130}
1131
1132/*!
1133 \class QOpenGLSharedResource
1134 \internal
1135 \since 5.0
1136 \brief The QOpenGLSharedResource class is used to keep track of resources
1137 that are shared between OpenGL contexts (like textures, framebuffer
1138 objects, shader programs, etc), and clean them up in a safe way when
1139 they're no longer needed.
1140 \inmodule QtGui
1141
1142 The QOpenGLSharedResource instance should never be deleted, instead free()
1143 should be called when it's no longer needed. Thus it will be put on a queue
1144 and freed at an appropriate time (when a context in the share group becomes
1145 current).
1146
1147 The sub-class needs to implement two pure virtual functions. The first,
1148 freeResource() must be implemented to actually do the freeing, for example
1149 call glDeleteTextures() on a texture id. Qt makes sure a valid context in
1150 the resource's share group is current at the time. The other,
1151 invalidateResource(), is called by Qt in the circumstance when the last
1152 context in the share group is destroyed before free() has been called. The
1153 implementation of invalidateResource() should set any identifiers to 0 or
1154 set a flag to prevent them from being used later on.
1155*/
1156QOpenGLSharedResource::QOpenGLSharedResource(QOpenGLContextGroup *group)
1157 : m_group(group)
1158{
1159 const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex);
1160 m_group->d_func()->m_sharedResources << this;
1161}
1162
1163QOpenGLSharedResource::~QOpenGLSharedResource()
1164{
1165}
1166
1167// schedule the resource for deletion at an appropriate time
1168void QOpenGLSharedResource::free()
1169{
1170 if (!m_group) {
1171 delete this;
1172 return;
1173 }
1174
1175 const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex);
1176 m_group->d_func()->m_sharedResources.removeOne(this);
1177 m_group->d_func()->m_pendingDeletion << this;
1178
1179 // can we delete right away?
1180 QOpenGLContext *current = QOpenGLContext::currentContext();
1181 if (current && current->shareGroup() == m_group) {
1182 m_group->d_func()->deletePendingResources(current);
1183 }
1184}
1185
1186/*!
1187 \class QOpenGLSharedResourceGuard
1188 \internal
1189 \since 5.0
1190 \brief The QOpenGLSharedResourceGuard class is a convenience sub-class of
1191 QOpenGLSharedResource to be used to track a single OpenGL object with a
1192 GLuint identifier. The constructor takes a function pointer to a function
1193 that will be used to free the resource if and when necessary.
1194 \inmodule QtGui
1195
1196*/
1197
1198QOpenGLSharedResourceGuard::~QOpenGLSharedResourceGuard()
1199 = default;
1200
1201void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context)
1202{
1203 if (m_id) {
1204 QOpenGLFunctions functions(context);
1205 m_func(&functions, m_id);
1206 m_id = 0;
1207 }
1208}
1209
1210/*!
1211 \class QOpenGLMultiGroupSharedResource
1212 \internal
1213 \since 5.0
1214 \brief The QOpenGLMultiGroupSharedResource keeps track of a shared resource
1215 that might be needed from multiple contexts, like a glyph cache or gradient
1216 cache. One instance of the object is created for each group when necessary.
1217 The shared resource instance should have a constructor that takes a
1218 QOpenGLContext *. To get an instance for a given context one calls
1219 T *QOpenGLMultiGroupSharedResource::value<T>(context), where T is a sub-class
1220 of QOpenGLSharedResource.
1221 \inmodule QtGui
1222
1223 You should not call free() on QOpenGLSharedResources owned by a
1224 QOpenGLMultiGroupSharedResource instance.
1225*/
1226QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource()
1227 : active(0)
1228{
1229#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1230 qDebug("Creating context group resource object %p.", this);
1231#endif
1232}
1233
1234QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource()
1235{
1236#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1237 qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size());
1238#endif
1239 for (int i = 0; i < m_groups.size(); ++i) {
1240 if (!m_groups.at(i)->shares().isEmpty()) {
1241 QOpenGLContext *context = m_groups.at(i)->shares().constFirst();
1242 QOpenGLSharedResource *resource = value(context);
1243 if (resource)
1244 resource->free();
1245 }
1246 m_groups.at(i)->d_func()->m_resources.remove(this);
1247 active.deref();
1248 }
1249#ifndef QT_NO_DEBUG
1250 if (active.loadRelaxed() != 0) {
1251 qWarning("QtGui: Resources are still available at program shutdown.\n"
1252 " This is possibly caused by a leaked QOpenGLWidget, \n"
1253 " QOpenGLFramebufferObject or QOpenGLPixelBuffer.");
1254 }
1255#endif
1256}
1257
1258void QOpenGLMultiGroupSharedResource::insert(QOpenGLContext *context, QOpenGLSharedResource *value)
1259{
1260#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1261 qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
1262#endif
1263 QOpenGLContextGroup *group = context->shareGroup();
1264 Q_ASSERT(!group->d_func()->m_resources.contains(this));
1265 group->d_func()->m_resources.insert(this, value);
1266 m_groups.append(group);
1267 active.ref();
1268}
1269
1270QOpenGLSharedResource *QOpenGLMultiGroupSharedResource::value(QOpenGLContext *context)
1271{
1272 QOpenGLContextGroup *group = context->shareGroup();
1273 return group->d_func()->m_resources.value(this, nullptr);
1274}
1275
1276QList<QOpenGLSharedResource *> QOpenGLMultiGroupSharedResource::resources() const
1277{
1278 QList<QOpenGLSharedResource *> result;
1279 for (QList<QOpenGLContextGroup *>::const_iterator it = m_groups.constBegin(); it != m_groups.constEnd(); ++it) {
1280 QOpenGLSharedResource *resource = (*it)->d_func()->m_resources.value(const_cast<QOpenGLMultiGroupSharedResource *>(this), nullptr);
1281 if (resource)
1282 result << resource;
1283 }
1284 return result;
1285}
1286
1287void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenGLSharedResource *value)
1288{
1289#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1290 qDebug("Cleaning up context group resource %p, for group %p in thread %p.", this, group, QThread::currentThread());
1291#endif
1292 value->invalidateResource();
1293 value->free();
1294 active.deref();
1295
1296 Q_ASSERT(m_groups.contains(group));
1297 m_groups.removeOne(group);
1298}
1299
1300QOpenGLContextVersionFunctionHelper::~QOpenGLContextVersionFunctionHelper()
1301 = default;
1302
1303#ifndef QT_NO_DEBUG_STREAM
1304QDebug operator<<(QDebug debug, const QOpenGLContext *ctx)
1305{
1306 QDebugStateSaver saver(debug);
1307 debug.nospace();
1308 debug.noquote();
1309 debug << "QOpenGLContext(";
1310 if (ctx) {
1311 debug << static_cast<const void *>(ctx);
1312 if (ctx->isValid()) {
1313 debug << ", format=" << ctx->format();
1314 if (const QSurface *sf = ctx->surface())
1315 debug << ", surface=" << sf;
1316 if (const QScreen *s = ctx->screen())
1317 debug << ", screen=\"" << s->name() << '"';
1318 } else {
1319 debug << ", invalid";
1320 }
1321 } else {
1322 debug << "0x0";
1323 }
1324 debug << ')';
1325 return debug;
1326}
1327
1328QDebug operator<<(QDebug debug, const QOpenGLContextGroup *cg)
1329{
1330 QDebugStateSaver saver(debug);
1331 debug.nospace();
1332 debug << "QOpenGLContextGroup(";
1333 if (cg)
1334 debug << cg->shares();
1335 else
1336 debug << "0x0";
1337 debug << ')';
1338 return debug;
1339}
1340#endif // QT_NO_DEBUG_STREAM
1341
1342using namespace QNativeInterface;
1343
1344void *QOpenGLContext::resolveInterface(const char *name, int revision) const
1345{
1346 Q_UNUSED(name); Q_UNUSED(revision);
1347
1348 auto *platformContext = handle();
1349 Q_UNUSED(platformContext);
1350
1351#if defined(Q_OS_MACOS)
1352 QT_NATIVE_INTERFACE_RETURN_IF(QCocoaGLContext, platformContext);
1353#endif
1354#if defined(Q_OS_WIN)
1355 QT_NATIVE_INTERFACE_RETURN_IF(QWGLContext, platformContext);
1356#endif
1357#if QT_CONFIG(xcb_glx_plugin)
1358 QT_NATIVE_INTERFACE_RETURN_IF(QGLXContext, platformContext);
1359#endif
1360#if QT_CONFIG(egl)
1361 QT_NATIVE_INTERFACE_RETURN_IF(QEGLContext, platformContext);
1362#endif
1363
1364 return nullptr;
1365}
1366
1367QT_END_NAMESPACE
1368
1369#include "moc_qopenglcontext.cpp"
QOpenGLContext * context
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