From 2d39471897f0a3a769406ec9c2b39558ebd45af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 26 Jan 2012 11:44:05 +0100 Subject: Introduced QOpenGLContext::defaultFramebufferObject(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add some debugging helpers to make sure applications are correctly written even on less restrictive platforms. Change-Id: Ie92e497c32e07b2b83662f7ab5540d8f37777fd0 Reviewed-by: Jørgen Lind --- src/gui/kernel/qopenglcontext.cpp | 71 ++++++++++++++++++++++++++- src/gui/kernel/qopenglcontext.h | 10 +++- src/gui/kernel/qopenglcontext_p.h | 23 ++++++++- src/gui/kernel/qplatformopenglcontext_qpa.cpp | 12 +++++ src/gui/kernel/qplatformopenglcontext_qpa.h | 3 ++ src/gui/opengl/qopenglframebufferobject.cpp | 8 +-- src/gui/opengl/qopenglfunctions.cpp | 3 ++ src/gui/opengl/qopenglfunctions.h | 2 + src/opengl/qglfunctions.cpp | 3 ++ src/opengl/qglfunctions.h | 3 ++ 10 files changed, 129 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp index 29107d4261..cd4e8ebc01 100644 --- a/src/gui/kernel/qopenglcontext.cpp +++ b/src/gui/kernel/qopenglcontext.cpp @@ -68,6 +68,11 @@ public: static QThreadStorage qwindow_context_storage; +#ifndef QT_NO_DEBUG +QHash QOpenGLContextPrivate::makeCurrentTracker; +QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex; +#endif + void QOpenGLContextPrivate::setCurrentContext(QOpenGLContext *context) { QGuiGLThreadContext *threadContext = qwindow_context_storage.localData(); @@ -204,6 +209,10 @@ void QOpenGLContext::destroy() QOpenGLContext::~QOpenGLContext() { destroy(); + +#ifndef QT_NO_DEBUG + QOpenGLContextPrivate::cleanMakeCurrentTracker(this); +#endif } /*! @@ -229,6 +238,29 @@ QOpenGLFunctions *QOpenGLContext::functions() const return d->functions; } +/*! + Call this to get the default framebuffer object for the current surface. + + On some platforms 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. +*/ +GLuint QOpenGLContext::defaultFramebufferObject() const +{ + if (!isValid()) + return 0; + + Q_D(const QOpenGLContext); + if (!d->surface || !d->surface->surfaceHandle()) + return 0; + + return d->platformGLContext->defaultFramebufferObject(d->surface->surfaceHandle()); +} + /*! If surface is 0 this is equivalent to calling doneCurrent(). @@ -260,6 +292,10 @@ bool QOpenGLContext::makeCurrent(QSurface *surface) d->shareGroup->d_func()->deletePendingResources(this); +#ifndef QT_NO_DEBUG + QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true); +#endif + return true; } @@ -294,6 +330,17 @@ QSurface *QOpenGLContext::surface() const } +/*! + Swap the back and front buffers of the given surface. + + Call this to finish a frame of OpenGL rendering, and make sure to + call makeCurrent() again before you begin a new frame. + + If you have bound a non-default framebuffer object, you need to + use bindDefaultFramebufferObject() to make sure that the default + framebuffer object is bound before calling swapBuffers(), as + some Qt platforms assume that the default framebuffer object is bound. +*/ void QOpenGLContext::swapBuffers(QSurface *surface) { Q_D(QOpenGLContext); @@ -306,8 +353,28 @@ void QOpenGLContext::swapBuffers(QSurface *surface) } QPlatformSurface *surfaceHandle = surface->surfaceHandle(); - if (surfaceHandle) - d->platformGLContext->swapBuffers(surfaceHandle); + if (!surfaceHandle) + return; + +#if !defined(QT_NO_DEBUG) + if (currentContext() != this) + qWarning() << "QOpenGLContext::swapBuffers() called with non-current surface"; + else if (!QOpenGLContextPrivate::toggleMakeCurrentTracker(this, false)) + qWarning() << "QOpenGLContext::swapBuffers() called without corresponding makeCurrent()"; + +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif + + GLint framebufferBinding = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &framebufferBinding); + + GLint platformFramebuffer = GLint(d->platformGLContext->defaultFramebufferObject(surfaceHandle)); + if (framebufferBinding != platformFramebuffer) + qWarning() << "QOpenGLContext::swapBuffers() called with non-default framebuffer object bound"; +#endif + + d->platformGLContext->swapBuffers(surfaceHandle); } QFunctionPointer QOpenGLContext::getProcAddress(const QByteArray &procName) diff --git a/src/gui/kernel/qopenglcontext.h b/src/gui/kernel/qopenglcontext.h index 2d86a6db31..449c75f22a 100644 --- a/src/gui/kernel/qopenglcontext.h +++ b/src/gui/kernel/qopenglcontext.h @@ -50,11 +50,17 @@ #include +#ifdef __GLEW_H__ +#warning qopenglfunctions.h is not compatible with GLEW, GLEW defines will be undefined +#warning To use GLEW with Qt, do not include or after glew.h +#endif + +#include + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE - class QOpenGLContextPrivate; class QOpenGLContextGroupPrivate; class QOpenGLFunctions; @@ -103,6 +109,8 @@ public: QOpenGLContextGroup *shareGroup() const; QScreen *screen() const; + GLuint defaultFramebufferObject() const; + bool makeCurrent(QSurface *surface); void doneCurrent(); diff --git a/src/gui/kernel/qopenglcontext_p.h b/src/gui/kernel/qopenglcontext_p.h index bd5fae989b..819f86fb24 100644 --- a/src/gui/kernel/qopenglcontext_p.h +++ b/src/gui/kernel/qopenglcontext_p.h @@ -47,6 +47,10 @@ #include #include +#ifndef QT_NO_DEBUG +#include +#endif + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -185,7 +189,6 @@ public: , surface(0) , functions(0) , current_fbo(0) - , default_fbo(0) , workaround_brokenFBOReadBack(false) , workaround_brokenTexSubImage(false) , active_engine(0) @@ -210,7 +213,6 @@ public: QOpenGLFunctions *functions; GLuint current_fbo; - GLuint default_fbo; bool workaround_brokenFBOReadBack; bool workaround_brokenTexSubImage; @@ -220,6 +222,23 @@ public: static void setCurrentContext(QOpenGLContext *context); int maxTextureSize() const { return 1024; } + +#if !defined(QT_NO_DEBUG) + static bool toggleMakeCurrentTracker(QOpenGLContext *context, bool value) + { + QMutexLocker locker(&makeCurrentTrackerMutex); + bool old = makeCurrentTracker.value(context, false); + makeCurrentTracker.insert(context, value); + return old; + } + static void cleanMakeCurrentTracker(QOpenGLContext *context) + { + QMutexLocker locker(&makeCurrentTrackerMutex); + makeCurrentTracker.remove(context); + } + static QHash makeCurrentTracker; + static QMutex makeCurrentTrackerMutex; +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformopenglcontext_qpa.cpp b/src/gui/kernel/qplatformopenglcontext_qpa.cpp index ba7a75a6b8..244556b13b 100644 --- a/src/gui/kernel/qplatformopenglcontext_qpa.cpp +++ b/src/gui/kernel/qplatformopenglcontext_qpa.cpp @@ -41,6 +41,8 @@ #include "qplatformopenglcontext_qpa.h" +#include + QT_BEGIN_NAMESPACE /*! @@ -94,6 +96,16 @@ QPlatformOpenGLContext::~QPlatformOpenGLContext() { } +/*! + Reimplement in subclass if your platform uses framebuffer objects for surfaces. + + The default implementation returns 0. +*/ +GLuint QPlatformOpenGLContext::defaultFramebufferObject(QPlatformSurface *) const +{ + return 0; +} + QOpenGLContext *QPlatformOpenGLContext::context() const { Q_D(const QPlatformOpenGLContext); diff --git a/src/gui/kernel/qplatformopenglcontext_qpa.h b/src/gui/kernel/qplatformopenglcontext_qpa.h index 772fbdf06f..5a838a0909 100644 --- a/src/gui/kernel/qplatformopenglcontext_qpa.h +++ b/src/gui/kernel/qplatformopenglcontext_qpa.h @@ -45,6 +45,7 @@ #include #include #include +#include QT_BEGIN_HEADER @@ -64,6 +65,8 @@ public: virtual void swapBuffers(QPlatformSurface *surface) = 0; + virtual GLuint defaultFramebufferObject(QPlatformSurface *surface) const; + virtual bool makeCurrent(QPlatformSurface *surface) = 0; virtual void doneCurrent() = 0; diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index bb723b7ce6..6cfb3c1b01 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -892,8 +892,8 @@ bool QOpenGLFramebufferObject::release() #endif if (current) { - current->d_func()->current_fbo = current->d_func()->default_fbo; - d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->default_fbo); + current->d_func()->current_fbo = current->defaultFramebufferObject(); + d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo); } return true; @@ -1057,8 +1057,8 @@ bool QOpenGLFramebufferObject::bindDefault() QOpenGLFunctions functions(ctx); if (ctx) { - ctx->d_func()->current_fbo = ctx->d_func()->default_fbo; - functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->default_fbo); + ctx->d_func()->current_fbo = ctx->defaultFramebufferObject(); + functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); #ifdef QT_DEBUG } else { qWarning("QOpenGLFramebufferObject::bindDefault() called without current context."); diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index b2a329067b..50f70d7408 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -478,6 +478,9 @@ void QOpenGLFunctions::initializeGLFunctions() Convenience function that calls glBindFramebuffer(\a target, \a framebuffer). + Note that Qt will translate a \a framebuffer argument of 0 to the currently + bound QOpenGLContext's defaultFramebufferObject(). + For more information, see the OpenGL/ES 2.0 documentation for \l{http://www.khronos.org/opengles/sdk/docs/man/glBindFramebuffer.xml}{glBindFramebuffer()}. */ diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h index 99b35a3a93..d197ccac91 100644 --- a/src/gui/opengl/qopenglfunctions.h +++ b/src/gui/opengl/qopenglfunctions.h @@ -478,6 +478,8 @@ inline void QOpenGLFunctions::glBindBuffer(GLenum target, GLuint buffer) inline void QOpenGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) { + if (framebuffer == 0) + framebuffer = QOpenGLContext::currentContext()->defaultFramebufferObject(); #if defined(QT_OPENGL_ES_2) ::glBindFramebuffer(target, framebuffer); #else diff --git a/src/opengl/qglfunctions.cpp b/src/opengl/qglfunctions.cpp index 2a11ee6a28..6e640b0fe7 100644 --- a/src/opengl/qglfunctions.cpp +++ b/src/opengl/qglfunctions.cpp @@ -395,6 +395,9 @@ void QGLFunctions::initializeGLFunctions(const QGLContext *context) Convenience function that calls glBindFramebuffer(\a target, \a framebuffer). + Note that Qt will translate a \a framebuffer argument of 0 to the currently + bound QOpenGLContext's defaultFramebufferObject(). + For more information, see the OpenGL/ES 2.0 documentation for \l{http://www.khronos.org/opengles/sdk/docs/man/glBindFramebuffer.xml}{glBindFramebuffer()}. */ diff --git a/src/opengl/qglfunctions.h b/src/opengl/qglfunctions.h index af6c583a10..40c00f1af8 100644 --- a/src/opengl/qglfunctions.h +++ b/src/opengl/qglfunctions.h @@ -48,6 +48,7 @@ #endif #include +#include QT_BEGIN_HEADER @@ -450,6 +451,8 @@ inline void QGLFunctions::glBindBuffer(GLenum target, GLuint buffer) inline void QGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) { + if (framebuffer == 0) + framebuffer = QOpenGLContext::currentContext()->defaultFramebufferObject(); #if defined(QT_OPENGL_ES_2) ::glBindFramebuffer(target, framebuffer); #else -- cgit v1.2.3