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