summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@digia.com>2014-03-20 12:58:22 +0100
committerLaszlo Agocs <laszlo.agocs@digia.com>2014-08-05 16:47:37 +0200
commite48737ae778b8ef5e8e905825a2787b97deea23d (patch)
tree220628a632981dd709e801d2855e563989b39331 /src/gui
parentb53e08e3356528e7d240188c8985e205a6eaa2a2 (diff)
Introduce QOpenGLWindow
[ChangeLog] Added QOpenGLWindow. This serves as a convenience class for creating windows showing OpenGL content via an API similar to QGLWidget and without any widget dependencies. Done-with: Jorgen Lind <jorgen.lind@digia.com> Task-number: QTBUG-36899 Change-Id: I52e9bc61acb129dbfd3841b3adeffab2dbcf7f05 Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/kernel/kernel.pri13
-rw-r--r--src/gui/kernel/qopenglwindow.cpp615
-rw-r--r--src/gui/kernel/qopenglwindow.h100
-rw-r--r--src/gui/kernel/qpaintdevicewindow.cpp214
-rw-r--r--src/gui/kernel/qpaintdevicewindow.h85
-rw-r--r--src/gui/kernel/qpaintdevicewindow_p.h130
6 files changed, 1153 insertions, 4 deletions
diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri
index 2aeea6288b..bc167cea57 100644
--- a/src/gui/kernel/kernel.pri
+++ b/src/gui/kernel/kernel.pri
@@ -69,7 +69,9 @@ HEADERS += \
kernel/qplatformscreenpageflipper.h \
kernel/qplatformsystemtrayicon.h \
kernel/qplatformsessionmanager.h \
- kernel/qpixelformat.h
+ kernel/qpixelformat.h \
+ kernel/qpaintdevicewindow.h \
+ kernel/qpaintdevicewindow_p.h
SOURCES += \
kernel/qgenericpluginfactory.cpp \
@@ -121,17 +123,20 @@ SOURCES += \
kernel/qplatformsystemtrayicon.cpp \
kernel/qplatformsessionmanager.cpp \
kernel/qplatformmenu.cpp \
- kernel/qpixelformat.cpp
+ kernel/qpixelformat.cpp \
+ kernel/qpaintdevicewindow.cpp
contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles2) {
HEADERS += \
kernel/qplatformopenglcontext.h \
kernel/qopenglcontext.h \
- kernel/qopenglcontext_p.h
+ kernel/qopenglcontext_p.h \
+ kernel/qopenglwindow.h
SOURCES += \
kernel/qplatformopenglcontext.cpp \
- kernel/qopenglcontext.cpp
+ kernel/qopenglcontext.cpp \
+ kernel/qopenglwindow.cpp
}
win32:HEADERS+=kernel/qwindowdefs_win.h
diff --git a/src/gui/kernel/qopenglwindow.cpp b/src/gui/kernel/qopenglwindow.cpp
new file mode 100644
index 0000000000..02bf4cb101
--- /dev/null
+++ b/src/gui/kernel/qopenglwindow.cpp
@@ -0,0 +1,615 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenglwindow.h"
+#include "qpaintdevicewindow_p.h"
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QOpenGLPaintDevice>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qopengltextureblitter_p.h>
+#include <QtGui/private/qopenglextensions_p.h>
+#include <QtGui/QMatrix4x4>
+#include <QtGui/QOffscreenSurface>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOpenGLWindow
+ \inmodule QtGui
+ \since 5.4
+ \brief The QOpenGLWindow class is a convenience subclass of QWindow to perform OpenGL painting.
+
+ QOpenGLWindow is an enhanced QWindow that allows easily creating windows that
+ perform OpenGL rendering using an API that is compatible with QOpenGLWidget
+ and is similar to the legacy QGLWidget. Unlike QOpenGLWidget, QOpenGLWindow
+ has no dependency on the widgets module and offers better performance.
+
+ A typical application will subclass QOpenGLWindow and reimplement the following
+ virtual functions:
+
+ \list
+
+ \li initializeGL() to perform OpenGL resource initialization
+
+ \li resizeGL() to set up the transformation matrices and other window size dependent resources
+
+ \li paintGL() to issue OpenGL commands or draw using QPainter
+
+ \endlist
+
+ To schedule a repaint, call the update() function. Note that this will not
+ immediately result in a call to paintGL(). Calling update() multiple times in
+ a row will not change the behavior in any way.
+
+ This is a slot so it can be connected to a \l QTimer::timeout() signal to
+ perform animation. Note however that in the modern OpenGL world it is a much
+ better choice to rely on synchronization to the vertical refresh rate of the
+ display. See \l{QSurfaceFormat::setSwapInterval()}{setSwapInterval()} on a
+ description of the swap interval. With a swap interval of \c 1, which is the
+ case on most systems by default, the
+ \l{QOpenGLContext::swapBuffers()}{swapBuffers()} call, that is executed
+ internally by QOpenGLWindow after each repaint, will block and wait for
+ vsync. This means that whenever the swap is done, an update can be scheduled
+ again by calling update(), without relying on timers.
+
+ To request a specific configuration for the context, use setFormat()
+ like for any other QWindow. This allows, among others, requesting a
+ given OpenGL version and profile, or enabling depth and stencil
+ buffers.
+
+ Unlike QWindow, QOpenGLWindow allows opening a painter on itself and perform
+ QPainter-based drawing.
+
+ QOpenGLWindow supports multiple update behaviors. The default,
+ \c NoPartialUpdate is equivalent to a regular, OpenGL-based QWindow or the
+ legacy QGLWidget. In contrast, \c PartialUpdateBlit and \c PartialUpdateBlend are
+ more in line with QOpenGLWidget's way of working, where there is always an
+ extra, dedicated framebuffer object present. These modes allow, by
+ sacrificing some performance, redrawing only a smaller area on each paint and
+ having the rest of the content preserved from of the previous frame. This is
+ useful for applications than render incrementally using QPainter, because
+ this way they do not have to redraw the entire window content on each
+ paintGL() call.
+
+ For more information on graphics in Qt, see \l {Graphics}.
+ */
+
+/*!
+ \enum QOpenGLWindow::UpdateBehavior
+
+ This enum describes the update strategy of the QOpenGLWindow.
+
+ \value NoPartialUpdate Indicates that the entire window surface will
+ redrawn on each update and so no additional framebuffers are needed.
+ This is the setting used in most cases and is equivalent to how drawing
+ directly via QWindow would function.
+
+ \value PartialUpdateBlit Indicates that the drawing performed in paintGL()
+ does not cover the entire window. In this case an extra framebuffer object
+ is created under the hood, and rendering performed in paintGL() will target
+ this framebuffer. This framebuffer is then blitted onto the window surface's
+ default framebuffer after each paint. This allows having QPainter-based drawing
+ code in paintGL() which only repaints a smaller area at a time, because, unlike
+ NoPartialUpdate, the previous content is preserved.
+
+ \value PartialUpdateBlend Similar to PartialUpdateBlit, but instead of using
+ framebuffer blits, the contents of the extra framebuffer is rendered by
+ drawing a textured quad with blending enabled. This, unlike PartialUpdateBlit,
+ allows alpha blended content and works even when the glBlitFramebuffer is
+ not available. Performance-wise this setting is likely to be somewhat slower
+ than PartialUpdateBlit.
+ */
+
+/*!
+ \fn void QOpenGLWindow::frameSwapped()
+
+ This signal is emitted after the potentially blocking
+ \l{QOpenGLContext::swapBuffers()}{buffer swap} has been done. Applications
+ that wish to continuously repaint synchronized to the vertical refresh,
+ should issue an update() upon this signal. This allows for a much smoother
+ experience compared to the traditional usage of timers.
+*/
+
+// GLES2 builds won't have these constants with the suffixless names
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+class QOpenGLWindowPaintDevice : public QOpenGLPaintDevice
+{
+public:
+ QOpenGLWindowPaintDevice(QOpenGLWindow *window) : m_window(window) { }
+ void ensureActiveTarget() Q_DECL_OVERRIDE;
+
+ QOpenGLWindow *m_window;
+};
+
+class QOpenGLWindowPrivate : public QPaintDeviceWindowPrivate
+{
+ Q_DECLARE_PUBLIC(QOpenGLWindow)
+public:
+ QOpenGLWindowPrivate(QOpenGLWindow::UpdateBehavior updateBehavior)
+ : updateBehavior(updateBehavior)
+ , hasFboBlit(false)
+ {
+ }
+
+ ~QOpenGLWindowPrivate()
+ {
+ Q_Q(QOpenGLWindow);
+ if (q->isValid()) {
+ q->makeCurrent(); // this works even when the platformwindow is destroyed
+ paintDevice.reset(0);
+ fbo.reset(0);
+ blitter.destroy();
+ q->doneCurrent();
+ }
+ }
+
+ static QOpenGLWindowPrivate *get(QOpenGLWindow *w) { return w->d_func(); }
+
+ void bindFBO()
+ {
+ if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
+ fbo->bind();
+ else
+ QOpenGLFramebufferObject::bindDefault();
+ }
+
+ void beginPaint(const QRegion &region) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(region);
+ Q_Q(QOpenGLWindow);
+
+ if (!context) {
+ context.reset(new QOpenGLContext);
+ context->setFormat(q->requestedFormat());
+ if (!context->create())
+ qWarning("QOpenGLWindow::beginPaint: Failed to create context");
+ if (!context->makeCurrent(q))
+ qWarning("QOpenGLWindow::beginPaint: Failed to make context current");
+
+ paintDevice.reset(new QOpenGLWindowPaintDevice(q));
+ if (updateBehavior == QOpenGLWindow::PartialUpdateBlit)
+ hasFboBlit = QOpenGLFramebufferObject::hasOpenGLFramebufferBlit();
+
+ q->initializeGL();
+ } else {
+ context->makeCurrent(q);
+ }
+
+ if (updateBehavior > QOpenGLWindow::NoPartialUpdate) {
+ if (!fbo || fbo->size() != q->size()) {
+ QOpenGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ if (q->requestedFormat().samples() > 0) {
+ if (updateBehavior != QOpenGLWindow::PartialUpdateBlend)
+ fboFormat.setSamples(q->requestedFormat().samples());
+ else
+ qWarning("QOpenGLWindow: PartialUpdateBlend does not support multisampling");
+ }
+ fbo.reset(new QOpenGLFramebufferObject(q->size(), fboFormat));
+ markWindowAsDirty();
+ }
+ } else {
+ markWindowAsDirty();
+ }
+
+ const int deviceWidth = q->width() * q->devicePixelRatio();
+ const int deviceHeight = q->height() * q->devicePixelRatio();
+ paintDevice->setSize(QSize(deviceWidth, deviceHeight));
+ context->functions()->glViewport(0, 0, deviceWidth, deviceHeight);
+
+ context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject());
+
+ q->paintUnderGL();
+
+ if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
+ fbo->bind();
+ }
+
+ void endPaint() Q_DECL_OVERRIDE
+ {
+ Q_Q(QOpenGLWindow);
+
+ if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
+ fbo->release();
+
+ context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject());
+
+ if (updateBehavior == QOpenGLWindow::PartialUpdateBlit && hasFboBlit) {
+ QOpenGLExtensions extensions(context.data());
+ extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle());
+ extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, context->defaultFramebufferObject());
+ extensions.glBlitFramebuffer(0, 0, q->width(), q->height(),
+ 0, 0, q->width(), q->height(),
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ } else if (updateBehavior > QOpenGLWindow::NoPartialUpdate) {
+ if (updateBehavior == QOpenGLWindow::PartialUpdateBlend) {
+ context->functions()->glEnable(GL_BLEND);
+ context->functions()->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ if (!blitter.isCreated())
+ blitter.create();
+
+ QRect windowRect(QPoint(0, 0), q->size());
+ QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(windowRect, windowRect);
+ blitter.bind();
+ blitter.blit(fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
+ blitter.release();
+
+ if (updateBehavior == QOpenGLWindow::PartialUpdateBlend)
+ context->functions()->glDisable(GL_BLEND);
+ }
+
+ q->paintOverGL();
+ }
+
+ void flush(const QRegion &region) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(region);
+ Q_Q(QOpenGLWindow);
+ context->swapBuffers(q);
+ emit q->frameSwapped();
+ }
+
+ QOpenGLWindow::UpdateBehavior updateBehavior;
+ bool hasFboBlit;
+ QScopedPointer<QOpenGLContext> context;
+ QScopedPointer<QOpenGLFramebufferObject> fbo;
+ QScopedPointer<QOpenGLWindowPaintDevice> paintDevice;
+ QOpenGLTextureBlitter blitter;
+ QColor backgroundColor;
+ QScopedPointer<QOffscreenSurface> offscreenSurface;
+};
+
+void QOpenGLWindowPaintDevice::ensureActiveTarget()
+{
+ QOpenGLWindowPrivate::get(m_window)->bindFBO();
+}
+
+/*!
+ Constructs a new QOpenGLWindow with the given \a parent and \a updateBehavior.
+
+ \sa QOpenGLWindow::UpdateBehavior
+ */
+QOpenGLWindow::QOpenGLWindow(QOpenGLWindow::UpdateBehavior updateBehavior, QWindow *parent)
+ : QPaintDeviceWindow(*(new QOpenGLWindowPrivate(updateBehavior)), parent)
+{
+ setSurfaceType(QSurface::OpenGLSurface);
+}
+
+/*!
+ \return the update behavior for this QOpenGLWindow.
+*/
+QOpenGLWindow::UpdateBehavior QOpenGLWindow::updateBehavior() const
+{
+ Q_D(const QOpenGLWindow);
+ return d->updateBehavior;
+}
+
+/*!
+ \return \c true if the window's OpenGL resources, like the context, have
+ been successfully initialized. Note that the return value is always \c false
+ until the window becomes exposed (shown).
+*/
+bool QOpenGLWindow::isValid() const
+{
+ Q_D(const QOpenGLWindow);
+ return d->context && d->context->isValid();
+}
+
+/*!
+ Prepares for rendering OpenGL content for this window by making the
+ corresponding context current and binding the framebuffer object, if there is
+ one, in that context context.
+
+ It is not necessary to call this function in most cases, because it is called
+ automatically before invoking paintGL(). It is provided nonetheless to support
+ advanced, multi-threaded scenarios where a thread different than the GUI or main
+ thread may want to update the surface or framebuffer contents. See QOpenGLContext
+ for more information on threading related issues.
+
+ This function is suitable for calling also when the underlying platform window
+ is already destroyed. This means that it is safe to call this function from
+ a QOpenGLWindow subclass' destructor. If there is no native window anymore,
+ an offscreen surface is used instead. This ensures that OpenGL resource
+ cleanup operations in the destructor will always work, as long as
+ this function is called first.
+
+ \sa QOpenGLContext, context(), paintGL(), doneCurrent()
+ */
+void QOpenGLWindow::makeCurrent()
+{
+ Q_D(QOpenGLWindow);
+
+ if (!isValid())
+ return;
+
+ // The platform window may be destroyed at this stage and therefore
+ // makeCurrent() may not safely be called with 'this'.
+ if (handle()) {
+ d->context->makeCurrent(this);
+ } else {
+ if (!d->offscreenSurface) {
+ d->offscreenSurface.reset(new QOffscreenSurface);
+ d->offscreenSurface->setFormat(d->context->format());
+ d->offscreenSurface->create();
+ }
+ d->context->makeCurrent(d->offscreenSurface.data());
+ }
+
+ d->bindFBO();
+}
+
+/*!
+ Releases the context.
+
+ It is not necessary to call this function in most cases, since the widget
+ will make sure the context is bound and released properly when invoking
+ paintGL().
+
+ \sa makeCurrent()
+ */
+void QOpenGLWindow::doneCurrent()
+{
+ Q_D(QOpenGLWindow);
+
+ if (!isValid())
+ return;
+
+ d->context->doneCurrent();
+}
+
+/*!
+ \return The QOpenGLContext used by this window or \c 0 if not yet initialized.
+ */
+QOpenGLContext *QOpenGLWindow::context() const
+{
+ Q_D(const QOpenGLWindow);
+ return d->context.data();
+}
+
+/*!
+ The framebuffer object handle used by this window.
+
+ When the update behavior is set to \c NoPartialUpdate, there is no separate
+ framebuffer object. In this case the returned value is the ID of the
+ default framebuffer.
+
+ Otherwise the value of the ID of the framebuffer object or \c 0 if not
+ yet initialized.
+ */
+GLuint QOpenGLWindow::defaultFramebufferObject() const
+{
+ Q_D(const QOpenGLWindow);
+ if (d->updateBehavior > NoPartialUpdate && d->fbo)
+ return d->fbo->handle();
+ else if (QOpenGLContext *ctx = QOpenGLContext::currentContext())
+ return ctx->defaultFramebufferObject();
+ else
+ return 0;
+}
+
+extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+
+/*!
+ Returns a 32-bit RGB image of the framebuffer.
+
+ \note This is a potentially expensive operation because it relies on
+ glReadPixels() to read back the pixels. This may be slow and can stall the
+ GPU pipeline.
+
+ \note When used together with update behavior \c NoPartialUpdate, the returned
+ image may not contain the desired content when called after the front and back
+ buffers have been swapped (unless preserved swap is enabled in the underlying
+ windowing system interface). In this mode the function reads from the back
+ buffer and the contents of that may not match the content on the screen (the
+ front buffer). In this case the only place where this function can safely be
+ used is paintGL() or paintOverGL().
+ */
+QImage QOpenGLWindow::grabFramebuffer()
+{
+ if (!isValid())
+ return QImage();
+
+ makeCurrent();
+ return qt_gl_read_framebuffer(size() * devicePixelRatio(), false, false);
+}
+
+/*!
+ This virtual function is called once before the first call to paintGL() or
+ resizeGL(). Reimplement it in a subclass.
+
+ This function should set up any required OpenGL resources and state.
+
+ There is no need to call makeCurrent() because this has already been done
+ when this function is called. Note however that the framebuffer, in case
+ partial update mode is used, is not yet available at this stage, so avoid
+ issuing draw calls from here. Defer such calls to paintGL() instead.
+
+ \sa paintGL(), resizeGL()
+ */
+void QOpenGLWindow::initializeGL()
+{
+}
+
+/*!
+ This virtual function is called whenever the widget has been resized.
+ Reimplement it in a subclass. The new size is passed in \a w and \a h.
+
+ There is no need to call makeCurrent() because this has already been done
+ when this function is called. Additionally, the framebuffer, if there is one,
+ is bound too.
+
+ \sa initializeGL(), paintGL()
+ */
+void QOpenGLWindow::resizeGL(int w, int h)
+{
+ Q_UNUSED(w);
+ Q_UNUSED(h);
+}
+
+/*!
+ This virtual function is called whenever the window contents needs to be
+ painted. Reimplement it in a subclass.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+
+ Before invoking this function, the context and the framebuffer, if there is
+ one, are bound, and the viewport is set up by a call to glViewport(). No
+ other state is set and no clearing or drawing is performed by the framework.
+
+ \note When using a partial update behavior, like \c PartialUpdateBlend, the
+ output of the previous paintGL() call is preserved and, after the additional
+ drawing perfomed in the current invocation of the function, the content is
+ blitted or blended over the content drawn directly to the window in
+ paintUnderGL().
+
+ \sa initializeGL(), resizeGL(), paintUnderGL(), paintOverGL(), UpdateBehavior
+ */
+void QOpenGLWindow::paintGL()
+{
+}
+
+/*!
+ The virtual function is called before each invocation of paintGL().
+
+ When the update mode is set to \c NoPartialUpdate, there is no difference
+ between this function and paintGL(), performing rendering in either of them
+ leads to the same result.
+
+ The difference becomes significant when using \c PartialUpdateBlend, where an
+ extra framebuffer object is used. There, paintGL() targets this additional
+ framebuffer object, which preserves its contents, while paintUnderGL() and
+ paintOverGL() target the default framebuffer, i.e. directly the window
+ surface, the contents of which is lost after each displayed frame.
+
+ \note Avoid relying on this function when the update behavior is
+ \c PartialUpdateBlit. This mode involves blitting the extra framebuffer used by
+ paintGL() onto the default framebuffer after each invocation of paintGL(),
+ thus overwriting all drawing generated in this function.
+
+ \sa paintGL(), paintOverGL(), UpdateBehavior
+ */
+void QOpenGLWindow::paintUnderGL()
+{
+}
+
+/*!
+ This virtual function is called after each invocation of paintGL().
+
+ When the update mode is set to NoPartialUpdate, there is no difference
+ between this function and paintGL(), performing rendering in either of them
+ leads to the same result.
+
+ Like paintUnderGL(), rendering in this function targets the default
+ framebuffer of the window, regardless of the update behavior. It gets called
+ after paintGL() has returned and the blit (PartialUpdateBlit) or quad drawing
+ (PartialUpdateBlend) has been done.
+
+ \sa paintGL(), paintUnderGL(), UpdateBehavior
+ */
+void QOpenGLWindow::paintOverGL()
+{
+}
+
+/*!
+ Paint \a event handler. Calls paintGL().
+
+ \sa paintGL()
+ */
+void QOpenGLWindow::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ paintGL();
+}
+
+/*!
+ Resize \a event handler. Calls resizeGL().
+
+ \sa resizeGL()
+ */
+void QOpenGLWindow::resizeEvent(QResizeEvent *event)
+{
+ Q_UNUSED(event);
+ resizeGL(width(), height());
+}
+
+/*!
+ \internal
+ */
+int QOpenGLWindow::metric(PaintDeviceMetric metric) const
+{
+ Q_D(const QOpenGLWindow);
+
+ switch (metric) {
+ case PdmDepth:
+ if (d->paintDevice)
+ return d->paintDevice->depth();
+ break;
+ case PdmDevicePixelRatio:
+ if (d->paintDevice)
+ return d->paintDevice->devicePixelRatio();
+ break;
+ default:
+ break;
+ }
+ return QPaintDeviceWindow::metric(metric);
+
+}
+
+/*!
+ \internal
+ */
+QPaintDevice *QOpenGLWindow::redirected(QPoint *) const
+{
+ Q_D(const QOpenGLWindow);
+ if (QOpenGLContext::currentContext() == d->context.data())
+ return d->paintDevice.data();
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qopenglwindow.h b/src/gui/kernel/qopenglwindow.h
new file mode 100644
index 0000000000..294bd90116
--- /dev/null
+++ b/src/gui/kernel/qopenglwindow.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENGLWINDOW_H
+#define QOPENGLWINDOW_H
+
+#include <QtGui/QPaintDeviceWindow>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QImage>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLWindowPrivate;
+
+class Q_GUI_EXPORT QOpenGLWindow : public QPaintDeviceWindow
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOpenGLWindow)
+
+public:
+ enum UpdateBehavior {
+ NoPartialUpdate,
+ PartialUpdateBlit,
+ PartialUpdateBlend
+ };
+
+ explicit QOpenGLWindow(UpdateBehavior updateBehavior = NoPartialUpdate, QWindow *parent = 0);
+
+ UpdateBehavior updateBehavior() const;
+ bool isValid() const;
+
+ void makeCurrent();
+ void doneCurrent();
+
+ QOpenGLContext *context() const;
+
+ GLuint defaultFramebufferObject() const;
+
+ QImage grabFramebuffer();
+
+Q_SIGNALS:
+ void frameSwapped();
+
+protected:
+ virtual void initializeGL();
+ virtual void resizeGL(int w, int h);
+ virtual void paintGL();
+ virtual void paintUnderGL();
+ virtual void paintOverGL();
+
+ void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
+ void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
+ int metric(PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
+ QPaintDevice *redirected(QPoint *) const Q_DECL_OVERRIDE;
+
+private:
+ Q_DISABLE_COPY(QOpenGLWindow)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/kernel/qpaintdevicewindow.cpp b/src/gui/kernel/qpaintdevicewindow.cpp
new file mode 100644
index 0000000000..232e8b5d6d
--- /dev/null
+++ b/src/gui/kernel/qpaintdevicewindow.cpp
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevicewindow_p.h"
+
+#include <QtGui/QGuiApplication>
+#include <QtGui/QScreen>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPaintDeviceWindow
+ \inmodule QtGui
+ \since 5.4
+ \brief Convenience subclass of QWindow that is also a QPaintDevice.
+
+ QPaintDeviceWindow is like a regular QWindow, with the added functionality
+ of being a paint device too. Whenever the content needs to be updated,
+ the virtual paintEvent() function is called. Subclasses, that reimplement
+ this function, can then simply open a QPainter on the window.
+
+ \note This class cannot directly be used in applications. It rather serves
+ as a base for subclasses like QOpenGLWindow.
+
+ \sa QOpenGLWindow
+*/
+
+/*!
+ Marks the entire window as dirty and schedules a repaint.
+
+ \note Subsequent calls to this function before the next paint
+ event will get ignored.
+*/
+void QPaintDeviceWindow::update()
+{
+ update(QRect(QPoint(0,0), size()));
+}
+
+/*!
+ Marks the \a rect of the window as dirty and schedules a repaint.
+
+ \note Subsequent calls to this function before the next paint
+ event will get ignored.
+*/
+void QPaintDeviceWindow::update(const QRect &rect)
+{
+ Q_D(QPaintDeviceWindow);
+ d->dirtyRegion += rect;
+ d->triggerUpdate();
+}
+
+/*!
+ Marks the \a region of the window as dirty and schedules a repaint.
+
+ \note Subsequent calls to this function before the next paint
+ event will get ignored.
+*/
+void QPaintDeviceWindow::update(const QRegion &region)
+{
+ Q_D(QPaintDeviceWindow);
+ d->dirtyRegion += region;
+ d->triggerUpdate();
+}
+
+/*!
+ Handles paint events passed in the \a event parameter.
+
+ The default implementation does nothing. Reimplement this function to
+ perform painting. If necessary, the dirty area is retrievable from
+ the \a event.
+*/
+void QPaintDeviceWindow::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ // Do nothing
+}
+
+/*!
+ \internal
+ */
+int QPaintDeviceWindow::metric(PaintDeviceMetric metric) const
+{
+ QScreen *screen = this->screen();
+ if (!screen && QGuiApplication::primaryScreen())
+ screen = QGuiApplication::primaryScreen();
+
+ switch (metric) {
+ case PdmWidth:
+ return width();
+ case PdmWidthMM:
+ if (screen)
+ return width() * screen->physicalSize().width() / screen->geometry().width();
+ break;
+ case PdmHeight:
+ return height();
+ case PdmHeightMM:
+ if (screen)
+ return height() * screen->physicalSize().height() / screen->geometry().height();
+ break;
+ case PdmDpiX:
+ if (screen)
+ return qRound(screen->logicalDotsPerInchX());
+ break;
+ case PdmDpiY:
+ if (screen)
+ return qRound(screen->logicalDotsPerInchY());
+ break;
+ case PdmPhysicalDpiX:
+ if (screen)
+ return qRound(screen->physicalDotsPerInchX());
+ break;
+ case PdmPhysicalDpiY:
+ if (screen)
+ return qRound(screen->physicalDotsPerInchY());
+ break;
+ case PdmDevicePixelRatio:
+ if (screen)
+ return screen->devicePixelRatio();
+ break;
+ default:
+ break;
+ }
+
+ return QPaintDevice::metric(metric);
+}
+
+/*!
+ \internal
+ */
+void QPaintDeviceWindow::exposeEvent(QExposeEvent *exposeEvent)
+{
+ Q_UNUSED(exposeEvent);
+ Q_D(QPaintDeviceWindow);
+ if (isExposed()) {
+ d->markWindowAsDirty();
+ // Do not rely on exposeEvent->region() as it has some issues for the
+ // time being, namely that it is sometimes in local coordinates,
+ // sometimes relative to the parent, depending on the platform plugin.
+ // We require local coords here.
+ d->doFlush(QRect(QPoint(0, 0), size()));
+ }
+}
+
+/*!
+ \internal
+ */
+bool QPaintDeviceWindow::event(QEvent *event)
+{
+ Q_D(QPaintDeviceWindow);
+
+ if (event->type() == QEvent::UpdateRequest) {
+ d->paintEventSent = false;
+ d->handleUpdateEvent();
+ return true;
+ }
+
+ return QWindow::event(event);
+}
+
+/*!
+ \internal
+ */
+QPaintDeviceWindow::QPaintDeviceWindow(QPaintDeviceWindowPrivate &dd, QWindow *parent)
+ : QWindow(dd, parent)
+{
+}
+
+/*!
+ \internal
+ */
+QPaintEngine *QPaintDeviceWindow::paintEngine() const
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qpaintdevicewindow.h b/src/gui/kernel/qpaintdevicewindow.h
new file mode 100644
index 0000000000..ee7d07254e
--- /dev/null
+++ b/src/gui/kernel/qpaintdevicewindow.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTDEVICEWINDOW_H
+#define QPAINTDEVICEWINDOW_H
+
+#include <QtGui/QWindow>
+#include <QtGui/QPaintDevice>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDeviceWindowPrivate;
+class QPaintEvent;
+
+class Q_GUI_EXPORT QPaintDeviceWindow : public QWindow, public QPaintDevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPaintDeviceWindow)
+
+public:
+ void update(const QRect &rect);
+ void update(const QRegion &region);
+
+ using QWindow::width;
+ using QWindow::height;
+ using QWindow::devicePixelRatio;
+
+public Q_SLOTS:
+ void update();
+
+protected:
+ virtual void paintEvent(QPaintEvent *event);
+
+ int metric(PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
+ void exposeEvent(QExposeEvent *) Q_DECL_OVERRIDE;
+ bool event(QEvent *event) Q_DECL_OVERRIDE;
+
+ QPaintDeviceWindow(QPaintDeviceWindowPrivate &dd, QWindow *parent);
+
+private:
+ QPaintEngine *paintEngine() const Q_DECL_OVERRIDE;
+ Q_DISABLE_COPY(QPaintDeviceWindow)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/kernel/qpaintdevicewindow_p.h b/src/gui/kernel/qpaintdevicewindow_p.h
new file mode 100644
index 0000000000..65313eda87
--- /dev/null
+++ b/src/gui/kernel/qpaintdevicewindow_p.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTDEVICEWINDOW_P_H
+#define QPAINTDEVICEWINDOW_P_H
+
+#include <QtGui/QPaintDeviceWindow>
+#include <QtCore/QCoreApplication>
+#include <QtGui/private/qwindow_p.h>
+#include <QtGui/QPaintEvent>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QPaintDeviceWindowPrivate : public QWindowPrivate
+{
+ Q_DECLARE_PUBLIC(QPaintDeviceWindow)
+
+public:
+ QPaintDeviceWindowPrivate() : paintEventSent(false) { }
+
+ virtual void beginPaint(const QRegion &region)
+ {
+ Q_UNUSED(region);
+ }
+
+ virtual void endPaint()
+ {
+ }
+
+ virtual void flush(const QRegion &region)
+ {
+ Q_UNUSED(region);
+ }
+
+ bool paint(const QRegion &region)
+ {
+ Q_Q(QPaintDeviceWindow);
+ QRegion toPaint = region & dirtyRegion;
+ if (toPaint.isEmpty())
+ return false;
+
+ // Clear the region now. The overridden functions may call update().
+ dirtyRegion -= toPaint;
+
+ beginPaint(toPaint);
+
+ QPaintEvent paintEvent(toPaint);
+ q->paintEvent(&paintEvent);
+
+ endPaint();
+
+ return true;
+ }
+
+ void triggerUpdate()
+ {
+ Q_Q(QPaintDeviceWindow);
+ if (!paintEventSent) {
+ QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
+ paintEventSent = true;
+ }
+ }
+
+ void doFlush(const QRegion &region)
+ {
+ QRegion toFlush = region;
+ if (paint(toFlush))
+ flush(toFlush);
+ }
+
+ void handleUpdateEvent()
+ {
+ if (dirtyRegion.isEmpty())
+ return;
+ doFlush(dirtyRegion);
+ }
+
+ void markWindowAsDirty()
+ {
+ Q_Q(QPaintDeviceWindow);
+ dirtyRegion += QRect(QPoint(0, 0), q->size());
+ }
+
+private:
+ QRegion dirtyRegion;
+ bool paintEventSent;
+};
+
+
+QT_END_NAMESPACE
+
+#endif //QPAINTDEVICEWINDOW_P_H