summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@digia.com>2013-12-30 17:09:12 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-06 12:57:20 +0100
commitbd5cea5ba8458ab3d34700999654dbd7d7174084 (patch)
treebb11017789766816742dab61d1b1e0d2436fea6a /src
parent63fd793fc32a8dd85a6d6ce1cfe3811ff99888f5 (diff)
Resurrect QGLWidget::renderPixmap()
This function has been completely broken since Qt 5.0. Unfortunately the autotest's verification steps were somewhat faulty so returning a null pixmap from renderPixmap() did not trigger a failure. The implementation is now done with framebuffer objects and pixel readback. This is not ideal performance-wise, but is the only option. In Qt 4 pixmaps were often backed by platform dependent native pixmaps that could be used as rendering targets for OpenGL content. This is not an option anymore since pixmaps are raster backed on all the major platforms. Task-number: QTBUG-33186 Change-Id: I8f558e33bf7967ac3be439fd5a3eac07b6444be5 Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/opengl/qgl.cpp103
-rw-r--r--src/opengl/qgl_p.h1
-rw-r--r--src/opengl/qgl_qpa.cpp3
3 files changed, 61 insertions, 46 deletions
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index b1fbe2ac71..40a8b1921c 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -2975,6 +2975,10 @@ bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *contex
Returns \c true if the paint device of this context is a pixmap;
otherwise returns \c false.
+
+ Since Qt 5 the paint device is never actually a pixmap. renderPixmap() is
+ however still simulated using framebuffer objects and readbacks, and this
+ function will return \c true in this case.
*/
/*!
@@ -3143,7 +3147,7 @@ QGLFormat QGLContext::requestedFormat() const
bool QGLContext::deviceIsPixmap() const
{
Q_D(const QGLContext);
- return d->paintDevice->devType() == QInternal::Pixmap;
+ return !d->readback_target_size.isEmpty();
}
@@ -3885,7 +3889,9 @@ void QGLWidget::setFormat(const QGLFormat &format)
void QGLWidget::updateGL()
{
- if (updatesEnabled() && testAttribute(Qt::WA_Mapped))
+ Q_D(QGLWidget);
+ const bool targetIsOffscreen = !d->glcx->d_ptr->readback_target_size.isEmpty();
+ if (updatesEnabled() && (testAttribute(Qt::WA_Mapped) || targetIsOffscreen))
glDraw();
}
@@ -4041,20 +4047,28 @@ void QGLWidget::paintEvent(QPaintEvent *)
You can use this method on both visible and invisible QGLWidget objects.
- This method will create a pixmap and a temporary QGLContext to
- render on the pixmap. It will then call initializeGL(),
- resizeGL(), and paintGL() on this context. Finally, the widget's
- original GL context is restored.
+ Internally the function renders into a framebuffer object and performs pixel
+ readback. This has a performance penalty, meaning that this function is not
+ suitable to be called at a high frequency.
+
+ After creating and binding the framebuffer object, the function will call
+ initializeGL(), resizeGL(), and paintGL(). On the next normal update
+ initializeGL() and resizeGL() will be triggered again since the size of the
+ destination pixmap and the QGLWidget's size may differ.
- The size of the pixmap will be \a w pixels wide and \a h pixels
- high unless one of these parameters is 0 (the default), in which
- case the pixmap will have the same size as the widget.
+ The size of the pixmap will be \a w pixels wide and \a h pixels high unless
+ one of these parameters is 0 (the default), in which case the pixmap will
+ have the same size as the widget.
- If \a useContext is true, this method will try to be more
- efficient by using the existing GL context to render the pixmap.
- The default is false. Only use true if you understand the risks.
- Note that under Windows a temporary context has to be created
- and usage of the \e useContext parameter is not supported.
+ Care must be taken when using framebuffer objects in paintGL() in
+ combination with this function. To switch back to the default framebuffer,
+ use QGLFramebufferObject::bindDefault(). Binding FBO 0 is wrong since
+ renderPixmap() uses a custom framebuffer instead of the one provided by the
+ windowing system.
+
+ \a useContext is ignored. Historically this parameter enabled the usage of
+ the existing GL context. This is not supported anymore since additional
+ contexts are never created.
Overlays are not rendered onto the pixmap.
@@ -4069,43 +4083,31 @@ void QGLWidget::paintEvent(QPaintEvent *)
QPixmap QGLWidget::renderPixmap(int w, int h, bool useContext)
{
+ Q_UNUSED(useContext);
Q_D(QGLWidget);
+
QSize sz = size();
if ((w > 0) && (h > 0))
sz = QSize(w, h);
- QPixmap pm(sz);
-
- d->glcx->doneCurrent();
-
- bool success = true;
-
- if (useContext && isValid() && d->renderCxPm(&pm))
- return pm;
-
- QGLFormat fmt = d->glcx->requestedFormat();
- fmt.setDirectRendering(false); // Direct is unlikely to work
- fmt.setDoubleBuffer(false); // We don't need dbl buf
-
- QGLContext* ocx = d->glcx;
- ocx->doneCurrent();
- d->glcx = new QGLContext(fmt, &pm);
- d->glcx->create();
-
- if (d->glcx->isValid())
+ QPixmap pm;
+ if (d->glcx->isValid()) {
+ d->glcx->makeCurrent();
+ QGLFramebufferObject fbo(sz, QGLFramebufferObject::CombinedDepthStencil);
+ fbo.bind();
+ d->glcx->setInitialized(false);
+ uint prevDefaultFbo = d->glcx->d_ptr->default_fbo;
+ d->glcx->d_ptr->default_fbo = fbo.handle();
+ d->glcx->d_ptr->readback_target_size = sz;
updateGL();
- else
- success = false;
-
- delete d->glcx;
- d->glcx = ocx;
-
- ocx->makeCurrent();
-
- if (success) {
- return pm;
+ fbo.release();
+ pm = QPixmap::fromImage(fbo.toImage());
+ d->glcx->d_ptr->default_fbo = prevDefaultFbo;
+ d->glcx->setInitialized(false);
+ d->glcx->d_ptr->readback_target_size = QSize();
}
- return QPixmap();
+
+ return pm;
}
/*!
@@ -4164,14 +4166,23 @@ void QGLWidget::glDraw()
if (d->glcx->deviceIsPixmap())
glDrawBuffer(GL_FRONT);
#endif
+ QSize readback_target_size = d->glcx->d_ptr->readback_target_size;
if (!d->glcx->initialized()) {
glInit();
const qreal scaleFactor = (window() && window()->windowHandle()) ?
window()->windowHandle()->devicePixelRatio() : 1.0;
- resizeGL(d->glcx->device()->width() * scaleFactor, d->glcx->device()->height() * scaleFactor); // New context needs this "resize"
+ int w, h;
+ if (readback_target_size.isEmpty()) {
+ w = d->glcx->device()->width() * scaleFactor;
+ h = d->glcx->device()->height() * scaleFactor;
+ } else {
+ w = readback_target_size.width();
+ h = readback_target_size.height();
+ }
+ resizeGL(w, h); // New context needs this "resize"
}
paintGL();
- if (doubleBuffer()) {
+ if (doubleBuffer() && readback_target_size.isEmpty()) {
if (d->autoSwap)
swapBuffers();
} else {
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
index 484c3ea2d9..22fc3f4ad0 100644
--- a/src/opengl/qgl_p.h
+++ b/src/opengl/qgl_p.h
@@ -270,6 +270,7 @@ public:
uint workaround_brokenAlphaTexSubImage_init : 1;
QPaintDevice *paintDevice;
+ QSize readback_target_size;
QColor transpColor;
QGLContext *q_ptr;
QGLFormat::OpenGLVersionFlags version_flags;
diff --git a/src/opengl/qgl_qpa.cpp b/src/opengl/qgl_qpa.cpp
index 6e698bf939..8b66c891bb 100644
--- a/src/opengl/qgl_qpa.cpp
+++ b/src/opengl/qgl_qpa.cpp
@@ -138,6 +138,9 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
{
Q_D(QGLContext);
if(!d->paintDevice || d->paintDevice->devType() != QInternal::Widget) {
+ // Unlike in Qt 4, the only possible target is a widget backed by an OpenGL-based
+ // QWindow. Pixmaps in particular are not supported anymore as paint devices since
+ // starting from Qt 5 QPixmap is raster-backed on almost all platforms.
d->valid = false;
}else {
QWidget *widget = static_cast<QWidget *>(d->paintDevice);