diff options
Diffstat (limited to 'src/gui/opengl/qopenglframebufferobject.cpp')
-rw-r--r-- | src/gui/opengl/qopenglframebufferobject.cpp | 228 |
1 files changed, 149 insertions, 79 deletions
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 14d7fac9f7..9953b4e889 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -72,8 +72,6 @@ QT_BEGIN_NAMESPACE #define QT_CHECK_GLERROR() {} #endif -// ####TODO Properly #ifdef this class to use #define symbols actually defined -// by OpenGL/ES includes #ifndef GL_MAX_SAMPLES #define GL_MAX_SAMPLES 0x8D57 #endif @@ -448,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); @@ -494,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); @@ -508,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(); @@ -581,30 +590,29 @@ void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpen funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { -#ifdef QT_OPENGL_ES - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT24, size.width(), size.height()); + if (QOpenGLFunctions::isES()) { + if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_DEPTH_COMPONENT24, size.width(), size.height()); + else + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_DEPTH_COMPONENT16, size.width(), size.height()); } else { funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT16, size.width(), size.height()); + GL_DEPTH_COMPONENT, size.width(), size.height()); } -#else - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT, size.width(), size.height()); -#endif } else { -#ifdef QT_OPENGL_ES - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, - size.width(), size.height()); + if (QOpenGLFunctions::isES()) { + if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, + size.width(), size.height()); + } else { + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, + size.width(), size.height()); + } } else { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, - size.width(), size.height()); + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); } -#else - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); -#endif } funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buffer); @@ -619,23 +627,18 @@ void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpen funcs.glGenRenderbuffers(1, &stencil_buffer); funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer); Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer)); - if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { -#ifdef QT_OPENGL_ES - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_STENCIL_INDEX8, size.width(), size.height()); -#else - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_STENCIL_INDEX, size.width(), size.height()); -#endif - } else { + #ifdef QT_OPENGL_ES - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, - size.width(), size.height()); + GLenum storage = GL_STENCIL_INDEX8; #else - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX, - size.width(), size.height()); + GLenum storage = QOpenGLFunctions::isES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX; #endif - } + + if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, size.width(), size.height()); + else + funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, size.width(), size.height()); + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil_buffer); valid = checkFramebufferStatus(ctx); @@ -768,7 +771,13 @@ QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum tar : d_ptr(new QOpenGLFramebufferObjectPrivate) { Q_D(QOpenGLFramebufferObject); - d->init(this, size, NoAttachment, target, DEFAULT_FORMAT); + d->init(this, size, NoAttachment, target, +#ifndef QT_OPENGL_ES_2 + QOpenGLFunctions::isES() ? GL_RGBA : GL_RGBA8 +#else + GL_RGBA +#endif + ); } /*! \overload @@ -782,7 +791,13 @@ QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum : d_ptr(new QOpenGLFramebufferObjectPrivate) { Q_D(QOpenGLFramebufferObject); - d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT); + d->init(this, QSize(width, height), NoAttachment, target, +#ifndef QT_OPENGL_ES_2 + QOpenGLFunctions::isES() ? GL_RGBA : GL_RGBA8 +#else + GL_RGBA +#endif + ); } /*! \overload @@ -831,6 +846,12 @@ QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attach : d_ptr(new QOpenGLFramebufferObjectPrivate) { Q_D(QOpenGLFramebufferObject); + if (!internal_format) +#ifdef QT_OPENGL_ES_2 + internal_format = GL_RGBA; +#else + internal_format = QOpenGLFunctions::isES() ? GL_RGBA : GL_RGBA8; +#endif d->init(this, QSize(width, height), attachment, target, internal_format); } @@ -852,6 +873,12 @@ QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment : d_ptr(new QOpenGLFramebufferObjectPrivate) { Q_D(QOpenGLFramebufferObject); + if (!internal_format) +#ifdef QT_OPENGL_ES_2 + internal_format = GL_RGBA; +#else + internal_format = QOpenGLFunctions::isES() ? GL_RGBA : GL_RGBA8; +#endif d->init(this, size, attachment, target, internal_format); } @@ -907,12 +934,16 @@ 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() { if (!isValid()) - return false; + return false; Q_D(QOpenGLFramebufferObject); QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) @@ -922,7 +953,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; @@ -940,7 +974,7 @@ bool QOpenGLFramebufferObject::bind() bool QOpenGLFramebufferObject::release() { if (!isValid()) - return false; + return false; QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) @@ -969,6 +1003,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 { @@ -977,6 +1013,40 @@ GLuint QOpenGLFramebufferObject::texture() const } /*! + \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 Returns the size of the texture attached to this framebuffer |