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 Returns \nullptr if no such function can be found.
811*/
812QFunctionPointer QOpenGLContext::getProcAddress(const QByteArray &procName) const
813{
814 return getProcAddress(procName.constData());
815}
816
817/*!
818 \overload
819 \since 5.8
820 */
821QFunctionPointer QOpenGLContext::getProcAddress(const char *procName) const
822{
823 Q_D(const QOpenGLContext);
824 if (!d->platformGLContext)
825 return nullptr;
826 return d->platformGLContext->getProcAddress(procName);
827}
828
829/*!
830 Returns the format of the underlying platform context, if create() has been called.
831
832 Otherwise, returns the requested format.
833
834 The requested and the actual format may differ. Requesting a given OpenGL version does
835 not mean the resulting context will target exactly the requested version. It is only
836 guaranteed that the version/profile/options combination for the created context is
837 compatible with the request, as long as the driver is able to provide such a context.
838
839 For example, requesting an OpenGL version 3.x core profile context may result in an
840 OpenGL 4.x core profile context. Similarly, a request for OpenGL 2.1 may result in an
841 OpenGL 3.0 context with deprecated functions enabled. Finally, depending on the
842 driver, unsupported versions may result in either a context creation failure or in a
843 context for the highest supported version.
844
845 Similar differences are possible in the buffer sizes, for example, the resulting
846 context may have a larger depth buffer than requested. This is perfectly normal.
847*/
848QSurfaceFormat QOpenGLContext::format() const
849{
850 Q_D(const QOpenGLContext);
851 if (!d->platformGLContext)
852 return d->requestedFormat;
853 return d->platformGLContext->format();
854}
855
856/*!
857 Returns the share group this context belongs to.
858*/
859QOpenGLContextGroup *QOpenGLContext::shareGroup() const
860{
861 Q_D(const QOpenGLContext);
862 return d->shareGroup;
863}
864
865/*!
866 Returns the share context this context was created with.
867
868 If the underlying platform was not able to support the requested
869 sharing, this will return 0.
870*/
871QOpenGLContext *QOpenGLContext::shareContext() const
872{
873 Q_D(const QOpenGLContext);
874 return d->shareContext;
875}
876
877/*!
878 Returns the screen the context was created for.
879*/
880QScreen *QOpenGLContext::screen() const
881{
882 Q_D(const QOpenGLContext);
883 return d->screen;
884}
885
886/*!
887 \enum QOpenGLContext::OpenGLModuleType
888 This enum defines the type of the underlying OpenGL implementation.
889
890 \value LibGL OpenGL
891 \value LibGLES OpenGL ES 2.0 or higher
892
893 \since 5.3
894*/
895
896/*!
897 Returns the underlying OpenGL implementation type.
898
899 On platforms where the OpenGL implementation is not dynamically
900 loaded, the return value is determined during compile time and never
901 changes.
902
903 \note A desktop OpenGL implementation may be capable of creating
904 ES-compatible contexts too. Therefore in most cases it is more
905 appropriate to check QSurfaceFormat::renderableType() or use
906 the convenience function isOpenGLES().
907
908 \note This function requires that the QGuiApplication instance is already created.
909
910 \since 5.3
911 */
912QOpenGLContext::OpenGLModuleType QOpenGLContext::openGLModuleType()
913{
914#if defined(QT_OPENGL_DYNAMIC)
915 Q_ASSERT(qGuiApp);
916 return QGuiApplicationPrivate::instance()->platformIntegration()->openGLModuleType();
917#elif QT_CONFIG(opengles2)
918 return LibGLES;
919#else
920 return LibGL;
921#endif
922}
923
924/*!
925 Returns true if the context is an OpenGL ES context.
926
927 If the context has not yet been created, the result is based on the
928 requested format set via setFormat().
929
930 \sa create(), format(), setFormat()
931
932 \since 5.3
933 */
934bool QOpenGLContext::isOpenGLES() const
935{
936 return format().renderableType() == QSurfaceFormat::OpenGLES;
937}
938
939/*!
940 Returns \c true if the platform supports OpenGL rendering outside the main (gui)
941 thread.
942
943 The value is controlled by the platform plugin in use and may also depend on the
944 graphics drivers.
945
946 \since 5.5
947 */
948bool QOpenGLContext::supportsThreadedOpenGL()
949{
950 Q_ASSERT(qGuiApp);
951 return QGuiApplicationPrivate::instance()->platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
952}
953
954/*!
955 \since 5.5
956
957 Returns the application-wide shared OpenGL context, if present.
958 Otherwise, returns \nullptr.
959
960 This is useful if you need to upload OpenGL objects (buffers, textures,
961 etc.) before creating or showing a QOpenGLWidget or QQuickWidget.
962
963 \warning Do not attempt to make the context returned by this function
964 current on any surface. Instead, you can create a new context which shares
965 with the global one, and then make the new context current.
966
967 \sa Qt::AA_ShareOpenGLContexts, setShareContext(), makeCurrent()
968*/
969QOpenGLContext *QOpenGLContext::globalShareContext()
970{
971 Q_ASSERT(qGuiApp);
972
973 static QMutex mutex;
974 QMutexLocker locker(&mutex);
975
976 // Lazily create a global share context when enabled unless there is already one
977 if (!qt_gl_global_share_context() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
978 QOpenGLContext *ctx = new QOpenGLContext;
979 ctx->setFormat(QSurfaceFormat::defaultFormat());
980 ctx->create();
981 ctx->moveToThread(qGuiApp->thread());
982 qt_gl_set_global_share_context(ctx);
983 QGuiApplicationPrivate::instance()->ownGlobalShareContext = true;
984 }
985 return qt_gl_global_share_context();
986}
987
988/*!
989 \internal
990*/
991QOpenGLTextureHelper* QOpenGLContext::textureFunctions() const
992{
993 Q_D(const QOpenGLContext);
994 return d->textureFunctions;
995}
996
997/*!
998 \internal
999*/
1000void QOpenGLContext::setTextureFunctions(QOpenGLTextureHelper* textureFuncs, std::function<void()> destroyCallback)
1001{
1002 Q_D(QOpenGLContext);
1003 d->textureFunctions = textureFuncs;
1004 d->textureFunctionsDestroyCallback = destroyCallback;
1005}
1006
1007/*!
1008 \class QOpenGLContextGroup
1009 \since 5.0
1010 \brief The QOpenGLContextGroup class represents a group of contexts sharing
1011 OpenGL resources.
1012 \inmodule QtGui
1013
1014 QOpenGLContextGroup is automatically created and managed by QOpenGLContext
1015 instances. Its purpose is to identify all the contexts that are sharing
1016 resources.
1017
1018 \sa QOpenGLContext::shareGroup()
1019*/
1020QOpenGLContextGroup::QOpenGLContextGroup()
1021 : QObject(*new QOpenGLContextGroupPrivate())
1022{
1023}
1024
1025/*!
1026 \internal
1027*/
1028QOpenGLContextGroup::~QOpenGLContextGroup()
1029{
1030 Q_D(QOpenGLContextGroup);
1031 d->cleanup();
1032}
1033
1034/*!
1035 Returns all the QOpenGLContext objects in this share group.
1036*/
1037QList<QOpenGLContext *> QOpenGLContextGroup::shares() const
1038{
1039 Q_D(const QOpenGLContextGroup);
1040 return d->m_shares;
1041}
1042
1043/*!
1044 Returns the QOpenGLContextGroup corresponding to the current context.
1045
1046 \sa QOpenGLContext::currentContext()
1047*/
1048QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup()
1049{
1050 QOpenGLContext *current = QOpenGLContext::currentContext();
1051 return current ? current->shareGroup() : nullptr;
1052}
1053
1054QOpenGLContextGroupPrivate::~QOpenGLContextGroupPrivate()
1055 = default;
1056
1057void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx)
1058{
1059 const auto locker = qt_scoped_lock(m_mutex);
1060 m_refs.ref();
1061 m_shares << ctx;
1062}
1063
1064void QOpenGLContextGroupPrivate::removeContext(QOpenGLContext *ctx)
1065{
1066 Q_Q(QOpenGLContextGroup);
1067
1068 bool deleteObject = false;
1069
1070 {
1071 const auto locker = qt_scoped_lock(m_mutex);
1072 m_shares.removeOne(ctx);
1073
1074 if (ctx == m_context && !m_shares.isEmpty())
1075 m_context = m_shares.constFirst();
1076
1077 if (!m_refs.deref()) {
1078 cleanup();
1079 deleteObject = true;
1080 }
1081 }
1082
1083 if (deleteObject) {
1084 if (q->thread() == QThread::currentThread())
1085 delete q; // Delete directly to prevent leak, refer to QTBUG-29056
1086 else
1087 q->deleteLater();
1088 }
1089}
1090
1091void QOpenGLContextGroupPrivate::cleanup()
1092{
1093 Q_Q(QOpenGLContextGroup);
1094 {
1095 QHash<QOpenGLMultiGroupSharedResource *, QOpenGLSharedResource *>::const_iterator it, end;
1096 end = m_resources.constEnd();
1097 for (it = m_resources.constBegin(); it != end; ++it)
1098 it.key()->cleanup(q, it.value());
1099 m_resources.clear();
1100 }
1101
1102 QList<QOpenGLSharedResource *>::iterator it = m_sharedResources.begin();
1103 QList<QOpenGLSharedResource *>::iterator end = m_sharedResources.end();
1104
1105 while (it != end) {
1106 (*it)->invalidateResource();
1107 (*it)->m_group = nullptr;
1108 ++it;
1109 }
1110
1111 m_sharedResources.clear();
1112
1113 qDeleteAll(m_pendingDeletion.begin(), m_pendingDeletion.end());
1114 m_pendingDeletion.clear();
1115}
1116
1117void QOpenGLContextGroupPrivate::deletePendingResources(QOpenGLContext *ctx)
1118{
1119 const auto locker = qt_scoped_lock(m_mutex);
1120
1121 const QList<QOpenGLSharedResource *> pending = m_pendingDeletion;
1122 m_pendingDeletion.clear();
1123
1124 QList<QOpenGLSharedResource *>::const_iterator it = pending.begin();
1125 QList<QOpenGLSharedResource *>::const_iterator end = pending.end();
1126 while (it != end) {
1127 (*it)->freeResource(ctx);
1128 delete *it;
1129 ++it;
1130 }
1131}
1132
1133/*!
1134 \class QOpenGLSharedResource
1135 \internal
1136 \since 5.0
1137 \brief The QOpenGLSharedResource class is used to keep track of resources
1138 that are shared between OpenGL contexts (like textures, framebuffer
1139 objects, shader programs, etc), and clean them up in a safe way when
1140 they're no longer needed.
1141 \inmodule QtGui
1142
1143 The QOpenGLSharedResource instance should never be deleted, instead free()
1144 should be called when it's no longer needed. Thus it will be put on a queue
1145 and freed at an appropriate time (when a context in the share group becomes
1146 current).
1147
1148 The sub-class needs to implement two pure virtual functions. The first,
1149 freeResource() must be implemented to actually do the freeing, for example
1150 call glDeleteTextures() on a texture id. Qt makes sure a valid context in
1151 the resource's share group is current at the time. The other,
1152 invalidateResource(), is called by Qt in the circumstance when the last
1153 context in the share group is destroyed before free() has been called. The
1154 implementation of invalidateResource() should set any identifiers to 0 or
1155 set a flag to prevent them from being used later on.
1156*/
1157QOpenGLSharedResource::QOpenGLSharedResource(QOpenGLContextGroup *group)
1158 : m_group(group)
1159{
1160 const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex);
1161 m_group->d_func()->m_sharedResources << this;
1162}
1163
1164QOpenGLSharedResource::~QOpenGLSharedResource()
1165{
1166}
1167
1168// schedule the resource for deletion at an appropriate time
1169void QOpenGLSharedResource::free()
1170{
1171 if (!m_group) {
1172 delete this;
1173 return;
1174 }
1175
1176 const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex);
1177 m_group->d_func()->m_sharedResources.removeOne(this);
1178 m_group->d_func()->m_pendingDeletion << this;
1179
1180 // can we delete right away?
1181 QOpenGLContext *current = QOpenGLContext::currentContext();
1182 if (current && current->shareGroup() == m_group) {
1183 m_group->d_func()->deletePendingResources(current);
1184 }
1185}
1186
1187/*!
1188 \class QOpenGLSharedResourceGuard
1189 \internal
1190 \since 5.0
1191 \brief The QOpenGLSharedResourceGuard class is a convenience sub-class of
1192 QOpenGLSharedResource to be used to track a single OpenGL object with a
1193 GLuint identifier. The constructor takes a function pointer to a function
1194 that will be used to free the resource if and when necessary.
1195 \inmodule QtGui
1196
1197*/
1198
1199QOpenGLSharedResourceGuard::~QOpenGLSharedResourceGuard()
1200 = default;
1201
1202void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context)
1203{
1204 if (m_id) {
1205 QOpenGLFunctions functions(context);
1206 m_func(&functions, m_id);
1207 m_id = 0;
1208 }
1209}
1210
1211/*!
1212 \class QOpenGLMultiGroupSharedResource
1213 \internal
1214 \since 5.0
1215 \brief The QOpenGLMultiGroupSharedResource keeps track of a shared resource
1216 that might be needed from multiple contexts, like a glyph cache or gradient
1217 cache. One instance of the object is created for each group when necessary.
1218 The shared resource instance should have a constructor that takes a
1219 QOpenGLContext *. To get an instance for a given context one calls
1220 T *QOpenGLMultiGroupSharedResource::value<T>(context), where T is a sub-class
1221 of QOpenGLSharedResource.
1222 \inmodule QtGui
1223
1224 You should not call free() on QOpenGLSharedResources owned by a
1225 QOpenGLMultiGroupSharedResource instance.
1226*/
1227QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource()
1228 : active(0)
1229{
1230#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1231 qDebug("Creating context group resource object %p.", this);
1232#endif
1233}
1234
1235QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource()
1236{
1237#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1238 qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size());
1239#endif
1240 for (int i = 0; i < m_groups.size(); ++i) {
1241 if (!m_groups.at(i)->shares().isEmpty()) {
1242 QOpenGLContext *context = m_groups.at(i)->shares().constFirst();
1243 QOpenGLSharedResource *resource = value(context);
1244 if (resource)
1245 resource->free();
1246 }
1247 m_groups.at(i)->d_func()->m_resources.remove(this);
1248 active.deref();
1249 }
1250#ifndef QT_NO_DEBUG
1251 if (active.loadRelaxed() != 0) {
1252 qWarning("QtGui: Resources are still available at program shutdown.\n"
1253 " This is possibly caused by a leaked QOpenGLWidget, \n"
1254 " QOpenGLFramebufferObject or QOpenGLPixelBuffer.");
1255 }
1256#endif
1257}
1258
1259void QOpenGLMultiGroupSharedResource::insert(QOpenGLContext *context, QOpenGLSharedResource *value)
1260{
1261#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1262 qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
1263#endif
1264 QOpenGLContextGroup *group = context->shareGroup();
1265 Q_ASSERT(!group->d_func()->m_resources.contains(this));
1266 group->d_func()->m_resources.insert(this, value);
1267 m_groups.append(group);
1268 active.ref();
1269}
1270
1271QOpenGLSharedResource *QOpenGLMultiGroupSharedResource::value(QOpenGLContext *context)
1272{
1273 QOpenGLContextGroup *group = context->shareGroup();
1274 return group->d_func()->m_resources.value(this, nullptr);
1275}
1276
1277QList<QOpenGLSharedResource *> QOpenGLMultiGroupSharedResource::resources() const
1278{
1279 QList<QOpenGLSharedResource *> result;
1280 for (QList<QOpenGLContextGroup *>::const_iterator it = m_groups.constBegin(); it != m_groups.constEnd(); ++it) {
1281 QOpenGLSharedResource *resource = (*it)->d_func()->m_resources.value(const_cast<QOpenGLMultiGroupSharedResource *>(this), nullptr);
1282 if (resource)
1283 result << resource;
1284 }
1285 return result;
1286}
1287
1288void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenGLSharedResource *value)
1289{
1290#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
1291 qDebug("Cleaning up context group resource %p, for group %p in thread %p.", this, group, QThread::currentThread());
1292#endif
1293 value->invalidateResource();
1294 value->free();
1295 active.deref();
1296
1297 Q_ASSERT(m_groups.contains(group));
1298 m_groups.removeOne(group);
1299}
1300
1301QOpenGLContextVersionFunctionHelper::~QOpenGLContextVersionFunctionHelper()
1302 = default;
1303
1304#ifndef QT_NO_DEBUG_STREAM
1305QDebug operator<<(QDebug debug, const QOpenGLContext *ctx)
1306{
1307 QDebugStateSaver saver(debug);
1308 debug.nospace();
1309 debug.noquote();
1310 debug << "QOpenGLContext(";
1311 if (ctx) {
1312 debug << static_cast<const void *>(ctx);
1313 if (ctx->isValid()) {
1314 debug << ", format=" << ctx->format();
1315 if (const QSurface *sf = ctx->surface())
1316 debug << ", surface=" << sf;
1317 if (const QScreen *s = ctx->screen())
1318 debug << ", screen=\"" << s->name() << '"';
1319 } else {
1320 debug << ", invalid";
1321 }
1322 } else {
1323 debug << "0x0";
1324 }
1325 debug << ')';
1326 return debug;
1327}
1328
1329QDebug operator<<(QDebug debug, const QOpenGLContextGroup *cg)
1330{
1331 QDebugStateSaver saver(debug);
1332 debug.nospace();
1333 debug << "QOpenGLContextGroup(";
1334 if (cg)
1335 debug << cg->shares();
1336 else
1337 debug << "0x0";
1338 debug << ')';
1339 return debug;
1340}
1341#endif // QT_NO_DEBUG_STREAM
1342
1343using namespace QNativeInterface;
1344
1345void *QOpenGLContext::resolveInterface(const char *name, int revision) const
1346{
1347 Q_UNUSED(name); Q_UNUSED(revision);
1348
1349 auto *platformContext = handle();
1350 Q_UNUSED(platformContext);
1351
1352#if defined(Q_OS_MACOS)
1353 QT_NATIVE_INTERFACE_RETURN_IF(QCocoaGLContext, platformContext);
1354#endif
1355#if defined(Q_OS_WIN)
1356 QT_NATIVE_INTERFACE_RETURN_IF(QWGLContext, platformContext);
1357#endif
1358#if QT_CONFIG(xcb_glx_plugin)
1359 QT_NATIVE_INTERFACE_RETURN_IF(QGLXContext, platformContext);
1360#endif
1361#if QT_CONFIG(egl)
1362 QT_NATIVE_INTERFACE_RETURN_IF(QEGLContext, platformContext);
1363#endif
1364
1365 return nullptr;
1366}
1367
1368QT_END_NAMESPACE
1369
1370#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