diff options
Diffstat (limited to 'src/widgets/kernel/qopenglwidget.cpp')
| -rw-r--r-- | src/widgets/kernel/qopenglwidget.cpp | 121 |
1 files changed, 111 insertions, 10 deletions
diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 12e054626ca..b5c547077c7 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -420,10 +420,25 @@ 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. + \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 +470,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 +525,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 +548,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 +565,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() @@ -648,6 +693,7 @@ void QOpenGLWidgetPrivate::beginCompose() q->makeCurrent(); context->functions()->glFlush(); } + hasBeenComposed = true; emit q->aboutToCompose(); } @@ -743,9 +789,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 +863,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 +980,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); |
