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