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

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