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 <gunnar.sletta@jollamobile.com>
This commit is contained in:
parent
b12b1ddf48
commit
dcbb16a452
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -66,6 +66,8 @@ private slots:
|
||||
void multiGroupSharedResourceCleanupCustom();
|
||||
void fboSimpleRendering_data();
|
||||
void fboSimpleRendering();
|
||||
void fboTextureOwnership_data();
|
||||
void fboTextureOwnership();
|
||||
void fboRendering_data();
|
||||
void fboRendering();
|
||||
void fboHandleNulledAfterContextDestroyed();
|
||||
@ -429,6 +431,54 @@ void tst_QOpenGL::fboSimpleRendering()
|
||||
delete fbo;
|
||||
}
|
||||
|
||||
void tst_QOpenGL::fboTextureOwnership_data()
|
||||
{
|
||||
common_data();
|
||||
}
|
||||
|
||||
void tst_QOpenGL::fboTextureOwnership()
|
||||
{
|
||||
QFETCH(int, surfaceClass);
|
||||
QScopedPointer<QSurface> surface(createSurface(surfaceClass));
|
||||
|
||||
QOpenGLContext ctx;
|
||||
QVERIFY(ctx.create());
|
||||
|
||||
ctx.makeCurrent(surface.data());
|
||||
|
||||
if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
|
||||
QSKIP("QOpenGLFramebufferObject not supported on this platform");
|
||||
|
||||
QOpenGLFramebufferObjectFormat fboFormat;
|
||||
fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment);
|
||||
|
||||
QOpenGLFramebufferObject *fbo = new QOpenGLFramebufferObject(200, 100, fboFormat);
|
||||
QVERIFY(fbo->texture() != 0);
|
||||
fbo->bind();
|
||||
|
||||
// pull out the texture
|
||||
GLuint texture = fbo->takeTexture();
|
||||
QVERIFY(texture != 0);
|
||||
QVERIFY(fbo->texture() == 0);
|
||||
|
||||
// verify that the next bind() creates a new texture
|
||||
fbo->bind();
|
||||
QVERIFY(fbo->texture() != 0 && fbo->texture() != texture);
|
||||
|
||||
glClearColor(1.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glFinish();
|
||||
|
||||
QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
|
||||
QImage reference(fb.size(), QImage::Format_RGB32);
|
||||
reference.fill(0xffff0000);
|
||||
|
||||
QFUZZY_COMPARE_IMAGES(fb, reference);
|
||||
|
||||
glDeleteTextures(1, &texture);
|
||||
delete fbo;
|
||||
}
|
||||
|
||||
void tst_QOpenGL::fboRendering_data()
|
||||
{
|
||||
common_data();
|
||||
|
Loading…
Reference in New Issue
Block a user