summaryrefslogtreecommitdiffstats
path: root/src/gui/opengl/qopenglframebufferobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/opengl/qopenglframebufferobject.cpp')
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp108
1 files changed, 65 insertions, 43 deletions
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp
index 3102e1ecd2..97b006b4fc 100644
--- a/src/gui/opengl/qopenglframebufferobject.cpp
+++ b/src/gui/opengl/qopenglframebufferobject.cpp
@@ -88,6 +88,10 @@ QT_BEGIN_NAMESPACE
#define GL_DEPTH_COMPONENT24 0x81A6
#endif
+#ifndef GL_DEPTH_COMPONENT24_OES
+#define GL_DEPTH_COMPONENT24_OES 0x81A6
+#endif
+
#ifndef GL_READ_FRAMEBUFFER
#define GL_READ_FRAMEBUFFER 0x8CA8
#endif
@@ -423,18 +427,21 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
return;
-
// Fall back to using a normal non-msaa FBO if we don't have support for MSAA
if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
|| !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) {
samples = 0;
}
-#ifndef QT_OPENGL_ES_2
- GLint maxSamples;
- funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
- samples = qBound(0, int(samples), int(maxSamples));
-#endif
+ // On GLES 2.0 multisampled framebuffers are available through vendor-specific extensions
+ const bool msaaES2 = ctx->isOpenGLES() && (ctx->hasExtension("GL_ANGLE_framebuffer_multisample")
+ || ctx->hasExtension("GL_NV_framebuffer_multisample"));
+
+ if (!ctx->isOpenGLES() || msaaES2) {
+ GLint maxSamples;
+ funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+ samples = qBound(0, int(samples), int(maxSamples));
+ }
samples = qMax(0, samples);
requestedSamples = samples;
@@ -456,11 +463,9 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
} else {
GLenum storageFormat = internal_format;
#ifdef GL_RGBA8_OES
- // Correct the internal format used by the render buffer when using ANGLE
- if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES && internal_format == GL_RGBA
- && strstr((const char *)funcs.glGetString(GL_RENDERER), "ANGLE") != 0) {
+ // Correct the internal format used by the render buffer when using ES with extensions
+ if (msaaES2 && internal_format == GL_RGBA)
storageFormat = GL_RGBA8_OES;
- }
#endif
mipmap = false;
@@ -488,7 +493,6 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi
initAttachments(ctx, attachment);
- funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
if (valid) {
fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
} else {
@@ -975,8 +979,6 @@ bool QOpenGLFramebufferObject::bind()
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;
}
@@ -1004,10 +1006,8 @@ bool QOpenGLFramebufferObject::release()
qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
#endif
- if (current) {
- current->d_func()->current_fbo = current->defaultFramebufferObject();
- d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
- }
+ if (current)
+ d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->defaultFramebufferObject());
return true;
}
@@ -1054,7 +1054,7 @@ GLuint QOpenGLFramebufferObject::takeTexture()
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())
+ if (current && current->shareGroup() == d->fbo_guard->group() && isBound())
release();
id = d->texture_guard->id();
// Do not call free() on texture_guard, just null it out.
@@ -1131,6 +1131,14 @@ Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format,
Will try to return a premultiplied ARBG32 or RGB32 image. Since 5.2 it will fall back to
a premultiplied RGBA8888 or RGBx8888 image when reading to ARGB32 is not supported.
+
+ For multisampled framebuffer objects the samples are resolved using the
+ \c{GL_EXT_framebuffer_blit} extension. If the extension is not available, the contents
+ of the returned image is undefined.
+
+ For singlesampled framebuffers the contents is retrieved via \c glReadPixels. This is
+ a potentially expensive and inefficient operation. Therefore it is recommended that
+ this function is used as seldom as possible.
*/
QImage QOpenGLFramebufferObject::toImage() const
{
@@ -1138,6 +1146,19 @@ QImage QOpenGLFramebufferObject::toImage() const
if (!d->valid)
return QImage();
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (!ctx) {
+ qWarning("QOpenGLFramebufferObject::toImage() called without a current context");
+ return QImage();
+ }
+
+ GLuint prevFbo = 0;
+ ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
+
+ if (prevFbo != d->fbo())
+ const_cast<QOpenGLFramebufferObject *>(this)->bind();
+
+ QImage image;
// qt_gl_read_framebuffer doesn't work on a multisample FBO
if (format().samples() != 0) {
QOpenGLFramebufferObject temp(size(), QOpenGLFramebufferObjectFormat());
@@ -1145,15 +1166,13 @@ QImage QOpenGLFramebufferObject::toImage() const
QRect rect(QPoint(0, 0), size());
blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect);
- return temp.toImage();
+ image = temp.toImage();
+ } else {
+ image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
}
- bool wasBound = isBound();
- if (!wasBound)
- const_cast<QOpenGLFramebufferObject *>(this)->bind();
- QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
- if (!wasBound)
- const_cast<QOpenGLFramebufferObject *>(this)->release();
+ if (prevFbo != d->fbo())
+ ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
return image;
}
@@ -1170,16 +1189,13 @@ QImage QOpenGLFramebufferObject::toImage() const
bool QOpenGLFramebufferObject::bindDefault()
{
QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
- QOpenGLFunctions functions(ctx);
- if (ctx) {
- ctx->d_func()->current_fbo = ctx->defaultFramebufferObject();
- functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
+ if (ctx)
+ ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
#ifdef QT_DEBUG
- } else {
+ else
qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
#endif
- }
return ctx != 0;
}
@@ -1192,7 +1208,7 @@ bool QOpenGLFramebufferObject::bindDefault()
*/
bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
{
- return QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
+ return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
}
/*!
@@ -1228,6 +1244,8 @@ QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() cons
This can be used to free or reattach the depth and stencil buffer
attachments as needed.
+
+ \note This function alters the current framebuffer binding.
*/
void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
{
@@ -1243,20 +1261,21 @@ void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachmen
#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 \c true if the framebuffer object is currently bound to a context,
+ Returns \c true if the framebuffer object is currently bound to the current context,
otherwise false is returned.
*/
-
bool QOpenGLFramebufferObject::isBound() const
{
Q_D(const QOpenGLFramebufferObject);
- QOpenGLContext *current = QOpenGLContext::currentContext();
- return current ? current->d_func()->current_fbo == d->fbo() : false;
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (!ctx)
+ return false;
+ GLint fbo = 0;
+ ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
+ return GLuint(fbo) == d->fbo();
}
/*!
@@ -1310,6 +1329,9 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
If \a source or \a target is 0, the default framebuffer will be used
instead of a framebuffer object as source or target respectively.
+ This function will have no effect unless hasOpenGLFramebufferBlit() returns
+ true.
+
The \a buffers parameter should be a mask consisting of any combination of
\c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
\c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
@@ -1326,10 +1348,7 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
have different sizes. The sizes must also be the same if any of the
framebuffer objects are multisample framebuffers.
- Note that the scissor test will restrict the blit area if enabled.
-
- This function will have no effect unless hasOpenGLFramebufferBlit() returns
- true.
+ \note The scissor test will restrict the blit area if enabled.
\sa hasOpenGLFramebufferBlit()
*/
@@ -1346,6 +1365,9 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
return;
+ GLuint prevFbo = 0;
+ ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
+
const int sx0 = sourceRect.left();
const int sx1 = sourceRect.left() + sourceRect.width();
const int sy0 = sourceRect.top();
@@ -1363,7 +1385,7 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
tx0, ty0, tx1, ty1,
buffers, filter);
- extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
+ ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW
}
QT_END_NAMESPACE