From b36ece3ff4ee516eab8f0f34c8e7f54d2fcee311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 14 Feb 2012 19:43:46 +0100 Subject: Added support for resetting QOpenGLFramebufferObject attachments. 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 --- src/gui/opengl/qopenglframebufferobject.cpp | 103 +++++++++++++++++++++------- src/gui/opengl/qopenglframebufferobject.h | 2 + src/gui/opengl/qopenglframebufferobject_p.h | 4 +- 3 files changed, 82 insertions(+), 27 deletions(-) (limited to 'src/gui/opengl') diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 5e22554303..aac6ea0558 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -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; @@ -1107,6 +1134,30 @@ QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() cons return NoAttachment; } +/*! + 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. diff --git a/src/gui/opengl/qopenglframebufferobject.h b/src/gui/opengl/qopenglframebufferobject.h index 0b2ead1cbb..63260f1940 100644 --- a/src/gui/opengl/qopenglframebufferobject.h +++ b/src/gui/opengl/qopenglframebufferobject.h @@ -101,6 +101,8 @@ public: QImage toImage() const; Attachment attachment() const; + void setAttachment(Attachment attachment); + GLuint handle() const; static bool bindDefault(); diff --git a/src/gui/opengl/qopenglframebufferobject_p.h b/src/gui/opengl/qopenglframebufferobject_p.h index 78d9d93ffe..23cab8f0af 100644 --- a/src/gui/opengl/qopenglframebufferobject_p.h +++ b/src/gui/opengl/qopenglframebufferobject_p.h @@ -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; -- cgit v1.2.3