| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <qpa/qplatformopenglcontext.h> |
| #include <qpa/qplatformintegration.h> |
| #include "qopenglcontext.h" |
| #include "qopenglcontext_p.h" |
| #include "qwindow.h" |
| |
| #include <QtCore/QThreadStorage> |
| #include <QtCore/QThread> |
| #include <QtCore/private/qlocking_p.h> |
| |
| #include <QtGui/private/qguiapplication_p.h> |
| #include <QtGui/private/qopengl_p.h> |
| #include <QtGui/private/qwindow_p.h> |
| #include <QtGui/QScreen> |
| #include <qpa/qplatformnativeinterface.h> |
| |
| #include <private/qopenglextensions_p.h> |
| #include <private/qopenglversionfunctionsfactory_p.h> |
| |
| #include <private/qopengltexturehelper_p.h> |
| |
| #include <QDebug> |
| |
| #ifndef QT_OPENGL_ES_2 |
| #include <QOpenGLFunctions_1_0> |
| #include <QOpenGLFunctions_3_2_Core> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| class QOpenGLVersionProfilePrivate |
| { |
| public: |
| QOpenGLVersionProfilePrivate() |
| : majorVersion(0), |
| minorVersion(0), |
| profile(QSurfaceFormat::NoProfile) |
| {} |
| |
| int majorVersion; |
| int minorVersion; |
| QSurfaceFormat::OpenGLContextProfile profile; |
| }; |
| |
| |
| /*! |
| \class QOpenGLVersionProfile |
| \inmodule QtGui |
| \since 5.1 |
| \brief The QOpenGLVersionProfile class represents the version and if applicable |
| the profile of an OpenGL context. |
| |
| An object of this class can be passed to QOpenGLContext::versionFunctions() to |
| request a functions object for a specific version and profile of OpenGL. |
| |
| It also contains some helper functions to check if a version supports profiles |
| or is a legacy version. |
| */ |
| |
| /*! |
| Creates a default invalid QOpenGLVersionProfile object. |
| */ |
| QOpenGLVersionProfile::QOpenGLVersionProfile() |
| : d(new QOpenGLVersionProfilePrivate) |
| { |
| } |
| |
| /*! |
| Creates a QOpenGLVersionProfile object initialised with the version and profile |
| from \a format. |
| */ |
| QOpenGLVersionProfile::QOpenGLVersionProfile(const QSurfaceFormat &format) |
| : d(new QOpenGLVersionProfilePrivate) |
| { |
| d->majorVersion = format.majorVersion(); |
| d->minorVersion = format.minorVersion(); |
| d->profile = format.profile(); |
| } |
| |
| /*! |
| Constructs a copy of \a other. |
| */ |
| QOpenGLVersionProfile::QOpenGLVersionProfile(const QOpenGLVersionProfile &other) |
| : d(new QOpenGLVersionProfilePrivate) |
| { |
| *d = *(other.d); |
| } |
| |
| /*! |
| Destroys the QOpenGLVersionProfile object. |
| */ |
| QOpenGLVersionProfile::~QOpenGLVersionProfile() |
| { |
| delete d; |
| } |
| |
| /*! |
| Assigns the version and profile of \a rhs to this QOpenGLVersionProfile object. |
| */ |
| QOpenGLVersionProfile &QOpenGLVersionProfile::operator=(const QOpenGLVersionProfile &rhs) |
| { |
| if (this == &rhs) |
| return *this; |
| *d = *(rhs.d); |
| return *this; |
| } |
| |
| /*! |
| Returns a QPair<int,int> where the components represent the major and minor OpenGL |
| version numbers respectively. |
| |
| \sa setVersion() |
| */ |
| QPair<int, int> QOpenGLVersionProfile::version() const |
| { |
| return qMakePair( d->majorVersion, d->minorVersion); |
| } |
| |
| /*! |
| Sets the major and minor version numbers to \a majorVersion and \a minorVersion respectively. |
| |
| \sa version() |
| */ |
| void QOpenGLVersionProfile::setVersion(int majorVersion, int minorVersion) |
| { |
| d->majorVersion = majorVersion; |
| d->minorVersion = minorVersion; |
| } |
| |
| /*! |
| Returns the OpenGL profile. Only makes sense if profiles are supported by this version. |
| |
| \sa setProfile() |
| */ |
| QSurfaceFormat::OpenGLContextProfile QOpenGLVersionProfile::profile() const |
| { |
| return d->profile; |
| } |
| |
| /*! |
| Sets the OpenGL profile \a profile. Only makes sense if profiles are supported by |
| this version. |
| |
| \sa profile() |
| */ |
| void QOpenGLVersionProfile::setProfile(QSurfaceFormat::OpenGLContextProfile profile) |
| { |
| d->profile = profile; |
| } |
| |
| /*! |
| Returns \c true if profiles are supported by the OpenGL version returned by version(). Only |
| OpenGL versions >= 3.2 support profiles. |
| |
| \sa profile(), version() |
| */ |
| bool QOpenGLVersionProfile::hasProfiles() const |
| { |
| return ( d->majorVersion > 3 |
| || (d->majorVersion == 3 && d->minorVersion > 1)); |
| } |
| |
| /*! |
| Returns \c true is the OpenGL version returned by version() contains deprecated functions |
| and does not support profiles i.e. if the OpenGL version is <= 3.1. |
| */ |
| bool QOpenGLVersionProfile::isLegacyVersion() const |
| { |
| return (d->majorVersion < 3 || (d->majorVersion == 3 && d->minorVersion == 0)); |
| } |
| |
| /*! |
| Returns \c true if the version number is valid. Note that for a default constructed |
| QOpenGLVersionProfile object this function will return \c false. |
| |
| \sa setVersion(), version() |
| */ |
| bool QOpenGLVersionProfile::isValid() const |
| { |
| return d->majorVersion > 0 && d->minorVersion >= 0; |
| } |
| |
| class QGuiGLThreadContext |
| { |
| public: |
| QGuiGLThreadContext() |
| : context(nullptr) |
| { |
| } |
| ~QGuiGLThreadContext() { |
| if (context) |
| context->doneCurrent(); |
| } |
| QOpenGLContext *context; |
| }; |
| |
| Q_GLOBAL_STATIC(QThreadStorage<QGuiGLThreadContext *>, qwindow_context_storage); |
| static QOpenGLContext *global_share_context = nullptr; |
| |
| #ifndef QT_NO_DEBUG |
| QHash<QOpenGLContext *, bool> QOpenGLContextPrivate::makeCurrentTracker; |
| QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex; |
| #endif |
| |
| /*! |
| \internal |
| |
| This function is used by Qt::AA_ShareOpenGLContexts and the Qt |
| WebEngine to set up context sharing across multiple windows. Do |
| not use it for any other purpose. |
| |
| Please maintain the binary compatibility of these functions. |
| */ |
| void qt_gl_set_global_share_context(QOpenGLContext *context) |
| { |
| global_share_context = context; |
| } |
| |
| /*! |
| \internal |
| */ |
| QOpenGLContext *qt_gl_global_share_context() |
| { |
| return global_share_context; |
| } |
| |
| /*! |
| \class QOpenGLContext |
| \inmodule QtGui |
| \since 5.0 |
| \brief The QOpenGLContext class represents a native OpenGL context, enabling |
| OpenGL rendering on a QSurface. |
| |
| QOpenGLContext represents the OpenGL state of an underlying OpenGL context. |
| To set up a context, set its screen and format such that they match those |
| of the surface or surfaces with which the context is meant to be used, if |
| necessary make it share resources with other contexts with |
| setShareContext(), and finally call create(). Use the return value or isValid() |
| to check if the context was successfully initialized. |
| |
| A context can be made current against a given surface by calling |
| makeCurrent(). When OpenGL rendering is done, call swapBuffers() to swap |
| the front and back buffers of the surface, so that the newly rendered |
| content becomes visible. To be able to support certain platforms, |
| QOpenGLContext requires that you call makeCurrent() again before starting |
| rendering a new frame, after calling swapBuffers(). |
| |
| If the context is temporarily not needed, such as when the application is |
| not rendering, it can be useful to delete it in order to free resources. |
| You can connect to the aboutToBeDestroyed() signal to clean up any |
| resources that have been allocated with different ownership from the |
| QOpenGLContext itself. |
| |
| Once a QOpenGLContext has been made current, you can render to it in a |
| platform independent way by using Qt's OpenGL enablers such as |
| QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, and |
| QOpenGLFramebufferObject. It is also possible to use the platform's OpenGL |
| API directly, without using the Qt enablers, although potentially at the |
| cost of portability. The latter is necessary when wanting to use OpenGL 1.x |
| or OpenGL ES 1.x. |
| |
| For more information about the OpenGL API, refer to the official |
| \l{http://www.opengl.org}{OpenGL documentation}. |
| |
| For an example of how to use QOpenGLContext see the |
| \l{OpenGL Window Example}{OpenGL Window} example. |
| |
| \section1 Thread Affinity |
| |
| QOpenGLContext can be moved to a different thread with moveToThread(). Do |
| not call makeCurrent() from a different thread than the one to which the |
| QOpenGLContext object belongs. A context can only be current in one thread |
| and against one surface at a time, and a thread only has one context |
| current at a time. |
| |
| \section1 Context Resource Sharing |
| |
| Resources such as textures and vertex buffer objects |
| can be shared between contexts. Use setShareContext() before calling |
| create() to specify that the contexts should share these resources. |
| QOpenGLContext internally keeps track of a QOpenGLContextGroup object which |
| can be accessed with shareGroup(), and which can be used to find all the |
| contexts in a given share group. A share group consists of all contexts that |
| have been successfully initialized and are sharing with an existing context in |
| the share group. A non-sharing context has a share group consisting of a |
| single context. |
| |
| \section1 Default Framebuffer |
| |
| On certain platforms, a framebuffer other than 0 might be the default frame |
| buffer depending on the current surface. Instead of calling |
| glBindFramebuffer(0), it is recommended that you use |
| glBindFramebuffer(ctx->defaultFramebufferObject()), to ensure that your |
| application is portable between different platforms. However, if you use |
| QOpenGLFunctions::glBindFramebuffer(), this is done automatically for you. |
| |
| \sa QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, QOpenGLFramebufferObject |
| */ |
| |
| /*! |
| \internal |
| |
| Set the current context. Returns the previously current context. |
| */ |
| QOpenGLContext *QOpenGLContextPrivate::setCurrentContext(QOpenGLContext *context) |
| { |
| QGuiGLThreadContext *threadContext = qwindow_context_storage()->localData(); |
| if (!threadContext) { |
| if (!QThread::currentThread()) { |
| qWarning("No QTLS available. currentContext won't work"); |
| return nullptr; |
| } |
| threadContext = new QGuiGLThreadContext; |
| qwindow_context_storage()->setLocalData(threadContext); |
| } |
| QOpenGLContext *previous = threadContext->context; |
| threadContext->context = context; |
| return previous; |
| } |
| |
| int QOpenGLContextPrivate::maxTextureSize() |
| { |
| if (max_texture_size != -1) |
| return max_texture_size; |
| |
| Q_Q(QOpenGLContext); |
| QOpenGLFunctions *funcs = q->functions(); |
| funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); |
| |
| #ifndef QT_OPENGL_ES |
| if (!q->isOpenGLES()) { |
| GLenum proxy = GL_PROXY_TEXTURE_2D; |
| |
| GLint size; |
| GLint next = 64; |
| funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| |
| QOpenGLFunctions_1_0 *gl1funcs = nullptr; |
| QOpenGLFunctions_3_2_Core *gl3funcs = nullptr; |
| |
| if (q->format().profile() == QSurfaceFormat::CoreProfile) |
| gl3funcs = q->versionFunctions<QOpenGLFunctions_3_2_Core>(); |
| else |
| gl1funcs = q->versionFunctions<QOpenGLFunctions_1_0>(); |
| |
| Q_ASSERT(gl1funcs || gl3funcs); |
| |
| if (gl1funcs) |
| gl1funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size); |
| else |
| gl3funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size); |
| |
| if (size == 0) { |
| return max_texture_size; |
| } |
| do { |
| size = next; |
| next = size * 2; |
| |
| if (next > max_texture_size) |
| break; |
| funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| if (gl1funcs) |
| gl1funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next); |
| else |
| gl3funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next); |
| |
| } while (next > size); |
| |
| max_texture_size = size; |
| } |
| #endif // QT_OPENGL_ES |
| |
| return max_texture_size; |
| } |
| |
| /*! |
| Returns the last context which called makeCurrent in the current thread, |
| or \nullptr, if no context is current. |
| */ |
| QOpenGLContext* QOpenGLContext::currentContext() |
| { |
| QGuiGLThreadContext *threadContext = qwindow_context_storage()->localData(); |
| if (threadContext) |
| return threadContext->context; |
| return nullptr; |
| } |
| |
| /*! |
| Returns \c true if the \a first and \a second contexts are sharing OpenGL resources. |
| */ |
| bool QOpenGLContext::areSharing(QOpenGLContext *first, QOpenGLContext *second) |
| { |
| return first->shareGroup() == second->shareGroup(); |
| } |
| |
| /*! |
| Returns the underlying platform context. |
| |
| \internal |
| */ |
| QPlatformOpenGLContext *QOpenGLContext::handle() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->platformGLContext; |
| } |
| |
| /*! |
| Returns the underlying platform context with which this context is sharing. |
| |
| \internal |
| */ |
| |
| QPlatformOpenGLContext *QOpenGLContext::shareHandle() const |
| { |
| Q_D(const QOpenGLContext); |
| if (d->shareContext) |
| return d->shareContext->handle(); |
| return nullptr; |
| } |
| |
| /*! |
| Creates a new OpenGL context instance with parent object \a parent. |
| |
| Before it can be used you need to set the proper format and call create(). |
| |
| \sa create(), makeCurrent() |
| */ |
| QOpenGLContext::QOpenGLContext(QObject *parent) |
| : QObject(*new QOpenGLContextPrivate(), parent) |
| { |
| setScreen(QGuiApplication::primaryScreen()); |
| } |
| |
| /*! |
| Sets the \a format the OpenGL context should be compatible with. You need |
| to call create() before it takes effect. |
| |
| When the format is not explicitly set via this function, the format returned |
| by QSurfaceFormat::defaultFormat() will be used. This means that when having |
| multiple contexts, individual calls to this function can be replaced by one |
| single call to QSurfaceFormat::setDefaultFormat() before creating the first |
| context. |
| */ |
| void QOpenGLContext::setFormat(const QSurfaceFormat &format) |
| { |
| Q_D(QOpenGLContext); |
| d->requestedFormat = format; |
| } |
| |
| /*! |
| Makes this context share textures, shaders, and other OpenGL resources |
| with \a shareContext. You need to call create() before it takes effect. |
| */ |
| void QOpenGLContext::setShareContext(QOpenGLContext *shareContext) |
| { |
| Q_D(QOpenGLContext); |
| d->shareContext = shareContext; |
| } |
| |
| /*! |
| Sets the \a screen the OpenGL context should be valid for. You need to call |
| create() before it takes effect. |
| */ |
| void QOpenGLContext::setScreen(QScreen *screen) |
| { |
| Q_D(QOpenGLContext); |
| if (d->screen) |
| disconnect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(_q_screenDestroyed(QObject*))); |
| d->screen = screen; |
| if (!d->screen) |
| d->screen = QGuiApplication::primaryScreen(); |
| if (d->screen) |
| connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(_q_screenDestroyed(QObject*))); |
| } |
| |
| void QOpenGLContextPrivate::_q_screenDestroyed(QObject *object) |
| { |
| Q_Q(QOpenGLContext); |
| if (object == static_cast<QObject *>(screen)) { |
| screen = nullptr; |
| q->setScreen(nullptr); |
| } |
| } |
| |
| /*! |
| Set the native handles for this context. When create() is called and a |
| native handle is set, configuration settings, like format(), are ignored |
| since this QOpenGLContext will wrap an already created native context |
| instead of creating a new one from scratch. |
| |
| On some platforms the native context handle is not sufficient and other |
| related handles (for example, for a window or display) have to be provided |
| in addition. Therefore \a handle is variant containing a platform-specific |
| value type. These classes can be found in the QtPlatformHeaders module. |
| |
| When create() is called with native handles set, QOpenGLContext does not |
| take ownership of the handles, so destroying the QOpenGLContext does not |
| destroy the native context. |
| |
| \note Some frameworks track the current context and surfaces internally. |
| Making the adopted QOpenGLContext current via Qt will have no effect on such |
| other frameworks' internal state. Therefore a subsequent makeCurrent done |
| via the other framework may have no effect. It is therefore advisable to |
| make explicit calls to make no context and surface current to reset the |
| other frameworks' internal state after performing OpenGL operations via Qt. |
| |
| \note Using foreign contexts with Qt windows and Qt contexts with windows |
| and surfaces created by other frameworks may give unexpected results, |
| depending on the platform, due to potential mismatches in context and window |
| pixel formats. To make sure this does not happen, avoid making contexts and |
| surfaces from different frameworks current together. Instead, prefer |
| approaches based on context sharing where OpenGL resources like textures are |
| accessible both from Qt's and the foreign framework's contexts. |
| |
| \since 5.4 |
| \sa nativeHandle() |
| */ |
| void QOpenGLContext::setNativeHandle(const QVariant &handle) |
| { |
| Q_D(QOpenGLContext); |
| d->nativeHandle = handle; |
| } |
| |
| /*! |
| Returns the native handle for the context. |
| |
| This function provides access to the QOpenGLContext's underlying native |
| context. The returned variant contains a platform-specific value type. These |
| classes can be found in the module QtPlatformHeaders. |
| |
| On platforms where retrieving the native handle is not supported, or if |
| neither create() nor setNativeHandle() was called, a null variant is |
| returned. |
| |
| \since 5.4 |
| \sa setNativeHandle() |
| */ |
| QVariant QOpenGLContext::nativeHandle() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->nativeHandle; |
| } |
| |
| /*! |
| Attempts to create the OpenGL context with the current configuration. |
| |
| The current configuration includes the format, the share context, and the |
| screen. |
| |
| If the OpenGL implementation on your system does not support the requested |
| version of OpenGL context, then QOpenGLContext will try to create the closest |
| matching version. The actual created context properties can be queried |
| using the QSurfaceFormat returned by the format() function. For example, if |
| you request a context that supports OpenGL 4.3 Core profile but the driver |
| and/or hardware only supports version 3.2 Core profile contexts then you will |
| get a 3.2 Core profile context. |
| |
| Returns \c true if the native context was successfully created and is ready to |
| be used with makeCurrent(), swapBuffers(), etc. |
| |
| \note If the context already exists, this function destroys the existing |
| context first, and then creates a new one. |
| |
| \sa makeCurrent(), format() |
| */ |
| bool QOpenGLContext::create() |
| { |
| Q_D(QOpenGLContext); |
| if (d->platformGLContext) |
| destroy(); |
| |
| d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformOpenGLContext(this); |
| if (!d->platformGLContext) |
| return false; |
| d->platformGLContext->setContext(this); |
| d->platformGLContext->initialize(); |
| if (!d->platformGLContext->isSharing()) |
| d->shareContext = nullptr; |
| d->shareGroup = d->shareContext ? d->shareContext->shareGroup() : new QOpenGLContextGroup; |
| d->shareGroup->d_func()->addContext(this); |
| return isValid(); |
| } |
| |
| /*! |
| \internal |
| |
| Destroy the underlying platform context associated with this context. |
| |
| If any other context is directly or indirectly sharing resources with this |
| context, the shared resources, which includes vertex buffer objects, shader |
| objects, textures, and framebuffer objects, are not freed. However, |
| destroying the underlying platform context frees any state associated with |
| the context. |
| |
| After \c destroy() has been called, you must call create() if you wish to |
| use the context again. |
| |
| \note This implicitly calls doneCurrent() if the context is current. |
| |
| \sa create() |
| */ |
| void QOpenGLContext::destroy() |
| { |
| deleteQGLContext(); |
| Q_D(QOpenGLContext); |
| if (d->platformGLContext) |
| emit aboutToBeDestroyed(); |
| if (QOpenGLContext::currentContext() == this) |
| doneCurrent(); |
| if (d->shareGroup) |
| d->shareGroup->d_func()->removeContext(this); |
| d->shareGroup = nullptr; |
| delete d->platformGLContext; |
| d->platformGLContext = nullptr; |
| delete d->functions; |
| d->functions = nullptr; |
| |
| for (QAbstractOpenGLFunctions *func : qAsConst(d->externalVersionFunctions)) { |
| QAbstractOpenGLFunctionsPrivate *func_d = QAbstractOpenGLFunctionsPrivate::get(func); |
| func_d->owningContext = nullptr; |
| func_d->initialized = false; |
| } |
| d->externalVersionFunctions.clear(); |
| qDeleteAll(d->versionFunctions); |
| d->versionFunctions.clear(); |
| |
| delete d->textureFunctions; |
| d->textureFunctions = nullptr; |
| |
| d->nativeHandle = QVariant(); |
| } |
| |
| /*! |
| \fn void QOpenGLContext::aboutToBeDestroyed() |
| |
| This signal is emitted before the underlying native OpenGL context is |
| destroyed, such that users may clean up OpenGL resources that might |
| otherwise be left dangling in the case of shared OpenGL contexts. |
| |
| If you wish to make the context current in order to do clean-up, make sure |
| to only connect to the signal using a direct connection. |
| */ |
| |
| /*! |
| Destroys the QOpenGLContext object. |
| |
| If this is the current context for the thread, doneCurrent() is also called. |
| */ |
| QOpenGLContext::~QOpenGLContext() |
| { |
| destroy(); |
| |
| #ifndef QT_NO_DEBUG |
| QOpenGLContextPrivate::cleanMakeCurrentTracker(this); |
| #endif |
| } |
| |
| /*! |
| Returns if this context is valid, i.e. has been successfully created. |
| |
| On some platforms the return value of \c false for a context that was |
| successfully created previously indicates that the OpenGL context was lost. |
| |
| The typical way to handle context loss scenarios in applications is to |
| check via this function whenever makeCurrent() fails and returns \c false. |
| If this function then returns \c false, recreate the underlying native |
| OpenGL context by calling create(), call makeCurrent() again and then |
| reinitialize all OpenGL resources. |
| |
| On some platforms context loss situations is not something that can |
| avoided. On others however, they may need to be opted-in to. This can be |
| done by enabling \l{QSurfaceFormat::ResetNotification}{ResetNotification} in |
| the QSurfaceFormat. This will lead to setting |
| \c{RESET_NOTIFICATION_STRATEGY_EXT} to \c{LOSE_CONTEXT_ON_RESET_EXT} in the |
| underlying native OpenGL context. QOpenGLContext will then monitor the |
| status via \c{glGetGraphicsResetStatusEXT()} in every makeCurrent(). |
| |
| \sa create() |
| */ |
| bool QOpenGLContext::isValid() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->platformGLContext && d->platformGLContext->isValid(); |
| } |
| |
| /*! |
| Get the QOpenGLFunctions instance for this context. |
| |
| QOpenGLContext offers this as a convenient way to access QOpenGLFunctions |
| without having to manage it manually. |
| |
| The context or a sharing context must be current. |
| |
| The returned QOpenGLFunctions instance is ready to be used and it |
| does not need initializeOpenGLFunctions() to be called. |
| */ |
| QOpenGLFunctions *QOpenGLContext::functions() const |
| { |
| Q_D(const QOpenGLContext); |
| if (!d->functions) |
| const_cast<QOpenGLFunctions *&>(d->functions) = new QOpenGLExtensions(QOpenGLContext::currentContext()); |
| return d->functions; |
| } |
| |
| /*! |
| Get the QOpenGLExtraFunctions instance for this context. |
| |
| QOpenGLContext offers this as a convenient way to access QOpenGLExtraFunctions |
| without having to manage it manually. |
| |
| The context or a sharing context must be current. |
| |
| The returned QOpenGLExtraFunctions instance is ready to be used and it |
| does not need initializeOpenGLFunctions() to be called. |
| |
| \note QOpenGLExtraFunctions contains functionality that is not guaranteed to |
| be available at runtime. Runtime availability depends on the platform, |
| graphics driver, and the OpenGL version requested by the application. |
| |
| \sa QOpenGLFunctions, QOpenGLExtraFunctions |
| */ |
| QOpenGLExtraFunctions *QOpenGLContext::extraFunctions() const |
| { |
| return static_cast<QOpenGLExtraFunctions *>(functions()); |
| } |
| |
| /*! |
| \fn T *QOpenGLContext::versionFunctions() const |
| |
| \overload versionFunctions() |
| |
| Returns a pointer to an object that provides access to all functions for |
| the version and profile of this context. There is no need to call |
| QAbstractOpenGLFunctions::initializeOpenGLFunctions() as long as this context |
| is current. It is also possible to call this function when the context is not |
| current, but in that case it is the caller's responsibility to ensure proper |
| initialization by calling QAbstractOpenGLFunctions::initializeOpenGLFunctions() |
| afterwards. |
| |
| Usually one would use the template version of this function to automatically |
| have the result cast to the correct type. |
| |
| \code |
| QOpenGLFunctions_3_3_Core* funcs = nullptr; |
| funcs = context->versionFunctions<QOpenGLFunctions_3_3_Core>(); |
| if (!funcs) { |
| qWarning() << "Could not obtain required OpenGL context version"; |
| exit(1); |
| } |
| \endcode |
| |
| It is possible to request a functions object for a different version and profile |
| than that for which the context was created. To do this either use the template |
| version of this function specifying the desired functions object type as the |
| template parameter or by passing in a QOpenGLVersionProfile object as an argument |
| to the non-template function. |
| |
| Note that requests for function objects of other versions or profiles can fail and |
| in doing so will return \nullptr. Situations in which creation of the functions |
| object can fail are if the request cannot be satisfied due to asking for functions |
| that are not in the version or profile of this context. For example: |
| |
| \list |
| \li Requesting a 3.3 core profile functions object would succeed. |
| \li Requesting a 3.3 compatibility profile functions object would fail. We would fail |
| to resolve the deprecated functions. |
| \li Requesting a 4.3 core profile functions object would fail. We would fail to resolve |
| the new core functions introduced in versions 4.0-4.3. |
| \li Requesting a 3.1 functions object would succeed. There is nothing in 3.1 that is not |
| also in 3.3 core. |
| \endlist |
| |
| Note that if creating a functions object via this method that the QOpenGLContext |
| retains ownership of the object. This is to allow the object to be cached and shared. |
| */ |
| |
| /*! |
| Returns a pointer to an object that provides access to all functions for the |
| \a versionProfile of this context. There is no need to call |
| QAbstractOpenGLFunctions::initializeOpenGLFunctions() as long as this context |
| is current. It is also possible to call this function when the context is not |
| current, but in that case it is the caller's responsibility to ensure proper |
| initialization by calling QAbstractOpenGLFunctions::initializeOpenGLFunctions() |
| afterwards. |
| |
| Usually one would use the template version of this function to automatically |
| have the result cast to the correct type. |
| */ |
| QAbstractOpenGLFunctions *QOpenGLContext::versionFunctions(const QOpenGLVersionProfile &versionProfile) const |
| { |
| #ifndef QT_OPENGL_ES_2 |
| if (isOpenGLES()) { |
| qWarning("versionFunctions: Not supported on OpenGL ES"); |
| return nullptr; |
| } |
| #endif // QT_OPENGL_ES_2 |
| |
| Q_D(const QOpenGLContext); |
| const QSurfaceFormat f = format(); |
| |
| // Ensure we have a valid version and profile. Default to context's if none specified |
| QOpenGLVersionProfile vp = versionProfile; |
| if (!vp.isValid()) |
| vp = QOpenGLVersionProfile(f); |
| |
| // Check that context is compatible with requested version |
| const QPair<int, int> v = qMakePair(f.majorVersion(), f.minorVersion()); |
| if (v < vp.version()) |
| return nullptr; |
| |
| // If this context only offers core profile functions then we can't create |
| // function objects for legacy or compatibility profile requests |
| if (((vp.hasProfiles() && vp.profile() != QSurfaceFormat::CoreProfile) || vp.isLegacyVersion()) |
| && f.profile() == QSurfaceFormat::CoreProfile) |
| return nullptr; |
| |
| // Create object if suitable one not cached |
| QAbstractOpenGLFunctions* funcs = nullptr; |
| auto it = d->versionFunctions.constFind(vp); |
| if (it == d->versionFunctions.constEnd()) { |
| funcs = QOpenGLVersionFunctionsFactory::create(vp); |
| if (funcs) { |
| funcs->setOwningContext(this); |
| d->versionFunctions.insert(vp, funcs); |
| } |
| } else { |
| funcs = it.value(); |
| } |
| |
| if (funcs && QOpenGLContext::currentContext() == this) |
| funcs->initializeOpenGLFunctions(); |
| |
| return funcs; |
| } |
| |
| /*! |
| Returns the set of OpenGL extensions supported by this context. |
| |
| The context or a sharing context must be current. |
| |
| \sa hasExtension() |
| */ |
| QSet<QByteArray> QOpenGLContext::extensions() const |
| { |
| Q_D(const QOpenGLContext); |
| if (d->extensionNames.isEmpty()) { |
| QOpenGLExtensionMatcher matcher; |
| d->extensionNames = matcher.extensions(); |
| } |
| |
| return d->extensionNames; |
| } |
| |
| /*! |
| Returns \c true if this OpenGL context supports the specified OpenGL |
| \a extension, \c false otherwise. |
| |
| The context or a sharing context must be current. |
| |
| \sa extensions() |
| */ |
| bool QOpenGLContext::hasExtension(const QByteArray &extension) const |
| { |
| return extensions().contains(extension); |
| } |
| |
| /*! |
| Call this to get the default framebuffer object for the current surface. |
| |
| On some platforms (for instance, iOS) the default framebuffer object depends |
| on the surface being rendered to, and might be different from 0. Thus, |
| instead of calling glBindFramebuffer(0), you should call |
| glBindFramebuffer(ctx->defaultFramebufferObject()) if you want your |
| application to work across different Qt platforms. |
| |
| If you use the glBindFramebuffer() in QOpenGLFunctions you do not have to |
| worry about this, as it automatically binds the current context's |
| defaultFramebufferObject() when 0 is passed. |
| |
| \note Widgets that render via framebuffer objects, like QOpenGLWidget and |
| QQuickWidget, will override the value returned from this function when |
| painting is active, because at that time the correct "default" framebuffer |
| is the widget's associated backing framebuffer, not the platform-specific |
| one belonging to the top-level window's surface. This ensures the expected |
| behavior for this function and other classes relying on it (for example, |
| QOpenGLFramebufferObject::bindDefault() or |
| QOpenGLFramebufferObject::release()). |
| |
| \sa QOpenGLFramebufferObject |
| */ |
| GLuint QOpenGLContext::defaultFramebufferObject() const |
| { |
| if (!isValid()) |
| return 0; |
| |
| Q_D(const QOpenGLContext); |
| if (!d->surface || !d->surface->surfaceHandle()) |
| return 0; |
| |
| if (d->defaultFboRedirect) |
| return d->defaultFboRedirect; |
| |
| return d->platformGLContext->defaultFramebufferObject(d->surface->surfaceHandle()); |
| } |
| |
| /*! |
| Makes the context current in the current thread, against the given |
| \a surface. Returns \c true if successful; otherwise returns \c false. |
| The latter may happen if the surface is not exposed, or the graphics |
| hardware is not available due to e.g. the application being suspended. |
| |
| If \a surface is \nullptr this is equivalent to calling doneCurrent(). |
| |
| Avoid calling this function from a different thread than the one the |
| QOpenGLContext instance lives in. If you wish to use QOpenGLContext from a |
| different thread you should first make sure it's not current in the |
| current thread, by calling doneCurrent() if necessary. Then call |
| moveToThread(otherThread) before using it in the other thread. |
| |
| By default Qt employs a check that enforces the above condition on the |
| thread affinity. It is still possible to disable this check by setting the |
| \c{Qt::AA_DontCheckOpenGLContextThreadAffinity} application attribute. Be |
| sure to understand the consequences of using QObjects from outside |
| the thread they live in, as explained in the |
| \l{QObject#Thread Affinity}{QObject thread affinity} documentation. |
| |
| \sa functions(), doneCurrent(), Qt::AA_DontCheckOpenGLContextThreadAffinity |
| */ |
| bool QOpenGLContext::makeCurrent(QSurface *surface) |
| { |
| Q_D(QOpenGLContext); |
| if (!isValid()) |
| return false; |
| |
| if (Q_UNLIKELY(!qApp->testAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity) |
| && thread() != QThread::currentThread())) { |
| qFatal("Cannot make QOpenGLContext current in a different thread"); |
| } |
| |
| if (!surface) { |
| doneCurrent(); |
| return true; |
| } |
| |
| if (!surface->surfaceHandle()) |
| return false; |
| if (!surface->supportsOpenGL()) { |
| qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface" << surface; |
| return false; |
| } |
| |
| if (!d->platformGLContext->makeCurrent(surface->surfaceHandle())) |
| return false; |
| |
| QOpenGLContextPrivate::setCurrentContext(this); |
| #ifndef QT_NO_DEBUG |
| QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true); |
| #endif |
| |
| d->surface = surface; |
| |
| static bool needsWorkaroundSet = false; |
| static bool needsWorkaround = false; |
| |
| if (!needsWorkaroundSet) { |
| QByteArray env; |
| #ifdef Q_OS_ANDROID |
| env = qgetenv(QByteArrayLiteral("QT_ANDROID_DISABLE_GLYPH_CACHE_WORKAROUND")); |
| needsWorkaround = env.isEmpty() || env == QByteArrayLiteral("0") || env == QByteArrayLiteral("false"); |
| #endif |
| env = qgetenv(QByteArrayLiteral("QT_ENABLE_GLYPH_CACHE_WORKAROUND")); |
| if (env == QByteArrayLiteral("1") || env == QByteArrayLiteral("true")) |
| needsWorkaround = true; |
| |
| if (!needsWorkaround) { |
| const char *rendererString = reinterpret_cast<const char *>(functions()->glGetString(GL_RENDERER)); |
| if (rendererString) |
| needsWorkaround = |
| qstrncmp(rendererString, "Mali-4xx", 6) == 0 // Mali-400, Mali-450 |
| || qstrcmp(rendererString, "Mali-T880") == 0 |
| || qstrncmp(rendererString, "Adreno (TM) 2xx", 13) == 0 // Adreno 200, 203, 205 |
| || qstrncmp(rendererString, "Adreno 2xx", 8) == 0 // Same as above but without the '(TM)' |
| || qstrncmp(rendererString, "Adreno (TM) 3xx", 13) == 0 // Adreno 302, 305, 320, 330 |
| || qstrncmp(rendererString, "Adreno 3xx", 8) == 0 // Same as above but without the '(TM)' |
| || qstrncmp(rendererString, "Adreno (TM) 4xx", 13) == 0 // Adreno 405, 418, 420, 430 |
| || qstrncmp(rendererString, "Adreno 4xx", 8) == 0 // Same as above but without the '(TM)' |
| || qstrncmp(rendererString, "Adreno (TM) 5xx", 13) == 0 // Adreno 505, 506, 510, 530, 540 |
| || qstrncmp(rendererString, "Adreno 5xx", 8) == 0 // Same as above but without the '(TM)' |
| || qstrncmp(rendererString, "Adreno (TM) 6xx", 13) == 0 // Adreno 610, 620, 630 |
| || qstrncmp(rendererString, "Adreno 6xx", 8) == 0 // Same as above but without the '(TM)' |
| || qstrcmp(rendererString, "GC800 core") == 0 |
| || qstrcmp(rendererString, "GC1000 core") == 0 |
| || strstr(rendererString, "GC2000") != nullptr |
| || qstrcmp(rendererString, "Immersion.16") == 0; |
| } |
| needsWorkaroundSet = true; |
| } |
| |
| if (needsWorkaround) |
| d->workaround_brokenFBOReadBack = true; |
| |
| d->shareGroup->d_func()->deletePendingResources(this); |
| |
| return true; |
| } |
| |
| /*! |
| Convenience function for calling makeCurrent with a 0 surface. |
| |
| This results in no context being current in the current thread. |
| |
| \sa makeCurrent(), currentContext() |
| */ |
| void QOpenGLContext::doneCurrent() |
| { |
| Q_D(QOpenGLContext); |
| if (!isValid()) |
| return; |
| |
| if (QOpenGLContext::currentContext() == this) |
| d->shareGroup->d_func()->deletePendingResources(this); |
| |
| d->platformGLContext->doneCurrent(); |
| QOpenGLContextPrivate::setCurrentContext(nullptr); |
| |
| d->surface = nullptr; |
| } |
| |
| /*! |
| Returns the surface the context has been made current with. |
| |
| This is the surface passed as an argument to makeCurrent(). |
| */ |
| QSurface *QOpenGLContext::surface() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->surface; |
| } |
| |
| |
| /*! |
| Swap the back and front buffers of \a surface. |
| |
| Call this to finish a frame of OpenGL rendering, and make sure to |
| call makeCurrent() again before issuing any further OpenGL commands, |
| for example as part of a new frame. |
| */ |
| void QOpenGLContext::swapBuffers(QSurface *surface) |
| { |
| Q_D(QOpenGLContext); |
| if (!isValid()) |
| return; |
| |
| if (!surface) { |
| qWarning("QOpenGLContext::swapBuffers() called with null argument"); |
| return; |
| } |
| |
| if (!surface->supportsOpenGL()) { |
| qWarning("QOpenGLContext::swapBuffers() called with non-opengl surface"); |
| return; |
| } |
| |
| if (surface->surfaceClass() == QSurface::Window |
| && !qt_window_private(static_cast<QWindow *>(surface))->receivedExpose) |
| { |
| qWarning("QOpenGLContext::swapBuffers() called with non-exposed window, behavior is undefined"); |
| } |
| |
| QPlatformSurface *surfaceHandle = surface->surfaceHandle(); |
| if (!surfaceHandle) |
| return; |
| |
| #if !defined(QT_NO_DEBUG) |
| if (!QOpenGLContextPrivate::toggleMakeCurrentTracker(this, false)) |
| qWarning("QOpenGLContext::swapBuffers() called without corresponding makeCurrent()"); |
| #endif |
| if (surface->format().swapBehavior() == QSurfaceFormat::SingleBuffer) |
| functions()->glFlush(); |
| d->platformGLContext->swapBuffers(surfaceHandle); |
| } |
| |
| /*! |
| Resolves the function pointer to an OpenGL extension function, identified by \a procName |
| |
| Returns \nullptr if no such function can be found. |
| */ |
| QFunctionPointer QOpenGLContext::getProcAddress(const QByteArray &procName) const |
| { |
| return getProcAddress(procName.constData()); |
| } |
| |
| /*! |
| \overload |
| \since 5.8 |
| */ |
| QFunctionPointer QOpenGLContext::getProcAddress(const char *procName) const |
| { |
| Q_D(const QOpenGLContext); |
| if (!d->platformGLContext) |
| return nullptr; |
| return d->platformGLContext->getProcAddress(procName); |
| } |
| |
| /*! |
| Returns the format of the underlying platform context, if create() has been called. |
| |
| Otherwise, returns the requested format. |
| |
| The requested and the actual format may differ. Requesting a given OpenGL version does |
| not mean the resulting context will target exactly the requested version. It is only |
| guaranteed that the version/profile/options combination for the created context is |
| compatible with the request, as long as the driver is able to provide such a context. |
| |
| For example, requesting an OpenGL version 3.x core profile context may result in an |
| OpenGL 4.x core profile context. Similarly, a request for OpenGL 2.1 may result in an |
| OpenGL 3.0 context with deprecated functions enabled. Finally, depending on the |
| driver, unsupported versions may result in either a context creation failure or in a |
| context for the highest supported version. |
| |
| Similar differences are possible in the buffer sizes, for example, the resulting |
| context may have a larger depth buffer than requested. This is perfectly normal. |
| */ |
| QSurfaceFormat QOpenGLContext::format() const |
| { |
| Q_D(const QOpenGLContext); |
| if (!d->platformGLContext) |
| return d->requestedFormat; |
| return d->platformGLContext->format(); |
| } |
| |
| /*! |
| Returns the share group this context belongs to. |
| */ |
| QOpenGLContextGroup *QOpenGLContext::shareGroup() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->shareGroup; |
| } |
| |
| /*! |
| Returns the share context this context was created with. |
| |
| If the underlying platform was not able to support the requested |
| sharing, this will return 0. |
| */ |
| QOpenGLContext *QOpenGLContext::shareContext() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->shareContext; |
| } |
| |
| /*! |
| Returns the screen the context was created for. |
| */ |
| QScreen *QOpenGLContext::screen() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->screen; |
| } |
| |
| /*! |
| internal: Needs to have a pointer to qGLContext. But since this is in Qt GUI we can't |
| have any type information. |
| |
| \internal |
| */ |
| void *QOpenGLContext::qGLContextHandle() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->qGLContextHandle; |
| } |
| |
| /*! |
| internal: If the delete function is specified QOpenGLContext "owns" |
| the passed context handle and will use the delete function to destroy it. |
| |
| \internal |
| */ |
| void QOpenGLContext::setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *)) |
| { |
| Q_D(QOpenGLContext); |
| d->qGLContextHandle = handle; |
| d->qGLContextDeleteFunction = qGLContextDeleteFunction; |
| } |
| |
| /*! |
| \internal |
| */ |
| void QOpenGLContext::deleteQGLContext() |
| { |
| Q_D(QOpenGLContext); |
| if (d->qGLContextDeleteFunction && d->qGLContextHandle) { |
| d->qGLContextDeleteFunction(d->qGLContextHandle); |
| d->qGLContextDeleteFunction = nullptr; |
| d->qGLContextHandle = nullptr; |
| } |
| } |
| |
| /*! |
| Returns the platform-specific handle for the OpenGL implementation that |
| is currently in use. (for example, a HMODULE on Windows) |
| |
| On platforms that do not use dynamic GL switching, the return value |
| is \nullptr. |
| |
| The library might be GL-only, meaning that windowing system interface |
| functions (for example EGL) may live in another, separate library. |
| |
| \note This function requires that the QGuiApplication instance is already created. |
| |
| \sa openGLModuleType() |
| |
| \since 5.3 |
| */ |
| void *QOpenGLContext::openGLModuleHandle() |
| { |
| #ifdef QT_OPENGL_DYNAMIC |
| QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface(); |
| Q_ASSERT(ni); |
| return ni->nativeResourceForIntegration(QByteArrayLiteral("glhandle")); |
| #else |
| return nullptr; |
| #endif |
| } |
| |
| /*! |
| \enum QOpenGLContext::OpenGLModuleType |
| This enum defines the type of the underlying OpenGL implementation. |
| |
| \value LibGL OpenGL |
| \value LibGLES OpenGL ES 2.0 or higher |
| |
| \since 5.3 |
| */ |
| |
| /*! |
| Returns the underlying OpenGL implementation type. |
| |
| On platforms where the OpenGL implementation is not dynamically |
| loaded, the return value is determined during compile time and never |
| changes. |
| |
| \note A desktop OpenGL implementation may be capable of creating |
| ES-compatible contexts too. Therefore in most cases it is more |
| appropriate to check QSurfaceFormat::renderableType() or use |
| the convenience function isOpenGLES(). |
| |
| \note This function requires that the QGuiApplication instance is already created. |
| |
| \since 5.3 |
| */ |
| QOpenGLContext::OpenGLModuleType QOpenGLContext::openGLModuleType() |
| { |
| #if defined(QT_OPENGL_DYNAMIC) |
| Q_ASSERT(qGuiApp); |
| return QGuiApplicationPrivate::instance()->platformIntegration()->openGLModuleType(); |
| #elif defined(QT_OPENGL_ES_2) |
| return LibGLES; |
| #else |
| return LibGL; |
| #endif |
| } |
| |
| /*! |
| Returns true if the context is an OpenGL ES context. |
| |
| If the context has not yet been created, the result is based on the |
| requested format set via setFormat(). |
| |
| \sa create(), format(), setFormat() |
| |
| \since 5.3 |
| */ |
| bool QOpenGLContext::isOpenGLES() const |
| { |
| return format().renderableType() == QSurfaceFormat::OpenGLES; |
| } |
| |
| /*! |
| Returns \c true if the platform supports OpenGL rendering outside the main (gui) |
| thread. |
| |
| The value is controlled by the platform plugin in use and may also depend on the |
| graphics drivers. |
| |
| \since 5.5 |
| */ |
| bool QOpenGLContext::supportsThreadedOpenGL() |
| { |
| Q_ASSERT(qGuiApp); |
| return QGuiApplicationPrivate::instance()->platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL); |
| } |
| |
| /*! |
| \since 5.5 |
| |
| Returns the application-wide shared OpenGL context, if present. |
| Otherwise, returns \nullptr. |
| |
| This is useful if you need to upload OpenGL objects (buffers, textures, |
| etc.) before creating or showing a QOpenGLWidget or QQuickWidget. |
| |
| \note You must set the Qt::AA_ShareOpenGLContexts flag on QGuiApplication |
| before creating the QGuiApplication object, otherwise Qt may not create a |
| global shared context. |
| |
| \warning Do not attempt to make the context returned by this function |
| current on any surface. Instead, you can create a new context which shares |
| with the global one, and then make the new context current. |
| |
| \sa Qt::AA_ShareOpenGLContexts, setShareContext(), makeCurrent() |
| */ |
| QOpenGLContext *QOpenGLContext::globalShareContext() |
| { |
| Q_ASSERT(qGuiApp); |
| return qt_gl_global_share_context(); |
| } |
| |
| /*! |
| \internal |
| */ |
| QOpenGLVersionFunctionsStorage *QOpenGLContext::functionsBackendStorage() const |
| { |
| Q_D(const QOpenGLContext); |
| return &d->versionFunctionsStorage; |
| } |
| |
| /*! |
| \internal |
| */ |
| void QOpenGLContext::insertExternalFunctions(QAbstractOpenGLFunctions *f) |
| { |
| Q_D(QOpenGLContext); |
| d->externalVersionFunctions.insert(f); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QOpenGLContext::removeExternalFunctions(QAbstractOpenGLFunctions *f) |
| { |
| Q_D(QOpenGLContext); |
| d->externalVersionFunctions.remove(f); |
| } |
| |
| /*! |
| \internal |
| */ |
| QOpenGLTextureHelper* QOpenGLContext::textureFunctions() const |
| { |
| Q_D(const QOpenGLContext); |
| return d->textureFunctions; |
| } |
| |
| /*! |
| \internal |
| */ |
| void QOpenGLContext::setTextureFunctions(QOpenGLTextureHelper* textureFuncs) |
| { |
| Q_D(QOpenGLContext); |
| d->textureFunctions = textureFuncs; |
| } |
| |
| /*! |
| \class QOpenGLContextGroup |
| \since 5.0 |
| \brief The QOpenGLContextGroup class represents a group of contexts sharing |
| OpenGL resources. |
| \inmodule QtGui |
| |
| QOpenGLContextGroup is automatically created and managed by QOpenGLContext |
| instances. Its purpose is to identify all the contexts that are sharing |
| resources. |
| |
| \sa QOpenGLContext::shareGroup() |
| */ |
| QOpenGLContextGroup::QOpenGLContextGroup() |
| : QObject(*new QOpenGLContextGroupPrivate()) |
| { |
| } |
| |
| /*! |
| \internal |
| */ |
| QOpenGLContextGroup::~QOpenGLContextGroup() |
| { |
| Q_D(QOpenGLContextGroup); |
| d->cleanup(); |
| } |
| |
| /*! |
| Returns all the QOpenGLContext objects in this share group. |
| */ |
| QList<QOpenGLContext *> QOpenGLContextGroup::shares() const |
| { |
| Q_D(const QOpenGLContextGroup); |
| return d->m_shares; |
| } |
| |
| /*! |
| Returns the QOpenGLContextGroup corresponding to the current context. |
| |
| \sa QOpenGLContext::currentContext() |
| */ |
| QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup() |
| { |
| QOpenGLContext *current = QOpenGLContext::currentContext(); |
| return current ? current->shareGroup() : nullptr; |
| } |
| |
| void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx) |
| { |
| const auto locker = qt_scoped_lock(m_mutex); |
| m_refs.ref(); |
| m_shares << ctx; |
| } |
| |
| void QOpenGLContextGroupPrivate::removeContext(QOpenGLContext *ctx) |
| { |
| Q_Q(QOpenGLContextGroup); |
| |
| bool deleteObject = false; |
| |
| { |
| const auto locker = qt_scoped_lock(m_mutex); |
| m_shares.removeOne(ctx); |
| |
| if (ctx == m_context && !m_shares.isEmpty()) |
| m_context = m_shares.constFirst(); |
| |
| if (!m_refs.deref()) { |
| cleanup(); |
| deleteObject = true; |
| } |
| } |
| |
| if (deleteObject) { |
| if (q->thread() == QThread::currentThread()) |
| delete q; // Delete directly to prevent leak, refer to QTBUG-29056 |
| else |
| q->deleteLater(); |
| } |
| } |
| |
| void QOpenGLContextGroupPrivate::cleanup() |
| { |
| Q_Q(QOpenGLContextGroup); |
| { |
| QHash<QOpenGLMultiGroupSharedResource *, QOpenGLSharedResource *>::const_iterator it, end; |
| end = m_resources.constEnd(); |
| for (it = m_resources.constBegin(); it != end; ++it) |
| it.key()->cleanup(q, it.value()); |
| m_resources.clear(); |
| } |
| |
| QList<QOpenGLSharedResource *>::iterator it = m_sharedResources.begin(); |
| QList<QOpenGLSharedResource *>::iterator end = m_sharedResources.end(); |
| |
| while (it != end) { |
| (*it)->invalidateResource(); |
| (*it)->m_group = nullptr; |
| ++it; |
| } |
| |
| m_sharedResources.clear(); |
| |
| qDeleteAll(m_pendingDeletion.begin(), m_pendingDeletion.end()); |
| m_pendingDeletion.clear(); |
| } |
| |
| void QOpenGLContextGroupPrivate::deletePendingResources(QOpenGLContext *ctx) |
| { |
| const auto locker = qt_scoped_lock(m_mutex); |
| |
| const QList<QOpenGLSharedResource *> pending = m_pendingDeletion; |
| m_pendingDeletion.clear(); |
| |
| QList<QOpenGLSharedResource *>::const_iterator it = pending.begin(); |
| QList<QOpenGLSharedResource *>::const_iterator end = pending.end(); |
| while (it != end) { |
| (*it)->freeResource(ctx); |
| delete *it; |
| ++it; |
| } |
| } |
| |
| /*! |
| \class QOpenGLSharedResource |
| \internal |
| \since 5.0 |
| \brief The QOpenGLSharedResource class is used to keep track of resources |
| that are shared between OpenGL contexts (like textures, framebuffer |
| objects, shader programs, etc), and clean them up in a safe way when |
| they're no longer needed. |
| \inmodule QtGui |
| |
| The QOpenGLSharedResource instance should never be deleted, instead free() |
| should be called when it's no longer needed. Thus it will be put on a queue |
| and freed at an appropriate time (when a context in the share group becomes |
| current). |
| |
| The sub-class needs to implement two pure virtual functions. The first, |
| freeResource() must be implemented to actually do the freeing, for example |
| call glDeleteTextures() on a texture id. Qt makes sure a valid context in |
| the resource's share group is current at the time. The other, |
| invalidateResource(), is called by Qt in the circumstance when the last |
| context in the share group is destroyed before free() has been called. The |
| implementation of invalidateResource() should set any identifiers to 0 or |
| set a flag to prevent them from being used later on. |
| */ |
| QOpenGLSharedResource::QOpenGLSharedResource(QOpenGLContextGroup *group) |
| : m_group(group) |
| { |
| const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex); |
| m_group->d_func()->m_sharedResources << this; |
| } |
| |
| QOpenGLSharedResource::~QOpenGLSharedResource() |
| { |
| } |
| |
| // schedule the resource for deletion at an appropriate time |
| void QOpenGLSharedResource::free() |
| { |
| if (!m_group) { |
| delete this; |
| return; |
| } |
| |
| const auto locker = qt_scoped_lock(m_group->d_func()->m_mutex); |
| m_group->d_func()->m_sharedResources.removeOne(this); |
| m_group->d_func()->m_pendingDeletion << this; |
| |
| // can we delete right away? |
| QOpenGLContext *current = QOpenGLContext::currentContext(); |
| if (current && current->shareGroup() == m_group) { |
| m_group->d_func()->deletePendingResources(current); |
| } |
| } |
| |
| /*! |
| \class QOpenGLSharedResourceGuard |
| \internal |
| \since 5.0 |
| \brief The QOpenGLSharedResourceGuard class is a convenience sub-class of |
| QOpenGLSharedResource to be used to track a single OpenGL object with a |
| GLuint identifier. The constructor takes a function pointer to a function |
| that will be used to free the resource if and when necessary. |
| \inmodule QtGui |
| |
| */ |
| void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context) |
| { |
| if (m_id) { |
| QOpenGLFunctions functions(context); |
| m_func(&functions, m_id); |
| m_id = 0; |
| } |
| } |
| |
| /*! |
| \class QOpenGLMultiGroupSharedResource |
| \internal |
| \since 5.0 |
| \brief The QOpenGLMultiGroupSharedResource keeps track of a shared resource |
| that might be needed from multiple contexts, like a glyph cache or gradient |
| cache. One instance of the object is created for each group when necessary. |
| The shared resource instance should have a constructor that takes a |
| QOpenGLContext *. To get an instance for a given context one calls |
| T *QOpenGLMultiGroupSharedResource::value<T>(context), where T is a sub-class |
| of QOpenGLSharedResource. |
| \inmodule QtGui |
| |
| You should not call free() on QOpenGLSharedResources owned by a |
| QOpenGLMultiGroupSharedResource instance. |
| */ |
| QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource() |
| : active(0) |
| { |
| #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG |
| qDebug("Creating context group resource object %p.", this); |
| #endif |
| } |
| |
| QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource() |
| { |
| #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG |
| qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size()); |
| #endif |
| for (int i = 0; i < m_groups.size(); ++i) { |
| if (!m_groups.at(i)->shares().isEmpty()) { |
| QOpenGLContext *context = m_groups.at(i)->shares().constFirst(); |
| QOpenGLSharedResource *resource = value(context); |
| if (resource) |
| resource->free(); |
| } |
| m_groups.at(i)->d_func()->m_resources.remove(this); |
| active.deref(); |
| } |
| #ifndef QT_NO_DEBUG |
| if (active.loadRelaxed() != 0) { |
| qWarning("QtGui: Resources are still available at program shutdown.\n" |
| " This is possibly caused by a leaked QOpenGLWidget, \n" |
| " QOpenGLFramebufferObject or QOpenGLPixelBuffer."); |
| } |
| #endif |
| } |
| |
| void QOpenGLMultiGroupSharedResource::insert(QOpenGLContext *context, QOpenGLSharedResource *value) |
| { |
| #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG |
| qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this); |
| #endif |
| QOpenGLContextGroup *group = context->shareGroup(); |
| Q_ASSERT(!group->d_func()->m_resources.contains(this)); |
| group->d_func()->m_resources.insert(this, value); |
| m_groups.append(group); |
| active.ref(); |
| } |
| |
| QOpenGLSharedResource *QOpenGLMultiGroupSharedResource::value(QOpenGLContext *context) |
| { |
| QOpenGLContextGroup *group = context->shareGroup(); |
| return group->d_func()->m_resources.value(this, 0); |
| } |
| |
| QList<QOpenGLSharedResource *> QOpenGLMultiGroupSharedResource::resources() const |
| { |
| QList<QOpenGLSharedResource *> result; |
| for (QList<QOpenGLContextGroup *>::const_iterator it = m_groups.constBegin(); it != m_groups.constEnd(); ++it) { |
| QOpenGLSharedResource *resource = (*it)->d_func()->m_resources.value(const_cast<QOpenGLMultiGroupSharedResource *>(this), 0); |
| if (resource) |
| result << resource; |
| } |
| return result; |
| } |
| |
| void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenGLSharedResource *value) |
| { |
| #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG |
| qDebug("Cleaning up context group resource %p, for group %p in thread %p.", this, group, QThread::currentThread()); |
| #endif |
| value->invalidateResource(); |
| value->free(); |
| active.deref(); |
| |
| Q_ASSERT(m_groups.contains(group)); |
| m_groups.removeOne(group); |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| QDebug operator<<(QDebug debug, const QOpenGLVersionProfile &vp) |
| { |
| QDebugStateSaver saver(debug); |
| debug.nospace(); |
| debug << "QOpenGLVersionProfile("; |
| if (vp.isValid()) { |
| debug << vp.version().first << '.' << vp.version().second |
| << ", profile=" << vp.profile(); |
| } else { |
| debug << "invalid"; |
| } |
| debug << ')'; |
| return debug; |
| } |
| |
| QDebug operator<<(QDebug debug, const QOpenGLContext *ctx) |
| { |
| QDebugStateSaver saver(debug); |
| debug.nospace(); |
| debug.noquote(); |
| debug << "QOpenGLContext("; |
| if (ctx) { |
| debug << static_cast<const void *>(ctx); |
| if (ctx->isValid()) { |
| debug << ", nativeHandle=" << ctx->nativeHandle() |
| << ", format=" << ctx->format(); |
| if (const QSurface *sf = ctx->surface()) |
| debug << ", surface=" << sf; |
| if (const QScreen *s = ctx->screen()) |
| debug << ", screen=\"" << s->name() << '"'; |
| } else { |
| debug << ", invalid"; |
| } |
| } else { |
| debug << '0'; |
| } |
| debug << ')'; |
| return debug; |
| } |
| |
| QDebug operator<<(QDebug debug, const QOpenGLContextGroup *cg) |
| { |
| QDebugStateSaver saver(debug); |
| debug.nospace(); |
| debug << "QOpenGLContextGroup("; |
| if (cg) |
| debug << cg->shares(); |
| else |
| debug << '0'; |
| debug << ')'; |
| return debug; |
| } |
| #endif // QT_NO_DEBUG_STREAM |
| |
| #include "moc_qopenglcontext.cpp" |
| |
| QT_END_NAMESPACE |