From dcbb16a45212d263496df1e5875da6205e4f5f53 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 30 Dec 2013 14:37:22 +0100 Subject: Add takeTexture() to QOpenGLFramebufferObject Add an API that allows to retrieve and detach the texture from the framebuffer object. The next bind() call will then create and attach a new texture. [ChangeLog][QtGui][QOpenGLFramebufferObject] Added takeTexture() for retrieving and detaching the texture from the framebuffer object. Task-number: QTBUG-35881 Change-Id: I2cca37f5872c1685b1238047f8b912e6534ab781 Reviewed-by: Gunnar Sletta --- src/gui/opengl/qopenglframebufferobject.cpp | 136 +++++++++++++++++++--------- src/gui/opengl/qopenglframebufferobject.h | 1 + src/gui/opengl/qopenglframebufferobject_p.h | 1 + 3 files changed, 97 insertions(+), 41 deletions(-) (limited to 'src/gui') diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index c06ba40b47..d5de8b009a 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -446,42 +446,12 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi funcs.glGenFramebuffers(1, &fbo); funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo); - GLuint texture = 0; GLuint color_buffer = 0; QT_CHECK_GLERROR(); // init texture if (samples == 0) { - glGenTextures(1, &texture); - glBindTexture(target, texture); - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - if (mipmap) { - int width = size.width(); - int height = size.height(); - int level = 0; - while (width > 1 || height > 1) { - width = qMax(1, width >> 1); - height = qMax(1, height >> 1); - ++level; - glTexImage2D(target, level, internal_format, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - } - } - funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - target, texture, 0); - - QT_CHECK_GLERROR(); - valid = checkFramebufferStatus(ctx); - glBindTexture(target, 0); - - color_buffer = 0; + initTexture(texture_target, internal_format, size, mipmap); } else { mipmap = false; funcs.glGenRenderbuffers(1, &color_buffer); @@ -492,8 +462,10 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi QT_CHECK_GLERROR(); valid = checkFramebufferStatus(ctx); - if (valid) + if (valid) { funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); + } } format.setTextureTarget(target); @@ -506,20 +478,59 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi 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); + if (color_buffer_guard) { + color_buffer_guard->free(); + color_buffer_guard = 0; + } else if (texture_guard) { + texture_guard->free(); + texture_guard = 0; + } funcs.glDeleteFramebuffers(1, &fbo); } QT_CHECK_GLERROR(); } +void QOpenGLFramebufferObjectPrivate::initTexture(GLenum target, GLenum internal_format, + const QSize &size, bool mipmap) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + GLuint texture = 0; + + glGenTextures(1, &texture); + glBindTexture(target, texture); + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (mipmap) { + int width = size.width(); + int height = size.height(); + int level = 0; + while (width > 1 || height > 1) { + width = qMax(1, width >> 1); + height = qMax(1, height >> 1); + ++level; + glTexImage2D(target, level, internal_format, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + } + funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + target, texture, 0); + + QT_CHECK_GLERROR(); + glBindTexture(target, 0); + valid = checkFramebufferStatus(ctx); + if (valid) + texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc); + else + glDeleteTextures(1, &texture); +} + void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment) { int samples = format.samples(); @@ -905,6 +916,10 @@ bool QOpenGLFramebufferObject::isValid() const framebuffer to this framebuffer object. Returns \c true upon success, false otherwise. + \note If takeTexture() was called, a new texture is created and associated + with the framebuffer object. This is potentially expensive and changes the + context state (the currently bound texture). + \sa release() */ bool QOpenGLFramebufferObject::bind() @@ -920,7 +935,10 @@ bool QOpenGLFramebufferObject::bind() qWarning("QOpenGLFramebufferObject::bind() called from incompatible context"); #endif d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); - d->valid = d->checkFramebufferStatus(current); + if (d->texture_guard || d->format.samples() != 0) + d->valid = d->checkFramebufferStatus(current); + else + d->initTexture(d->format.textureTarget(), d->format.internalTextureFormat(), d->size, d->format.mipmap()); if (d->valid && current) current->d_func()->current_fbo = d->fbo(); return d->valid; @@ -967,6 +985,8 @@ bool QOpenGLFramebufferObject::release() If a multisample framebuffer object is used then the value returned from this function will be invalid. + + \sa takeTexture() */ GLuint QOpenGLFramebufferObject::texture() const { @@ -974,6 +994,40 @@ GLuint QOpenGLFramebufferObject::texture() const return d->texture_guard ? d->texture_guard->id() : 0; } +/*! + \fn GLuint QOpenGLFramebufferObject::takeTexture() + + Returns the texture id for the texture attached to this framebuffer + object. The ownership of the texture is transferred to the caller. + + If the framebuffer object is currently bound, an implicit release() + will be done. During the next call to bind() a new texture will be + created. + + If a multisample framebuffer object is used, then there is no + texture and the return value from this function will be invalid. + Similarly, incomplete framebuffer objects will also return 0. + + \since 5.3 + + \sa texture(), bind(), release() + */ +GLuint QOpenGLFramebufferObject::takeTexture() +{ + Q_D(QOpenGLFramebufferObject); + GLuint id = 0; + if (isValid() && d->texture_guard) { + QOpenGLContext *current = QOpenGLContext::currentContext(); + if (current && current->shareGroup() == d->fbo_guard->group() && current->d_func()->current_fbo == d->fbo()) + release(); + id = d->texture_guard->id(); + // Do not call free() on texture_guard, just null it out. + // This way the texture will not be deleted when the guard is destroyed. + d->texture_guard = 0; + } + return id; +} + /*! \fn QSize QOpenGLFramebufferObject::size() const diff --git a/src/gui/opengl/qopenglframebufferobject.h b/src/gui/opengl/qopenglframebufferobject.h index 215d3701ca..a431618f6d 100644 --- a/src/gui/opengl/qopenglframebufferobject.h +++ b/src/gui/opengl/qopenglframebufferobject.h @@ -97,6 +97,7 @@ public: int height() const { return size().height(); } GLuint texture() const; + GLuint takeTexture(); QSize size() const; QImage toImage() const; Attachment attachment() const; diff --git a/src/gui/opengl/qopenglframebufferobject_p.h b/src/gui/opengl/qopenglframebufferobject_p.h index c8c69c4b3e..1c7d2e1e5d 100644 --- a/src/gui/opengl/qopenglframebufferobject_p.h +++ b/src/gui/opengl/qopenglframebufferobject_p.h @@ -116,6 +116,7 @@ public: QOpenGLFramebufferObject::Attachment attachment, GLenum internal_format, GLenum texture_target, GLint samples = 0, bool mipmap = false); + void initTexture(GLenum target, GLenum internal_format, const QSize &size, bool mipmap); void initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment); bool checkFramebufferStatus(QOpenGLContext *ctx) const; -- cgit v1.2.3