Added support for resetting QOpenGLFramebufferObject attachments.
authorSamuel Rødal <samuel.rodal@nokia.com>
Tue, 14 Feb 2012 18:43:46 +0000 (19:43 +0100)
committerQt by Nokia <qt-info@nokia.com>
Wed, 15 Feb 2012 16:01:50 +0000 (17:01 +0100)
As the documentation says, this can be useful to free or recreate
attachments when needed. For example, it might be useful to free stencil
and depth attachments to free up resources when not rendering to the
framebuffer object.

Change-Id: Ib267024fdd380a788c256eb8fb86e0f8832329e0
Reviewed-by: Kim M. Kalland <kim.kalland@nokia.com>
src/gui/opengl/qopenglframebufferobject.cpp
src/gui/opengl/qopenglframebufferobject.h
src/gui/opengl/qopenglframebufferobject_p.h

index 5e22554..aac6ea0 100644 (file)
@@ -313,9 +313,8 @@ bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFo
     return !(*this == other);
 }
 
-bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus() const
+bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus(QOpenGLContext *ctx) const
 {
-    QOpenGLContext *ctx = QOpenGLContext::currentContext();
     if (!ctx)
         return false;   // Context no longer exists.
     GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -402,8 +401,6 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
 
     GLuint texture = 0;
     GLuint color_buffer = 0;
-    GLuint depth_buffer = 0;
-    GLuint stencil_buffer = 0;
 
     QT_CHECK_GLERROR();
     // init texture
@@ -432,7 +429,7 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
                 target, texture, 0);
 
         QT_CHECK_GLERROR();
-        valid = checkFramebufferStatus();
+        valid = checkFramebufferStatus(ctx);
         glBindTexture(target, 0);
 
         color_buffer = 0;
@@ -458,12 +455,57 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
                                              GL_RENDERBUFFER, color_buffer);
 
         QT_CHECK_GLERROR();
-        valid = checkFramebufferStatus();
+        valid = checkFramebufferStatus(ctx);
 
         if (valid)
             funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
     }
 
+    format.setTextureTarget(target);
+    format.setSamples(int(samples));
+    format.setInternalTextureFormat(internal_format);
+    format.setMipmap(mipmap);
+
+    initAttachments(ctx, attachment);
+
+    funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
+    if (valid) {
+        fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
+        if (color_buffer)
+            color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
+        else
+            texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
+    } else {
+        if (color_buffer)
+            funcs.glDeleteRenderbuffers(1, &color_buffer);
+        else
+            glDeleteTextures(1, &texture);
+        funcs.glDeleteFramebuffers(1, &fbo);
+    }
+    QT_CHECK_GLERROR();
+}
+
+void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment)
+{
+    int samples = format.samples();
+
+    // free existing attachments
+    if (depth_buffer_guard) {
+        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
+        depth_buffer_guard->free();
+    }
+    if (stencil_buffer_guard) {
+        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+        if (stencil_buffer_guard != depth_buffer_guard)
+            stencil_buffer_guard->free();
+    }
+
+    depth_buffer_guard = 0;
+    stencil_buffer_guard = 0;
+
+    GLuint depth_buffer = 0;
+    GLuint stencil_buffer = 0;
+
     // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
     // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
     // might not be supported while separate buffers are, according to QTBUG-12861.
@@ -488,7 +530,7 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
         funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                      GL_RENDERBUFFER, stencil_buffer);
 
-        valid = checkFramebufferStatus();
+        valid = checkFramebufferStatus(ctx);
         if (!valid) {
             funcs.glDeleteRenderbuffers(1, &depth_buffer);
             stencil_buffer = depth_buffer = 0;
@@ -529,7 +571,7 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
         }
         funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                      GL_RENDERBUFFER, depth_buffer);
-        valid = checkFramebufferStatus();
+        valid = checkFramebufferStatus(ctx);
         if (!valid) {
             funcs.glDeleteRenderbuffers(1, &depth_buffer);
             depth_buffer = 0;
@@ -559,7 +601,7 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
         }
         funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                   GL_RENDERBUFFER, stencil_buffer);
-        valid = checkFramebufferStatus();
+        valid = checkFramebufferStatus(ctx);
         if (!valid) {
             funcs.glDeleteRenderbuffers(1, &stencil_buffer);
             stencil_buffer = 0;
@@ -567,7 +609,7 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
     }
 
     // The FBO might have become valid after removing the depth or stencil buffer.
-    valid = checkFramebufferStatus();
+    valid = checkFramebufferStatus(ctx);
 
     if (depth_buffer && stencil_buffer) {
         fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
@@ -577,13 +619,7 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
         fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
     }
 
-    funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
     if (valid) {
-        fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
-        if (color_buffer)
-            color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
-        else
-            texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
         if (depth_buffer)
             depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
         if (stencil_buffer) {
@@ -593,23 +629,14 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
                 stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
         }
     } else {
-        if (color_buffer)
-            funcs.glDeleteRenderbuffers(1, &color_buffer);
-        else
-            glDeleteTextures(1, &texture);
         if (depth_buffer)
             funcs.glDeleteRenderbuffers(1, &depth_buffer);
         if (stencil_buffer && depth_buffer != stencil_buffer)
             funcs.glDeleteRenderbuffers(1, &stencil_buffer);
-        funcs.glDeleteFramebuffers(1, &fbo);
     }
     QT_CHECK_GLERROR();
 
-    format.setTextureTarget(target);
-    format.setSamples(int(samples));
     format.setAttachment(fbo_attachment);
-    format.setInternalTextureFormat(internal_format);
-    format.setMipmap(mipmap);
 }
 
 /*!
@@ -861,7 +888,7 @@ bool QOpenGLFramebufferObject::bind()
         qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
 #endif
     d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
-    d->valid = d->checkFramebufferStatus();
+    d->valid = d->checkFramebufferStatus(current);
     if (d->valid && current)
         current->d_func()->current_fbo = d->fbo();
     return d->valid;
@@ -1108,6 +1135,30 @@ QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() cons
 }
 
 /*!
+    Sets the attachments of the framebuffer object.
+
+    This can be used to free or reattach the depth and stencil buffer
+    attachments as needed.
+ */
+void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
+{
+    Q_D(QOpenGLFramebufferObject);
+    if (attachment == d->fbo_attachment || !isValid())
+        return;
+    QOpenGLContext *current = QOpenGLContext::currentContext();
+    if (!current)
+        return;
+#ifdef QT_DEBUG
+    if (current->shareGroup() != d->fbo_guard->group())
+        qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
+#endif
+    d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
+    d->initAttachments(current, attachment);
+    if (current->d_func()->current_fbo != d->fbo())
+        d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
+}
+
+/*!
     Returns true if the framebuffer object is currently bound to a context,
     otherwise false is returned.
 */
index 0b2ead1..63260f1 100644 (file)
@@ -101,6 +101,8 @@ public:
     QImage toImage() const;
     Attachment attachment() const;
 
+    void setAttachment(Attachment attachment);
+
     GLuint handle() const;
 
     static bool bindDefault();
index 78d9d93..23cab8f 100644 (file)
@@ -120,7 +120,9 @@ public:
               QOpenGLFramebufferObject::Attachment attachment,
               GLenum internal_format, GLenum texture_target,
               GLint samples = 0, bool mipmap = false);
-    bool checkFramebufferStatus() const;
+    void initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment);
+
+    bool checkFramebufferStatus(QOpenGLContext *ctx) const;
     QOpenGLSharedResourceGuard *fbo_guard;
     QOpenGLSharedResourceGuard *texture_guard;
     QOpenGLSharedResourceGuard *depth_buffer_guard;