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.glGenFramebuffers(1, &fbo);
|
||||||
funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
|
||||||
GLuint texture = 0;
|
|
||||||
GLuint color_buffer = 0;
|
GLuint color_buffer = 0;
|
||||||
|
|
||||||
QT_CHECK_GLERROR();
|
QT_CHECK_GLERROR();
|
||||||
// init texture
|
// init texture
|
||||||
if (samples == 0) {
|
if (samples == 0) {
|
||||||
glGenTextures(1, &texture);
|
initTexture(texture_target, internal_format, size, mipmap);
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
mipmap = false;
|
mipmap = false;
|
||||||
funcs.glGenRenderbuffers(1, &color_buffer);
|
funcs.glGenRenderbuffers(1, &color_buffer);
|
||||||
@ -492,8 +462,10 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
|
|||||||
QT_CHECK_GLERROR();
|
QT_CHECK_GLERROR();
|
||||||
valid = checkFramebufferStatus(ctx);
|
valid = checkFramebufferStatus(ctx);
|
||||||
|
|
||||||
if (valid)
|
if (valid) {
|
||||||
funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
||||||
|
color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format.setTextureTarget(target);
|
format.setTextureTarget(target);
|
||||||
@ -506,20 +478,59 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
|
|||||||
funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
|
funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
|
||||||
if (valid) {
|
if (valid) {
|
||||||
fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
|
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 {
|
} else {
|
||||||
if (color_buffer)
|
if (color_buffer_guard) {
|
||||||
funcs.glDeleteRenderbuffers(1, &color_buffer);
|
color_buffer_guard->free();
|
||||||
else
|
color_buffer_guard = 0;
|
||||||
glDeleteTextures(1, &texture);
|
} else if (texture_guard) {
|
||||||
|
texture_guard->free();
|
||||||
|
texture_guard = 0;
|
||||||
|
}
|
||||||
funcs.glDeleteFramebuffers(1, &fbo);
|
funcs.glDeleteFramebuffers(1, &fbo);
|
||||||
}
|
}
|
||||||
QT_CHECK_GLERROR();
|
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)
|
void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment)
|
||||||
{
|
{
|
||||||
int samples = format.samples();
|
int samples = format.samples();
|
||||||
@ -905,6 +916,10 @@ bool QOpenGLFramebufferObject::isValid() const
|
|||||||
framebuffer to this framebuffer object.
|
framebuffer to this framebuffer object.
|
||||||
Returns \c true upon success, false otherwise.
|
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()
|
\sa release()
|
||||||
*/
|
*/
|
||||||
bool QOpenGLFramebufferObject::bind()
|
bool QOpenGLFramebufferObject::bind()
|
||||||
@ -920,7 +935,10 @@ bool QOpenGLFramebufferObject::bind()
|
|||||||
qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
|
qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
|
||||||
#endif
|
#endif
|
||||||
d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
|
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)
|
if (d->valid && current)
|
||||||
current->d_func()->current_fbo = d->fbo();
|
current->d_func()->current_fbo = d->fbo();
|
||||||
return d->valid;
|
return d->valid;
|
||||||
@ -967,6 +985,8 @@ bool QOpenGLFramebufferObject::release()
|
|||||||
|
|
||||||
If a multisample framebuffer object is used then the value returned
|
If a multisample framebuffer object is used then the value returned
|
||||||
from this function will be invalid.
|
from this function will be invalid.
|
||||||
|
|
||||||
|
\sa takeTexture()
|
||||||
*/
|
*/
|
||||||
GLuint QOpenGLFramebufferObject::texture() const
|
GLuint QOpenGLFramebufferObject::texture() const
|
||||||
{
|
{
|
||||||
@ -974,6 +994,40 @@ GLuint QOpenGLFramebufferObject::texture() const
|
|||||||
return d->texture_guard ? d->texture_guard->id() : 0;
|
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
|
\fn QSize QOpenGLFramebufferObject::size() const
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ public:
|
|||||||
int height() const { return size().height(); }
|
int height() const { return size().height(); }
|
||||||
|
|
||||||
GLuint texture() const;
|
GLuint texture() const;
|
||||||
|
GLuint takeTexture();
|
||||||
QSize size() const;
|
QSize size() const;
|
||||||
QImage toImage() const;
|
QImage toImage() const;
|
||||||
Attachment attachment() const;
|
Attachment attachment() const;
|
||||||
|
@ -116,6 +116,7 @@ public:
|
|||||||
QOpenGLFramebufferObject::Attachment attachment,
|
QOpenGLFramebufferObject::Attachment attachment,
|
||||||
GLenum internal_format, GLenum texture_target,
|
GLenum internal_format, GLenum texture_target,
|
||||||
GLint samples = 0, bool mipmap = false);
|
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);
|
void initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment);
|
||||||
|
|
||||||
bool checkFramebufferStatus(QOpenGLContext *ctx) const;
|
bool checkFramebufferStatus(QOpenGLContext *ctx) const;
|
||||||
|
@ -66,6 +66,8 @@ private slots:
|
|||||||
void multiGroupSharedResourceCleanupCustom();
|
void multiGroupSharedResourceCleanupCustom();
|
||||||
void fboSimpleRendering_data();
|
void fboSimpleRendering_data();
|
||||||
void fboSimpleRendering();
|
void fboSimpleRendering();
|
||||||
|
void fboTextureOwnership_data();
|
||||||
|
void fboTextureOwnership();
|
||||||
void fboRendering_data();
|
void fboRendering_data();
|
||||||
void fboRendering();
|
void fboRendering();
|
||||||
void fboHandleNulledAfterContextDestroyed();
|
void fboHandleNulledAfterContextDestroyed();
|
||||||
@ -429,6 +431,54 @@ void tst_QOpenGL::fboSimpleRendering()
|
|||||||
delete fbo;
|
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()
|
void tst_QOpenGL::fboRendering_data()
|
||||||
{
|
{
|
||||||
common_data();
|
common_data();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user