summaryrefslogtreecommitdiffstats
path: root/src/widgets/kernel/qopenglwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/kernel/qopenglwidget.cpp')
-rw-r--r--src/widgets/kernel/qopenglwidget.cpp167
1 files changed, 147 insertions, 20 deletions
diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp
index 12e054626c..d4d23604a3 100644
--- a/src/widgets/kernel/qopenglwidget.cpp
+++ b/src/widgets/kernel/qopenglwidget.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtWidgets module of the Qt Toolkit.
**
@@ -10,9 +10,9 @@
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -23,8 +23,8 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
@@ -46,6 +46,7 @@
#include <QtGui/private/qopenglextensions_p.h>
#include <QtGui/private/qfont_p.h>
#include <QtGui/private/qopenglpaintdevice_p.h>
+#include <QtGui/private/qopenglcontext_p.h>
#include <QtWidgets/private/qwidget_p.h>
QT_BEGIN_NAMESPACE
@@ -420,10 +421,42 @@ QT_BEGIN_NAMESPACE
the intention is to have a semi-transparent window. In that case the
traditional approach of setting Qt::WA_TranslucentBackground is sufficient.
+ QOpenGLWidget supports multiple update behaviors, just like QOpenGLWindow. In
+ preserved mode the rendered content from the previous paintGL() call is
+ available in the next one, allowing incremental rendering. In non-preserved
+ mode the content is lost and paintGL() implementations are expected to redraw
+ everything in the view.
+
+ Before Qt 5.5 the default behavior of QOpenGLWidget was to preserve the
+ rendered contents between paintGL() calls. Since Qt 5.5 the default behavior
+ is non-preserved because this provides better performance and the majority of
+ applications have no need for the previous content. This also resembles the
+ semantics of an OpenGL-based QWindow and matches the default behavior of
+ QOpenGLWindow in that the color and ancillary buffers are invalidated for
+ each frame. To restore the preserved behavior, call setUpdateBehavior() with
+ \c PartialUpdate.
+
+ \section1 Alternatives
+
+ Adding a QOpenGLWidget into a window turns on OpenGL-based
+ compositing for the entire window. In some special cases this may
+ not be ideal, and the old QGLWidget-style behavior with a separate,
+ native child window is desired. Desktop applications that understand
+ the limitations of this approach (for example when it comes to
+ overlaps, transparency, scroll views and MDI areas), can use
+ QOpenGLWindow with QWidget::createWindowContainer(). This is a
+ modern alternative to QGLWidget and is faster than QOpenGLWidget due
+ to the lack of the additional composition step. It is strongly
+ recommended to limit the usage of this approach to cases where there
+ is no other choice. Note that this option is not suitable for most
+ embedded and mobile platforms, and it is known to have issues on
+ certain desktop platforms (e.g. OS X) too. The stable,
+ cross-platform solution is always QOpenGLWidget.
+
\e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
countries.}
- \sa QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts
+ \sa QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts, UpdateBehavior
*/
/*!
@@ -455,6 +488,30 @@ QT_BEGIN_NAMESPACE
due to resizing the widget.
*/
+/*!
+ \enum QOpenGLWidget::UpdateBehavior
+ \since 5.5
+
+ This enum describes the update semantics of QOpenGLWidget.
+
+ \value NoPartialUpdate QOpenGLWidget will discard the
+ contents of the color buffer and the ancillary buffers after the
+ QOpenGLWidget is rendered to screen. This is the same behavior that can be
+ expected by calling QOpenGLContext::swapBuffers with a default opengl
+ enabled QWindow as the argument. NoPartialUpdate can have some performance
+ benefits on certain hardware architectures common in the mobile and
+ embedded space when a framebuffer object is used as the rendering target.
+ The framebuffer object is invalidated between frames with
+ glDiscardFramebufferEXT if supported or a glClear. Please see the
+ documentation of EXT_discard_framebuffer for more information:
+ https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt
+
+ \value PartialUpdate The framebuffer objects color buffer and ancillary
+ buffers are not invalidated between frames.
+
+ \sa updateBehavior(), setUpdateBehavior()
+*/
+
class QOpenGLWidgetPaintDevicePrivate : public QOpenGLPaintDevicePrivate
{
public:
@@ -486,9 +543,11 @@ public:
surface(0),
initialized(false),
fakeHidden(false),
- paintDevice(0),
inBackingStorePaint(false),
- flushPending(false)
+ hasBeenComposed(false),
+ flushPending(false),
+ paintDevice(0),
+ updateBehavior(QOpenGLWidget::NoPartialUpdate)
{
requestedFormat = QSurfaceFormat::defaultFormat();
}
@@ -507,6 +566,8 @@ public:
void invokeUserPaint();
void render();
+ void invalidateFbo();
+
QImage grabFramebuffer() Q_DECL_OVERRIDE;
void beginBackingStorePainting() Q_DECL_OVERRIDE { inBackingStorePaint = true; }
void endBackingStorePainting() Q_DECL_OVERRIDE { inBackingStorePaint = false; }
@@ -522,10 +583,12 @@ public:
QOffscreenSurface *surface;
bool initialized;
bool fakeHidden;
- QOpenGLPaintDevice *paintDevice;
bool inBackingStorePaint;
- QSurfaceFormat requestedFormat;
+ bool hasBeenComposed;
bool flushPending;
+ QOpenGLPaintDevice *paintDevice;
+ QSurfaceFormat requestedFormat;
+ QOpenGLWidget::UpdateBehavior updateBehavior;
};
void QOpenGLWidgetPaintDevicePrivate::beginPaint()
@@ -646,8 +709,9 @@ void QOpenGLWidgetPrivate::beginCompose()
if (flushPending) {
flushPending = false;
q->makeCurrent();
- context->functions()->glFlush();
+ static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
}
+ hasBeenComposed = true;
emit q->aboutToCompose();
}
@@ -722,17 +786,25 @@ void QOpenGLWidgetPrivate::resolveSamples()
q->makeCurrent();
QRect rect(QPoint(0, 0), fbo->size());
QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
+ flushPending = true;
}
}
void QOpenGLWidgetPrivate::invokeUserPaint()
{
Q_Q(QOpenGLWidget);
- QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ Q_ASSERT(ctx && fbo);
+
+ QOpenGLFunctions *f = ctx->functions();
+ QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbo->handle();
f->glViewport(0, 0, q->width() * q->devicePixelRatio(), q->height() * q->devicePixelRatio());
q->paintGL();
- f->glFlush();
+ flushPending = true;
+
+ QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = 0;
}
void QOpenGLWidgetPrivate::render()
@@ -743,9 +815,31 @@ void QOpenGLWidgetPrivate::render()
return;
q->makeCurrent();
+
+ if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) {
+ invalidateFbo();
+ hasBeenComposed = false;
+ }
+
invokeUserPaint();
}
+void QOpenGLWidgetPrivate::invalidateFbo()
+{
+ QOpenGLExtensions *f = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
+ if (f->hasOpenGLExtension(QOpenGLExtensions::DiscardFramebuffer)) {
+ const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0
+ const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT
+ const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT
+ const GLenum attachments[] = {
+ gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment
+ };
+ f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments);
+ } else {
+ f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+}
+
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
QImage QOpenGLWidgetPrivate::grabFramebuffer()
@@ -795,10 +889,45 @@ QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f)
}
/*!
- Destroys the widget
- */
+ Destroys the QOpenGLWidget instance, freeing its resources.
+
+ The QOpenGLWidget's context is made current in the destructor, allowing for
+ safe destruction of any child object that may need to release OpenGL
+ resources belonging to the context provided by this widget.
+
+ \warning if you have objects wrapping OpenGL resources (such as
+ QOpenGLBuffer, QOpenGLShaderProgram, etc.) as members of a OpenGLWidget
+ subclass, you may need to add a call to makeCurrent() in that subclass'
+ destructor as well. Due to the rules of C++ object destruction, those objects
+ will be destroyed \e{before} calling this function (but after that the
+ destructor of the subclass has run), therefore making the OpenGL context
+ current in this function happens too late for their safe disposal.
+
+ \sa makeCurrent
+*/
QOpenGLWidget::~QOpenGLWidget()
{
+ makeCurrent();
+}
+
+/*!
+ Sets this widget's update behavior to \a updateBehavior.
+ \since 5.5
+*/
+void QOpenGLWidget::setUpdateBehavior(UpdateBehavior updateBehavior)
+{
+ Q_D(QOpenGLWidget);
+ d->updateBehavior = updateBehavior;
+}
+
+/*!
+ \return the update behavior of the widget.
+ \since 5.5
+*/
+QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
+{
+ Q_D(const QOpenGLWidget);
+ return d->updateBehavior;
}
/*!
@@ -877,10 +1006,8 @@ bool QOpenGLWidget::isValid() const
void QOpenGLWidget::makeCurrent()
{
Q_D(QOpenGLWidget);
- if (!d->initialized) {
- qWarning("QOpenGLWidget: Cannot make uninitialized widget current");
+ if (!d->initialized)
return;
- }
d->context->makeCurrent(d->surface);