summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/opengl/opengl.pro6
-rw-r--r--examples/opengl/qopenglwindow/background.frag24
-rw-r--r--examples/opengl/qopenglwindow/background_renderer.cpp201
-rw-r--r--examples/opengl/qopenglwindow/background_renderer.h78
-rw-r--r--examples/opengl/qopenglwindow/main.cpp159
-rw-r--r--examples/opengl/qopenglwindow/qopenglwindow.pro15
-rw-r--r--examples/opengl/qopenglwindow/shaders.qrc5
-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
-rw-r--r--tests/auto/gui/kernel/kernel.pro3
-rw-r--r--tests/auto/gui/kernel/qopenglwindow/qopenglwindow.pro8
-rw-r--r--tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp300
16 files changed, 1950 insertions, 6 deletions
diff --git a/examples/opengl/opengl.pro b/examples/opengl/opengl.pro
index fb5f02c07c..8d09d21d72 100644
--- a/examples/opengl/opengl.pro
+++ b/examples/opengl/opengl.pro
@@ -6,7 +6,8 @@ contains(QT_CONFIG, dynamicgl) {
SUBDIRS = hellowindow \
contextinfo \
qopenglwidget \
- threadedqopenglwidget
+ threadedqopenglwidget \
+ qopenglwindow
} else: !contains(QT_CONFIG, opengles2) {
SUBDIRS = 2dpainting \
grabber \
@@ -23,6 +24,7 @@ contains(QT_CONFIG, dynamicgl) {
cube \
textures \
qopenglwidget \
- threadedqopenglwidget
+ threadedqopenglwidget \
+ qopenglwindow
EXAMPLE_FILES = shared
diff --git a/examples/opengl/qopenglwindow/background.frag b/examples/opengl/qopenglwindow/background.frag
new file mode 100644
index 0000000000..067c3505dc
--- /dev/null
+++ b/examples/opengl/qopenglwindow/background.frag
@@ -0,0 +1,24 @@
+uniform highp int currentTime;
+uniform highp vec2 windowSize;
+
+float noise(vec2 co)
+{
+ return 0.5 * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
+}
+
+float curvSpeed()
+{
+ return mod(float(currentTime), 1000000.0) / 500.0;
+}
+
+float curv()
+{
+ return 1.0 - abs((gl_FragCoord.y / (windowSize.y / 10.0) - 5.0) - sin((gl_FragCoord.x / (windowSize.x/20.0)) - curvSpeed()));
+}
+
+void main()
+{
+ float coordNoise = noise(gl_FragCoord.xy);
+ float proximity = smoothstep(0.5, 1.0, (curv() + 1.0) * (coordNoise ));
+ gl_FragColor = vec4(coordNoise, coordNoise, coordNoise, 1.0) * proximity;
+}
diff --git a/examples/opengl/qopenglwindow/background_renderer.cpp b/examples/opengl/qopenglwindow/background_renderer.cpp
new file mode 100644
index 0000000000..6cec6666b6
--- /dev/null
+++ b/examples/opengl/qopenglwindow/background_renderer.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "background_renderer.h"
+
+#include <QtCore/qmath.h>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTime>
+
+#include <QtGui/QOpenGLShaderProgram>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+
+#include <math.h>
+
+static const char vertex_shader[] =
+ "attribute highp vec3 vertexCoord;"
+ "void main() {"
+ " gl_Position = vec4(vertexCoord,1.0);"
+ "}";
+
+static const char fragment_shader[] =
+ "void main() {"
+ " gl_FragColor = vec4(0.0,1.0,0.0,1.0);"
+ "}";
+
+static const float vertices[] = { -1, -1, 0,
+ -1, 1, 0,
+ 1, -1, 0,
+ 1, 1, 0 };
+
+FragmentToy::FragmentToy(const QString &fragmentSource, QObject *parent)
+ : QObject(parent)
+ , m_recompile_shaders(true)
+{
+ if (QFile::exists(fragmentSource)) {
+ QFileInfo info(fragmentSource);
+ m_fragment_file_last_modified = info.lastModified();
+ m_fragment_file = fragmentSource;
+#ifndef QT_NO_FILESYSTEMWATCHER
+ m_watcher.addPath(info.canonicalFilePath());
+ QObject::connect(&m_watcher, &QFileSystemWatcher::fileChanged, this, &FragmentToy::fileChanged);
+#endif
+ }
+}
+
+void FragmentToy::draw(const QSize &windowSize)
+{
+ if (!m_program)
+ initializeOpenGLFunctions();
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+
+ glClearColor(0,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (!m_vao.isCreated())
+ m_vao.create();
+
+ m_vao.bind();
+
+ if (!m_vertex_buffer.isCreated()) {
+ m_vertex_buffer.create();
+ m_vertex_buffer.bind();
+ m_vertex_buffer.allocate(vertices, sizeof(vertices));
+ m_vertex_buffer.release();
+ }
+
+ if (!m_program) {
+ m_program.reset(new QOpenGLShaderProgram);
+ m_program->create();
+ m_vertex_shader.reset(new QOpenGLShader(QOpenGLShader::Vertex));
+ if (!m_vertex_shader->compileSourceCode(vertex_shader)) {
+ qWarning() << "Failed to compile the vertex shader:" << m_vertex_shader->log();
+ }
+ if (!m_program->addShader(m_vertex_shader.data())) {
+ qWarning() << "Failed to add vertex shader to program:" << m_program->log();
+ }
+ }
+
+ if (!m_fragment_shader) {
+ QByteArray data;
+ if (m_fragment_file.size()) {
+ QFile file(m_fragment_file);
+ if (file.open(QIODevice::ReadOnly)) {
+ data = file.readAll();
+ } else {
+ qWarning() << "Failed to load input file, falling back to default";
+ data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
+ }
+ } else {
+ QFile qrcFile(":/background.frag");
+ if (qrcFile.open(QIODevice::ReadOnly))
+ data = qrcFile.readAll();
+ else
+ data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
+ }
+ if (data.size()) {
+ m_fragment_shader.reset(new QOpenGLShader(QOpenGLShader::Fragment));
+ if (!m_fragment_shader->compileSourceCode(data)) {
+ qWarning() << "Failed to compile fragment shader:" << m_fragment_shader->log();
+ m_fragment_shader.reset(Q_NULLPTR);
+ }
+ } else {
+ qWarning() << "Unknown error, no fragment shader";
+ }
+
+ if (m_fragment_shader) {
+ if (!m_program->addShader(m_fragment_shader.data())) {
+ qWarning() << "Failed to add fragment shader to program:" << m_program->log();
+ }
+ }
+ }
+
+ if (m_recompile_shaders) {
+ m_recompile_shaders = false;
+
+ if (m_program->link()) {
+ m_vertex_coord_pos = m_program->attributeLocation("vertexCoord");
+ } else {
+ qWarning() << "Failed to link shader program" << m_program->log();
+ }
+
+ }
+
+ m_program->bind();
+
+ m_vertex_buffer.bind();
+ m_program->setAttributeBuffer("vertexCoord", GL_FLOAT, 0, 3, 0);
+ m_program->enableAttributeArray("vertexCoord");
+ m_vertex_buffer.release();
+
+ float radiens = (QTime::currentTime().msecsSinceStartOfDay() / 60) / (2 * M_PI);
+ m_program->setUniformValue("currentTime", (uint) QDateTime::currentDateTime().toMSecsSinceEpoch());
+ m_program->setUniformValue("radiens", radiens);
+ m_program->setUniformValue("windowSize", windowSize);
+
+ QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ m_program->release();
+ m_vao.release();
+}
+
+void FragmentToy::fileChanged(const QString &path)
+{
+ Q_UNUSED(path);
+ if (QFile::exists(m_fragment_file)) {
+ QFileInfo fragment_source(m_fragment_file);
+ if (fragment_source.lastModified() > m_fragment_file_last_modified) {
+ m_fragment_file_last_modified = fragment_source.lastModified();
+ m_recompile_shaders = true;
+ if (m_program) {
+ m_program->removeShader(m_fragment_shader.data());
+ m_fragment_shader.reset(Q_NULLPTR);
+ }
+ }
+ } else {
+ m_recompile_shaders = true;
+ if (m_program) {
+ m_program->removeShader(m_fragment_shader.data());
+ m_fragment_shader.reset(Q_NULLPTR);
+ }
+ }
+}
diff --git a/examples/opengl/qopenglwindow/background_renderer.h b/examples/opengl/qopenglwindow/background_renderer.h
new file mode 100644
index 0000000000..4f968325d5
--- /dev/null
+++ b/examples/opengl/qopenglwindow/background_renderer.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FRAGMENT_TOY_H
+#define FRAGMENT_TOY_H
+
+#include <QtCore/QObject>
+#include <QtCore/QFile>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileSystemWatcher>
+#include <QtGui/QOpenGLVertexArrayObject>
+#include <QtGui/QOpenGLBuffer>
+#include <QtGui/QOpenGLShaderProgram>
+#include <QtGui/QOpenGLFunctions>
+
+class FragmentToy : public QObject, protected QOpenGLFunctions
+{
+ Q_OBJECT
+public:
+ FragmentToy(const QString &fragmentSource, QObject *parent = 0);
+
+ void draw(const QSize &windowSize);
+
+private:
+ void fileChanged(const QString &path);
+ bool m_recompile_shaders;
+#ifndef QT_NO_FILESYSTEMWATCHER
+ QFileSystemWatcher m_watcher;
+#endif
+ QString m_fragment_file;
+ QDateTime m_fragment_file_last_modified;
+
+ QScopedPointer<QOpenGLShaderProgram> m_program;
+ QScopedPointer<QOpenGLShader> m_vertex_shader;
+ QScopedPointer<QOpenGLShader> m_fragment_shader;
+ QOpenGLVertexArrayObject m_vao;
+ QOpenGLBuffer m_vertex_buffer;
+ GLuint m_vertex_coord_pos;
+};
+
+#endif //FRAGMENT_TOY_H
diff --git a/examples/opengl/qopenglwindow/main.cpp b/examples/opengl/qopenglwindow/main.cpp
new file mode 100644
index 0000000000..19ac9e8a15
--- /dev/null
+++ b/examples/opengl/qopenglwindow/main.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QOpenGLWindow>
+#include <QtGui/QScreen>
+#include <QtGui/QPainter>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QMatrix4x4>
+#include <QtGui/QStaticText>
+
+#include "background_renderer.h"
+
+static QPainterPath painterPathForTriangle()
+{
+ static const QPointF bottomLeft(-1.0, -1.0);
+ static const QPointF top(0.0, 1.0);
+ static const QPointF bottomRight(1.0, -1.0);
+
+ QPainterPath path(bottomLeft);
+ path.lineTo(top);
+ path.lineTo(bottomRight);
+ path.closeSubpath();
+ return path;
+}
+
+class OpenGLWindow : public QOpenGLWindow
+{
+ Q_OBJECT
+
+public:
+ // Use NoPartialUpdate. This means that all the rendering goes directly to
+ // the window surface, no additional framebuffer object stands in the
+ // middle. This is fine since we will clear the entire framebuffer on each
+ // paint. Under the hood this means that the behavior is equivalent to the
+ // manual makeCurrent - perform OpenGL calls - swapBuffers loop that is
+ // typical in pure QWindow-based applications.
+ OpenGLWindow()
+ : QOpenGLWindow(QOpenGLWindow::NoPartialUpdate)
+ , m_fragment_toy("background.frag")
+ , m_text_layout("The triangle and this text is rendered with QPainter")
+ {
+ m_view.lookAt(QVector3D(3,1,1),
+ QVector3D(0,0,0),
+ QVector3D(0,1,0));
+ connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
+
+ QLinearGradient gradient(QPointF(-1,-1), QPointF(1,1));
+ gradient.setColorAt(0, Qt::red);
+ gradient.setColorAt(1, Qt::green);
+
+ m_brush = QBrush(gradient);
+ }
+
+protected:
+ void paintGL() Q_DECL_OVERRIDE
+ {
+ m_fragment_toy.draw(size());
+
+ QPainter p(this);
+ p.setWorldTransform(m_window_normalised_matrix.toTransform());
+
+ QMatrix4x4 mvp = m_projection * m_view * m_model_triangle;
+ p.setTransform(mvp.toTransform(), true);
+
+ p.fillPath(painterPathForTriangle(), m_brush);
+
+ QTransform text_transform = (m_window_painter_matrix * m_view * m_model_text).toTransform();
+ p.setTransform(text_transform, false);
+ p.setPen(QPen(Qt::white));
+ m_text_layout.prepare(text_transform);
+ qreal x = - (m_text_layout.size().width() / 2);
+ qreal y = 0;
+ p.drawStaticText(x, y, m_text_layout);
+
+ m_model_triangle.rotate(-1, 0, 1, 0);
+ m_model_text.rotate(1, 0, 1, 0);
+ }
+
+ void resizeGL(int w, int h) Q_DECL_OVERRIDE
+ {
+ m_window_normalised_matrix.setToIdentity();
+ m_window_normalised_matrix.translate(w / 2.0, h / 2.0);
+ m_window_normalised_matrix.scale(w / 2.0, -h / 2.0);
+
+ m_window_painter_matrix.setToIdentity();
+ m_window_painter_matrix.translate(w / 2.0, h / 2.0);
+
+ m_text_layout.setTextWidth(std::max(w * 0.2, 80.0));
+
+ m_projection.setToIdentity();
+ m_projection.perspective(45.f, qreal(w) / qreal(h), 0.1f, 100.f);
+
+ }
+
+private:
+ QMatrix4x4 m_window_normalised_matrix;
+ QMatrix4x4 m_window_painter_matrix;
+ QMatrix4x4 m_projection;
+ QMatrix4x4 m_view;
+ QMatrix4x4 m_model_triangle;
+ QMatrix4x4 m_model_text;
+ QBrush m_brush;
+
+ FragmentToy m_fragment_toy;
+ QStaticText m_text_layout;
+};
+
+int main (int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ OpenGLWindow window;
+ QSurfaceFormat fmt;
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ window.setFormat(fmt);
+ window.show();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/examples/opengl/qopenglwindow/qopenglwindow.pro b/examples/opengl/qopenglwindow/qopenglwindow.pro
new file mode 100644
index 0000000000..dad4b58158
--- /dev/null
+++ b/examples/opengl/qopenglwindow/qopenglwindow.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+TARGET = qopenglwindow
+INCLUDEPATH += .
+
+RESOURCES += shaders.qrc
+
+SOURCES += \
+ main.cpp \
+ background_renderer.cpp
+
+HEADERS += \
+ background_renderer.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwindow
+INSTALLS += target
diff --git a/examples/opengl/qopenglwindow/shaders.qrc b/examples/opengl/qopenglwindow/shaders.qrc
new file mode 100644
index 0000000000..64eefe2c70
--- /dev/null
+++ b/examples/opengl/qopenglwindow/shaders.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>background.frag</file>
+</qresource>
+</RCC>
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
diff --git a/tests/auto/gui/kernel/kernel.pro b/tests/auto/gui/kernel/kernel.pro
index bbcdd91ea3..7d8af15b25 100644
--- a/tests/auto/gui/kernel/kernel.pro
+++ b/tests/auto/gui/kernel/kernel.pro
@@ -21,6 +21,7 @@ SUBDIRS=\
qwindow \
qguiapplication \
qpixelformat \
+ qopenglwindow
!qtHaveModule(widgets): SUBDIRS -= \
qmouseevent_modal \
@@ -28,3 +29,5 @@ SUBDIRS=\
!qtHaveModule(network): SUBDIRS -= \
qguieventloop
+
+!contains(QT_CONFIG, opengl(es2)?): SUBDIRS -= qopenglwindow
diff --git a/tests/auto/gui/kernel/qopenglwindow/qopenglwindow.pro b/tests/auto/gui/kernel/qopenglwindow/qopenglwindow.pro
new file mode 100644
index 0000000000..43a62b4811
--- /dev/null
+++ b/tests/auto/gui/kernel/qopenglwindow/qopenglwindow.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+TARGET = tst_qopenglwindow
+
+QT += core-private gui-private testlib
+
+SOURCES += tst_qopenglwindow.cpp
+
+win32:CONFIG+=insignificant_test # QTBUG-28264
diff --git a/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp b/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
new file mode 100644
index 0000000000..34f3e11c27
--- /dev/null
+++ b/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite 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 <QtGui/QOpenGLWindow>
+#include <QtTest/QtTest>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QPainter>
+
+class tst_QOpenGLWindow : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void create();
+ void basic();
+ void painter();
+ void partial_data();
+ void partial();
+ void underOver();
+};
+
+void tst_QOpenGLWindow::create()
+{
+ QOpenGLWindow w;
+ QVERIFY(!w.isValid());
+
+ w.resize(640, 480);
+ w.show();
+
+ QTest::qWaitForWindowExposed(&w);
+
+ QVERIFY(w.isValid());
+}
+
+class Window : public QOpenGLWindow
+{
+public:
+ void reset() {
+ initCount = resizeCount = paintCount = 0;
+ }
+
+ void initializeGL() Q_DECL_OVERRIDE {
+ ++initCount;
+ }
+
+ void resizeGL(int w, int h) Q_DECL_OVERRIDE {
+ ++resizeCount;
+ QCOMPARE(w, size().width());
+ QCOMPARE(h, size().height());
+ }
+
+ void paintGL() Q_DECL_OVERRIDE {
+ ++paintCount;
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QVERIFY(ctx);
+ QCOMPARE(ctx, context());
+ QOpenGLFunctions *f = ctx->functions();
+ QVERIFY(f);
+
+ f->glClearColor(1, 0, 0, 1);
+ f->glClear(GL_COLOR_BUFFER_BIT);
+
+ img = grabFramebuffer();
+ }
+
+ int initCount;
+ int resizeCount;
+ int paintCount;
+ QImage img;
+};
+
+void tst_QOpenGLWindow::basic()
+{
+ Window w;
+ w.reset();
+ w.resize(640, 480);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ // Check that the virtuals are invoked.
+ QCOMPARE(w.initCount, 1);
+ int resCount = w.resizeCount;
+ QVERIFY(resCount >= 1);
+ QVERIFY(w.paintCount >= 1);
+
+ // Check that something has been drawn;
+ QCOMPARE(w.img.size(), w.size());
+ QVERIFY(w.img.pixel(5, 5) == qRgb(255, 0, 0));
+
+ // Check that the viewport was properly set.
+ w.makeCurrent();
+ GLint v[4] = { 0, 0, 0, 0 };
+ w.context()->functions()->glGetIntegerv(GL_VIEWPORT, v);
+ QCOMPARE(v[0], 0);
+ QCOMPARE(v[1], 0);
+ QCOMPARE(v[2], GLint(w.width() * w.devicePixelRatio()));
+ QCOMPARE(v[3], GLint(w.height() * w.devicePixelRatio()));
+ w.doneCurrent();
+
+ // Check that a future resize triggers resizeGL.
+ w.resize(800, 600);
+ int maxWait = 1000;
+ while (w.resizeCount == resCount && maxWait-- >= 0)
+ QTest::qWait(10);
+ QVERIFY(w.resizeCount > resCount);
+}
+
+class PainterWindow : public QOpenGLWindow
+{
+public:
+ void paintGL() Q_DECL_OVERRIDE {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QVERIFY(ctx);
+ QCOMPARE(ctx, context());
+ QOpenGLFunctions *f = ctx->functions();
+ QVERIFY(f);
+
+ QPainter p(this);
+ p.beginNativePainting();
+ f->glClearColor(1, 0, 0, 1);
+ f->glClear(GL_COLOR_BUFFER_BIT);
+ p.endNativePainting();
+ p.fillRect(QRect(0, 0, 100, 100), Qt::blue);
+ p.end();
+
+ img = grabFramebuffer();
+ }
+
+ QImage img;
+};
+
+void tst_QOpenGLWindow::painter()
+{
+ PainterWindow w;
+ w.resize(400, 400);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ QCOMPARE(w.img.size(), w.size());
+ QVERIFY(w.img.pixel(5, 5) == qRgb(0, 0, 255));
+ QVERIFY(w.img.pixel(200, 5) == qRgb(255, 0, 0));
+}
+
+class PartialPainterWindow : public QOpenGLWindow
+{
+public:
+ PartialPainterWindow(QOpenGLWindow::UpdateBehavior u)
+ : QOpenGLWindow(u), x(0) { }
+
+ void paintGL() Q_DECL_OVERRIDE {
+ ++paintCount;
+
+ QPainter p(this);
+ if (!x)
+ p.fillRect(QRect(0, 0, width(), height()), Qt::green);
+
+ p.fillRect(QRect(x, 0, 10, 10), Qt::blue);
+ x += 20;
+ }
+
+ int paintCount;
+ int x;
+};
+
+void tst_QOpenGLWindow::partial_data()
+{
+ QTest::addColumn<int>("behavior");
+ QTest::newRow("blit") << int(QOpenGLWindow::PartialUpdateBlit);
+ QTest::newRow("blend") << int(QOpenGLWindow::PartialUpdateBlend);
+}
+
+void tst_QOpenGLWindow::partial()
+{
+ QFETCH(int, behavior);
+ QOpenGLWindow::UpdateBehavior u = QOpenGLWindow::UpdateBehavior(behavior);
+ PartialPainterWindow w(u);
+ w.resize(800, 400);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ // Add a couple of small blue rects.
+ for (int i = 0; i < 10; ++i) {
+ w.paintCount = 0;
+ w.update();
+ int maxWait = 1000;
+ while (w.paintCount == 0 && maxWait-- >= 0)
+ QTest::qWait(10);
+ }
+
+ // Now since the painting went to an extra framebuffer, all the rects should
+ // be present since everything is preserved between the frames.
+ QImage img = w.grabFramebuffer();
+ QCOMPARE(img.size(), w.size());
+ QCOMPARE(img.pixel(5, 5), qRgb(0, 0, 255));
+ QCOMPARE(img.pixel(15, 5), qRgb(0, 255, 0));
+ QCOMPARE(img.pixel(25, 5), qRgb(0, 0, 255));
+}
+
+class PaintUnderOverWindow : public QOpenGLWindow
+{
+public:
+ PaintUnderOverWindow() : QOpenGLWindow(PartialUpdateBlend), m_state(None) { }
+ enum State {
+ None,
+ PaintUnder,
+ Paint,
+ PaintOver,
+ Error
+ } m_state;
+
+ void paintUnderGL() Q_DECL_OVERRIDE {
+ if (m_state == None || m_state == PaintOver)
+ m_state = PaintUnder;
+ else
+ m_state = Error;
+
+ GLuint fbo = 0xFFFF;
+ QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &fbo);
+ QVERIFY(fbo == 0);
+ }
+
+ void paintGL() Q_DECL_OVERRIDE {
+ if (m_state == PaintUnder)
+ m_state = Paint;
+ else
+ m_state = Error;
+
+ // Using PartialUpdateBlend so paintGL() targets a user fbo, not the default.
+ GLuint fbo = 0xFFFF;
+ QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &fbo);
+ QVERIFY(fbo != 0);
+ QCOMPARE(fbo, defaultFramebufferObject());
+ }
+
+ void paintOverGL() Q_DECL_OVERRIDE {
+ if (m_state == Paint)
+ m_state = PaintOver;
+ else
+ m_state = Error;
+
+ GLuint fbo = 0xFFFF;
+ QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &fbo);
+ QVERIFY(fbo == 0);
+ }
+};
+
+void tst_QOpenGLWindow::underOver()
+{
+ PaintUnderOverWindow w;
+ w.resize(400, 400);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ // under -> paint -> over -> under -> paint -> ... is the only acceptable sequence
+ QCOMPARE(w.m_state, PaintUnderOverWindow::PaintOver);
+}
+
+#include <tst_qopenglwindow.moc>
+
+QTEST_MAIN(tst_QOpenGLWindow)