summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/opengl/hellogl_es2/hellogl_es2.pro23
-rw-r--r--examples/opengl/hellogl_es2/mainwindow.cpp107
-rw-r--r--examples/opengl/opengl.pro11
-rw-r--r--examples/opengl/qopenglwidget/bubble.cpp (renamed from examples/opengl/hellogl_es2/bubble.cpp)2
-rw-r--r--examples/opengl/qopenglwidget/bubble.h (renamed from examples/opengl/hellogl_es2/bubble.h)2
-rw-r--r--examples/opengl/qopenglwidget/glwidget.cpp (renamed from examples/opengl/hellogl_es2/glwidget.cpp)84
-rw-r--r--examples/opengl/qopenglwidget/glwidget.h (renamed from examples/opengl/hellogl_es2/glwidget.h)41
-rw-r--r--examples/opengl/qopenglwidget/main.cpp (renamed from examples/opengl/hellogl_es2/main.cpp)2
-rw-r--r--examples/opengl/qopenglwidget/mainwindow.cpp152
-rw-r--r--examples/opengl/qopenglwidget/mainwindow.h66
-rw-r--r--examples/opengl/qopenglwidget/qopenglwidget.pro15
-rw-r--r--examples/opengl/qopenglwidget/qt.png (renamed from examples/opengl/hellogl_es2/qt.png)bin5174 -> 5174 bytes
-rw-r--r--examples/opengl/qopenglwidget/texture.qrc (renamed from examples/opengl/hellogl_es2/texture.qrc)0
-rw-r--r--examples/opengl/threadedqopenglwidget/glwidget.cpp356
-rw-r--r--examples/opengl/threadedqopenglwidget/glwidget.h124
-rw-r--r--examples/opengl/threadedqopenglwidget/main.cpp69
-rw-r--r--examples/opengl/threadedqopenglwidget/mainwindow.cpp52
-rw-r--r--examples/opengl/threadedqopenglwidget/mainwindow.h (renamed from examples/opengl/hellogl_es2/mainwindow.h)8
-rw-r--r--examples/opengl/threadedqopenglwidget/threadedqopenglwidget.pro11
-rw-r--r--examples/widgets/graphicsview/padnavigator/padnavigator.cpp2
-rw-r--r--src/corelib/global/qnamespace.h2
-rw-r--r--src/corelib/global/qnamespace.qdoc7
-rw-r--r--src/gui/opengl/qopenglpaintdevice.cpp12
-rw-r--r--src/gui/opengl/qopenglpaintengine.cpp2
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp48
-rw-r--r--src/gui/painting/qplatformbackingstore.h3
-rw-r--r--src/opengl/qgl.cpp7
-rw-r--r--src/platformsupport/eglconvenience/qeglcompositor.cpp11
-rw-r--r--src/platformsupport/eglconvenience/qeglplatformbackingstore.cpp7
-rw-r--r--src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp109
-rw-r--r--src/widgets/graphicsview/qgraphicsview.cpp2
-rw-r--r--src/widgets/kernel/kernel.pri2
-rw-r--r--src/widgets/kernel/qopenglwidget.cpp852
-rw-r--r--src/widgets/kernel/qopenglwidget.h (renamed from src/widgets/kernel/qopenglwidget_p.h)73
-rw-r--r--src/widgets/kernel/qwidget.cpp63
-rw-r--r--src/widgets/kernel/qwidget_p.h13
-rw-r--r--src/widgets/kernel/qwidgetbackingstore.cpp100
-rw-r--r--src/widgets/kernel/qwidgetbackingstore_p.h16
-rw-r--r--src/widgets/widgets/qabstractscrollarea.cpp4
-rw-r--r--tests/auto/widgets/widgets/qopenglwidget/qopenglwidget.pro8
-rw-r--r--tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp266
-rw-r--r--tests/auto/widgets/widgets/widgets.pro2
-rw-r--r--tests/manual/qopenglwidget/openglwidget/main.cpp17
-rw-r--r--tests/manual/qopenglwidget/openglwidget/openglwidget.cpp7
-rw-r--r--tests/manual/qopenglwidget/openglwidget/openglwidget.h2
45 files changed, 2388 insertions, 374 deletions
diff --git a/examples/opengl/hellogl_es2/hellogl_es2.pro b/examples/opengl/hellogl_es2/hellogl_es2.pro
deleted file mode 100644
index b15e8d1909..0000000000
--- a/examples/opengl/hellogl_es2/hellogl_es2.pro
+++ /dev/null
@@ -1,23 +0,0 @@
-QT += opengl widgets
-
-SOURCES += main.cpp
-SOURCES += glwidget.cpp
-SOURCES += mainwindow.cpp
-SOURCES += bubble.cpp
-
-HEADERS += glwidget.h
-HEADERS += mainwindow.h
-HEADERS += bubble.h
-
-RESOURCES += texture.qrc
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/opengl/hellogl_es2
-INSTALLS += target
-
-maemo5 {
- # Debian package name may not contain numbers or special characters
- # such as '_', lets change this in Maemo.
- TARGET = helloglestwo
- CONFIG += qt_example
-}
diff --git a/examples/opengl/hellogl_es2/mainwindow.cpp b/examples/opengl/hellogl_es2/mainwindow.cpp
deleted file mode 100644
index eb4ce0ac3e..0000000000
--- a/examples/opengl/hellogl_es2/mainwindow.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 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 "mainwindow.h"
-
-#include <QApplication>
-#include <QMenuBar>
-#include <QGroupBox>
-#include <QGridLayout>
-#include <QSlider>
-#include <QLabel>
-#include <QTimer>
-
-#include "glwidget.h"
-
-MainWindow::MainWindow()
-{
- GLWidget *glwidget = new GLWidget();
- QLabel *label = new QLabel(this);
- QTimer *timer = new QTimer(this);
- QSlider *slider = new QSlider(this);
- slider->setOrientation(Qt::Horizontal);
-
- slider->setRange(0, 100);
- slider->setSliderPosition(50);
- timer->setInterval(10);
- label->setText("A QGlWidget with OpenGl ES");
- label->setAlignment(Qt::AlignHCenter);
-
- QGroupBox * groupBox = new QGroupBox(this);
- setCentralWidget(groupBox);
- groupBox->setTitle("OpenGL ES Example");
-
- QGridLayout *layout = new QGridLayout(groupBox);
-
- layout->addWidget(glwidget,1,0,8,1);
- layout->addWidget(label,9,0,1,1);
- layout->addWidget(slider, 11,0,1,1);
-
- groupBox->setLayout(layout);
-
- QMenu *fileMenu = new QMenu("File");
- QMenu *helpMenu = new QMenu("Help");
- QMenu *showMenu = new QMenu("Show");
- menuBar()->addMenu(fileMenu);
- menuBar()->addMenu(showMenu);
- menuBar()->addMenu(helpMenu);
- QAction *exit = new QAction("Exit", fileMenu);
- QAction *aboutQt = new QAction("AboutQt", helpMenu);
- QAction *showLogo = new QAction("Show 3D Logo", showMenu);
- QAction *showTexture = new QAction("Show 2D Texture", showMenu);
- QAction *showBubbles = new QAction("Show bubbles", showMenu);
- showBubbles->setCheckable(true);
- showBubbles->setChecked(true);
- fileMenu->addAction(exit);
- helpMenu->addAction(aboutQt);
- showMenu->addAction(showLogo);
- showMenu->addAction(showTexture);
- showMenu->addAction(showBubbles);
-
- QObject::connect(timer, SIGNAL(timeout()), glwidget, SLOT(updateGL()));
- QObject::connect(exit, SIGNAL(triggered(bool)), this, SLOT(close()));
- QObject::connect(aboutQt, SIGNAL(triggered(bool)), qApp, SLOT(aboutQt()));
-
- QObject::connect(showLogo, SIGNAL(triggered(bool)), glwidget, SLOT(setLogo()));
- QObject::connect(showTexture, SIGNAL(triggered(bool)), glwidget, SLOT(setTexture()));
- QObject::connect(showBubbles, SIGNAL(triggered(bool)), glwidget, SLOT(showBubbles(bool)));
- QObject::connect(slider, SIGNAL(valueChanged(int)), glwidget, SLOT(setScaling(int)));
- timer->start();
-}
diff --git a/examples/opengl/opengl.pro b/examples/opengl/opengl.pro
index aea281ce5c..fb5f02c07c 100644
--- a/examples/opengl/opengl.pro
+++ b/examples/opengl/opengl.pro
@@ -5,10 +5,9 @@ TEMPLATE = subdirs
contains(QT_CONFIG, dynamicgl) {
SUBDIRS = hellowindow \
contextinfo \
- hellogl_es2
-} else: contains(QT_CONFIG, opengles2){
- SUBDIRS = hellogl_es2
-} else {
+ qopenglwidget \
+ threadedqopenglwidget
+} else: !contains(QT_CONFIG, opengles2) {
SUBDIRS = 2dpainting \
grabber \
hellogl \
@@ -22,6 +21,8 @@ contains(QT_CONFIG, dynamicgl) {
paintedwindow \
contextinfo \
cube \
- textures
+ textures \
+ qopenglwidget \
+ threadedqopenglwidget
EXAMPLE_FILES = shared
diff --git a/examples/opengl/hellogl_es2/bubble.cpp b/examples/opengl/qopenglwidget/bubble.cpp
index 3e0f053bc8..bf04c64966 100644
--- a/examples/opengl/hellogl_es2/bubble.cpp
+++ b/examples/opengl/qopenglwidget/bubble.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
diff --git a/examples/opengl/hellogl_es2/bubble.h b/examples/opengl/qopenglwidget/bubble.h
index 60195b4415..ea33466ef7 100644
--- a/examples/opengl/hellogl_es2/bubble.h
+++ b/examples/opengl/qopenglwidget/bubble.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
diff --git a/examples/opengl/hellogl_es2/glwidget.cpp b/examples/opengl/qopenglwidget/glwidget.cpp
index 733475d321..e95ca363fc 100644
--- a/examples/opengl/hellogl_es2/glwidget.cpp
+++ b/examples/opengl/qopenglwidget/glwidget.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
@@ -43,19 +43,25 @@
#include <QPaintEngine>
#include <math.h>
+#include "mainwindow.h"
#include "bubble.h"
-
const int bubbleNum = 8;
-GLWidget::GLWidget(QWidget *parent)
- : QGLWidget(parent)
+GLWidget::GLWidget(MainWindow *mw, bool button, const QColor &background)
+ : m_mainWindow(mw),
+ m_transparent(false),
+ m_btn(0),
+ m_hasButton(button),
+ m_background(background)
{
+ QSurfaceFormat format;
+ format.setDepthBufferSize(24);
+ format.setStencilBufferSize(8);
+ setFormat(format);
+
qtLogo = true;
frames = 0;
- setAttribute(Qt::WA_PaintOnScreen);
- setAttribute(Qt::WA_NoSystemBackground);
- setAutoBufferSwap(false);
m_showBubbles = true;
setMinimumSize(300, 250);
}
@@ -66,10 +72,10 @@ GLWidget::~GLWidget()
void GLWidget::setScaling(int scale) {
- if (scale > 50)
- m_fScale = 1 + qreal(scale -50) / 50 * 0.5;
- else if (scale < 50)
- m_fScale = 1- (qreal(50 - scale) / 50 * 1/2);
+ if (scale > 30)
+ m_fScale = 1 + qreal(scale - 30) / 30 * 0.25;
+ else if (scale < 30)
+ m_fScale = 1 - (qreal(30 - scale) / 30 * 0.25);
else
m_fScale = 1;
}
@@ -173,12 +179,14 @@ void GLWidget::initializeGL ()
{
initializeOpenGLFunctions();
- glClearColor(0.1f, 0.1f, 0.2f, 1.0f);
-
glGenTextures(1, &m_uiTexture);
- m_uiTexture = bindTexture(QImage(":/qt.png"));
+ QImage img = QImage(":/qt.png").convertToFormat(QImage::Format_RGBA8888);
+ glBindTexture(GL_TEXTURE_2D, m_uiTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits());
- QGLShader *vshader1 = new QGLShader(QGLShader::Vertex, this);
+ QOpenGLShader *vshader1 = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc1 =
"attribute highp vec4 vertex;\n"
"attribute mediump vec3 normal;\n"
@@ -195,7 +203,7 @@ void GLWidget::initializeGL ()
"}\n";
vshader1->compileSourceCode(vsrc1);
- QGLShader *fshader1 = new QGLShader(QGLShader::Fragment, this);
+ QOpenGLShader *fshader1 = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc1 =
"varying mediump vec4 color;\n"
"void main(void)\n"
@@ -212,7 +220,7 @@ void GLWidget::initializeGL ()
normalAttr1 = program1.attributeLocation("normal");
matrixUniform1 = program1.uniformLocation("matrix");
- QGLShader *vshader2 = new QGLShader(QGLShader::Vertex);
+ QOpenGLShader *vshader2 = new QOpenGLShader(QOpenGLShader::Vertex);
const char *vsrc2 =
"attribute highp vec4 vertex;\n"
"attribute highp vec4 texCoord;\n"
@@ -229,7 +237,7 @@ void GLWidget::initializeGL ()
"}\n";
vshader2->compileSourceCode(vsrc2);
- QGLShader *fshader2 = new QGLShader(QGLShader::Fragment);
+ QOpenGLShader *fshader2 = new QOpenGLShader(QOpenGLShader::Fragment);
const char *fsrc2 =
"varying highp vec4 texc;\n"
"uniform sampler2D tex;\n"
@@ -252,9 +260,6 @@ void GLWidget::initializeGL ()
matrixUniform2 = program2.uniformLocation("matrix");
textureUniform2 = program2.uniformLocation("tex");
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-
m_fAngle = 0;
m_fScale = 1;
createGeometry();
@@ -270,12 +275,10 @@ void GLWidget::paintGL()
painter.beginNativePainting();
- glClearColor(0.1f, 0.1f, 0.2f, 1.0f);
+ glClearColor(m_background.red() / 255.0f, m_background.green() / 255.0f,
+ m_background.blue() / 255.0f, m_transparent ? 0.0f : 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-
glFrontFace(GL_CW);
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
@@ -313,14 +316,12 @@ void GLWidget::paintGL()
if (const int elapsed = time.elapsed()) {
QString framesPerSecond;
framesPerSecond.setNum(frames /(elapsed / 1000.0), 'f', 2);
- painter.setPen(Qt::white);
- painter.drawText(20, 40, framesPerSecond + " fps");
+ painter.setPen(m_transparent ? Qt::black : Qt::white);
+ painter.drawText(20, 40, framesPerSecond + " paintGL calls / s");
}
painter.end();
- swapBuffers();
-
QMutableListIterator<Bubble*> iter(bubbles);
while (iter.hasNext()) {
@@ -461,3 +462,28 @@ void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
normals << n;
normals << n;
}
+
+void GLWidget::setTransparent(bool transparent)
+{
+ setAttribute(Qt::WA_AlwaysStackOnTop, transparent);
+ m_transparent = transparent;
+ // Call update() on the top-level window after toggling AlwayStackOnTop to make sure
+ // the entire backingstore is updated accordingly.
+ window()->update();
+}
+
+void GLWidget::resizeGL(int w, int h)
+{
+ if (m_hasButton) {
+ if (!m_btn) {
+ m_btn = new QPushButton("A widget on top.\nPress me!", this);
+ connect(m_btn, &QPushButton::clicked, this, &GLWidget::handleButtonPress);
+ }
+ m_btn->move(w / 2, h / 2);
+ }
+}
+
+void GLWidget::handleButtonPress()
+{
+ m_mainWindow->addNew();
+}
diff --git a/examples/opengl/hellogl_es2/glwidget.h b/examples/opengl/qopenglwidget/glwidget.h
index 00073aa047..6c43ac1576 100644
--- a/examples/opengl/hellogl_es2/glwidget.h
+++ b/examples/opengl/qopenglwidget/glwidget.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
@@ -41,33 +41,45 @@
#ifndef GLWIDGET_H
#define GLWIDGET_H
-#include <QGLWidget>
+#include <QOpenGLWidget>
#include <QOpenGLFunctions>
-#include <QGLShaderProgram>
+#include <QOpenGLShaderProgram>
#include <QVector3D>
#include <QMatrix4x4>
#include <QTime>
#include <QVector>
+#include <QPushButton>
class Bubble;
-class GLWidget : public QGLWidget, protected QOpenGLFunctions
+class MainWindow;
+
+class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
- GLWidget(QWidget *parent = 0);
+ GLWidget(MainWindow *mw, bool button, const QColor &background);
~GLWidget();
+
public slots:
void setScaling(int scale);
void setLogo();
void setTexture();
void showBubbles(bool);
+ void setTransparent(bool transparent);
+
+private slots:
+ void handleButtonPress();
+
protected:
- void paintGL ();
- void initializeGL ();
+ void resizeGL(int w, int h) Q_DECL_OVERRIDE;
+ void paintGL() Q_DECL_OVERRIDE;
+ void initializeGL() Q_DECL_OVERRIDE;
+
private:
- GLuint m_uiTexture;
- qreal m_fAngle;
- qreal m_fScale;
+ MainWindow *m_mainWindow;
+ GLuint m_uiTexture;
+ qreal m_fAngle;
+ qreal m_fScale;
bool m_showBubbles;
void paintTexturedCube();
void paintQtLogo();
@@ -81,8 +93,8 @@ private:
QList<Bubble*> bubbles;
int frames;
QTime time;
- QGLShaderProgram program1;
- QGLShaderProgram program2;
+ QOpenGLShaderProgram program1;
+ QOpenGLShaderProgram program2;
int vertexAttr1;
int normalAttr1;
int matrixUniform1;
@@ -91,5 +103,10 @@ private:
int texCoordAttr2;
int matrixUniform2;
int textureUniform2;
+ bool m_transparent;
+ QPushButton *m_btn;
+ bool m_hasButton;
+ QColor m_background;
};
+
#endif
diff --git a/examples/opengl/hellogl_es2/main.cpp b/examples/opengl/qopenglwidget/main.cpp
index 5fb3385989..2156e60155 100644
--- a/examples/opengl/hellogl_es2/main.cpp
+++ b/examples/opengl/qopenglwidget/main.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
diff --git a/examples/opengl/qopenglwidget/mainwindow.cpp b/examples/opengl/qopenglwidget/mainwindow.cpp
new file mode 100644
index 0000000000..f09acacaf0
--- /dev/null
+++ b/examples/opengl/qopenglwidget/mainwindow.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** 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 "mainwindow.h"
+
+#include <QApplication>
+#include <QMenuBar>
+#include <QGroupBox>
+#include <QSlider>
+#include <QLabel>
+#include <QCheckBox>
+#include <QSpinBox>
+
+#include "glwidget.h"
+
+MainWindow::MainWindow()
+ : m_nextX(1), m_nextY(1)
+{
+ GLWidget *glwidget = new GLWidget(this, true, qRgb(20, 20, 50));
+ QLabel *label = new QLabel(this);
+ m_timer = new QTimer(this);
+ QSlider *slider = new QSlider(this);
+ slider->setOrientation(Qt::Horizontal);
+
+ QLabel *updateLabel = new QLabel("Update interval");
+ QSpinBox *updateInterval = new QSpinBox(this);
+ updateInterval->setSuffix(" ms");
+ updateInterval->setValue(10);
+ updateInterval->setToolTip("Interval for the timer that calls update().\n"
+ "Note that on most systems the swap will block to wait for vsync\n"
+ "and therefore an interval < 16 ms will likely lead to a 60 FPS update rate.");
+ QGroupBox *updateGroupBox = new QGroupBox(this);
+ QCheckBox *transparent = new QCheckBox("Transparent background", this);
+ transparent->setToolTip("Toggles Qt::WA_AlwaysStackOnTop and transparent clear color for glClear().\n"
+ "Note how the button on top stacks incorrectly when enabling this.");
+ QHBoxLayout *updateLayout = new QHBoxLayout;
+ updateLayout->addWidget(updateLabel);
+ updateLayout->addWidget(updateInterval);
+ updateLayout->addWidget(transparent);
+ updateGroupBox->setLayout(updateLayout);
+
+ slider->setRange(0, 50);
+ slider->setSliderPosition(30);
+ m_timer->setInterval(10);
+ label->setText("A QOpenGLWidget");
+ label->setAlignment(Qt::AlignHCenter);
+
+ QGroupBox * groupBox = new QGroupBox(this);
+ setCentralWidget(groupBox);
+ groupBox->setTitle("QOpenGLWidget Example");
+
+ m_layout = new QGridLayout(groupBox);
+
+ m_layout->addWidget(glwidget,1,0,8,1);
+ m_layout->addWidget(label,9,0,1,1);
+ m_layout->addWidget(updateGroupBox, 10, 0, 1, 1);
+ m_layout->addWidget(slider, 11,0,1,1);
+
+ groupBox->setLayout(m_layout);
+
+ QMenu *fileMenu = new QMenu("&File");
+ QMenu *helpMenu = new QMenu("&Help");
+ QMenu *showMenu = new QMenu("&Show");
+ menuBar()->addMenu(fileMenu);
+ menuBar()->addMenu(showMenu);
+ menuBar()->addMenu(helpMenu);
+ QAction *exit = new QAction("E&xit", fileMenu);
+ QAction *aboutQt = new QAction("About Qt", helpMenu);
+ QAction *showLogo = new QAction("Show 3D Logo", showMenu);
+ QAction *showTexture = new QAction("Show 2D Texture", showMenu);
+ QAction *showBubbles = new QAction("Show bubbles", showMenu);
+ showBubbles->setCheckable(true);
+ showBubbles->setChecked(true);
+ fileMenu->addAction(exit);
+ helpMenu->addAction(aboutQt);
+ showMenu->addAction(showLogo);
+ showMenu->addAction(showTexture);
+ showMenu->addAction(showBubbles);
+
+ connect(exit, SIGNAL(triggered(bool)), this, SLOT(close()));
+ connect(aboutQt, SIGNAL(triggered(bool)), qApp, SLOT(aboutQt()));
+
+ connect(m_timer, SIGNAL(timeout()), glwidget, SLOT(update()));
+
+ connect(showLogo, SIGNAL(triggered(bool)), glwidget, SLOT(setLogo()));
+ connect(showTexture, SIGNAL(triggered(bool)), glwidget, SLOT(setTexture()));
+ connect(showBubbles, SIGNAL(triggered(bool)), glwidget, SLOT(showBubbles(bool)));
+ connect(slider, SIGNAL(valueChanged(int)), glwidget, SLOT(setScaling(int)));
+ connect(transparent, &QCheckBox::toggled, glwidget, &GLWidget::setTransparent);
+
+ connect(updateInterval, SIGNAL(valueChanged(int)), this, SLOT(updateIntervalChanged(int)));
+
+ m_timer->start();
+}
+
+void MainWindow::updateIntervalChanged(int value)
+{
+ m_timer->setInterval(value);
+ m_timer->start();
+}
+
+void MainWindow::addNew()
+{
+ if (m_nextY == 4)
+ return;
+ GLWidget *w = new GLWidget(this, false, qRgb(qrand() % 256, qrand() % 256, qrand() % 256));
+ connect(m_timer, SIGNAL(timeout()), w, SLOT(update()));
+ m_layout->addWidget(w, m_nextY, m_nextX, 1, 1);
+ if (m_nextX == 3) {
+ m_nextX = 1;
+ ++m_nextY;
+ } else {
+ ++m_nextX;
+ }
+}
diff --git a/examples/opengl/qopenglwidget/mainwindow.h b/examples/opengl/qopenglwidget/mainwindow.h
new file mode 100644
index 0000000000..9db3e8cbec
--- /dev/null
+++ b/examples/opengl/qopenglwidget/mainwindow.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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 MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QTimer>
+#include <QGridLayout>
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+ void addNew();
+
+private slots:
+ void updateIntervalChanged(int value);
+
+private:
+ QTimer *m_timer;
+ QGridLayout *m_layout;
+ int m_nextX;
+ int m_nextY;
+};
+
+#endif
diff --git a/examples/opengl/qopenglwidget/qopenglwidget.pro b/examples/opengl/qopenglwidget/qopenglwidget.pro
new file mode 100644
index 0000000000..0165285c02
--- /dev/null
+++ b/examples/opengl/qopenglwidget/qopenglwidget.pro
@@ -0,0 +1,15 @@
+QT += widgets
+
+SOURCES += main.cpp \
+ glwidget.cpp \
+ mainwindow.cpp \
+ bubble.cpp
+
+HEADERS += glwidget.h \
+ mainwindow.h \
+ bubble.h
+
+RESOURCES += texture.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwidget
+INSTALLS += target
diff --git a/examples/opengl/hellogl_es2/qt.png b/examples/opengl/qopenglwidget/qt.png
index 79e383cf50..79e383cf50 100644
--- a/examples/opengl/hellogl_es2/qt.png
+++ b/examples/opengl/qopenglwidget/qt.png
Binary files differ
diff --git a/examples/opengl/hellogl_es2/texture.qrc b/examples/opengl/qopenglwidget/texture.qrc
index ff1d0e535f..ff1d0e535f 100644
--- a/examples/opengl/hellogl_es2/texture.qrc
+++ b/examples/opengl/qopenglwidget/texture.qrc
diff --git a/examples/opengl/threadedqopenglwidget/glwidget.cpp b/examples/opengl/threadedqopenglwidget/glwidget.cpp
new file mode 100644
index 0000000000..34d97bf1bf
--- /dev/null
+++ b/examples/opengl/threadedqopenglwidget/glwidget.cpp
@@ -0,0 +1,356 @@
+/****************************************************************************
+**
+** 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 "glwidget.h"
+#include <math.h>
+#include <QGuiApplication>
+
+const int bubbleNum = 8;
+
+GLWidget::GLWidget(QWidget *parent)
+ : QOpenGLWidget(parent)
+{
+ QSurfaceFormat format;
+ format.setDepthBufferSize(16);
+ setFormat(format);
+
+ setMinimumSize(300, 250);
+
+ connect(this, &QOpenGLWidget::aboutToCompose, this, &GLWidget::onAboutToCompose);
+ connect(this, &QOpenGLWidget::frameSwapped, this, &GLWidget::onFrameSwapped);
+ connect(this, &QOpenGLWidget::aboutToResize, this, &GLWidget::onAboutToResize);
+ connect(this, &QOpenGLWidget::resized, this, &GLWidget::onResized);
+
+ m_thread = new QThread;
+ m_renderer = new Renderer(this);
+ m_renderer->moveToThread(m_thread);
+ connect(m_thread, &QThread::finished, m_renderer, &QObject::deleteLater);
+
+ connect(this, &GLWidget::renderRequested, m_renderer, &Renderer::render);
+ connect(m_renderer, &Renderer::contextWanted, this, &GLWidget::grabContext);
+
+ m_thread->start();
+}
+
+GLWidget::~GLWidget()
+{
+ m_renderer->prepareExit();
+ m_thread->quit();
+ m_thread->wait();
+ delete m_thread;
+}
+
+void GLWidget::onAboutToCompose()
+{
+ // We are on the gui thread here. Composition is about to
+ // begin. Wait until the render thread finishes.
+ m_renderer->lockRenderer();
+}
+
+void GLWidget::onFrameSwapped()
+{
+ m_renderer->unlockRenderer();
+ // Assuming a blocking swap, our animation is driven purely by the
+ // vsync in this example.
+ emit renderRequested();
+}
+
+void GLWidget::onAboutToResize()
+{
+ m_renderer->lockRenderer();
+}
+
+void GLWidget::onResized()
+{
+ m_renderer->unlockRenderer();
+}
+
+void GLWidget::grabContext()
+{
+ m_renderer->lockRenderer();
+ QMutexLocker lock(m_renderer->grabMutex());
+ context()->moveToThread(m_thread);
+ m_renderer->grabCond()->wakeAll();
+ m_renderer->unlockRenderer();
+}
+
+Renderer::Renderer(GLWidget *w)
+ : m_inited(false),
+ m_glwidget(w),
+ m_exiting(false)
+{
+}
+
+void Renderer::paintQtLogo()
+{
+ program.enableAttributeArray(normalAttr);
+ program.enableAttributeArray(vertexAttr);
+ program.setAttributeArray(vertexAttr, vertices.constData());
+ program.setAttributeArray(normalAttr, normals.constData());
+ glDrawArrays(GL_TRIANGLES, 0, vertices.size());
+ program.disableAttributeArray(normalAttr);
+ program.disableAttributeArray(vertexAttr);
+}
+
+// Some OpenGL implementations have serious issues with compiling and linking
+// shaders on multiple threads concurrently. Avoid this.
+Q_GLOBAL_STATIC(QMutex, initMutex)
+
+void Renderer::render()
+{
+ if (m_exiting)
+ return;
+
+ QOpenGLContext *ctx = m_glwidget->context();
+ if (!ctx) // QOpenGLWidget not yet initialized
+ return;
+
+ // Grab the context.
+ m_grabMutex.lock();
+ emit contextWanted();
+ m_grabCond.wait(&m_grabMutex);
+ QMutexLocker lock(&m_renderMutex);
+ m_grabMutex.unlock();
+
+ if (m_exiting)
+ return;
+
+ Q_ASSERT(ctx->thread() == QThread::currentThread());
+
+ // Make the context (and an offscreen surface) current for this thread. The
+ // QOpenGLWidget's fbo is bound in the context.
+ m_glwidget->makeCurrent();
+
+ if (!m_inited) {
+ m_inited = true;
+ initializeOpenGLFunctions();
+
+ QMutexLocker initLock(initMutex());
+ QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
+ const char *vsrc =
+ "attribute highp vec4 vertex;\n"
+ "attribute mediump vec3 normal;\n"
+ "uniform mediump mat4 matrix;\n"
+ "varying mediump vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
+ " float angle = max(dot(normal, toLight), 0.0);\n"
+ " vec3 col = vec3(0.40, 1.0, 0.0);\n"
+ " color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
+ " color = clamp(color, 0.0, 1.0);\n"
+ " gl_Position = matrix * vertex;\n"
+ "}\n";
+ vshader->compileSourceCode(vsrc);
+
+ QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
+ const char *fsrc =
+ "varying mediump vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+ fshader->compileSourceCode(fsrc);
+
+ program.addShader(vshader);
+ program.addShader(fshader);
+ program.link();
+
+ vertexAttr = program.attributeLocation("vertex");
+ normalAttr = program.attributeLocation("normal");
+ matrixUniform = program.uniformLocation("matrix");
+
+ m_fAngle = 0;
+ m_fScale = 1;
+ createGeometry();
+
+ m_elapsed.start();
+ }
+
+ //qDebug("%p elapsed %lld", QThread::currentThread(), m_elapsed.restart());
+
+ glClearColor(0.1f, 0.2f, 0.2f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glFrontFace(GL_CW);
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+
+ QMatrix4x4 modelview;
+ modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f);
+ modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f);
+ modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f);
+ modelview.scale(m_fScale);
+ modelview.translate(0.0f, -0.2f, 0.0f);
+
+ program.bind();
+ program.setUniformValue(matrixUniform, modelview);
+ paintQtLogo();
+ program.release();
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ m_fAngle += 1.0f;
+
+ // Make no context current on this thread and move the QOpenGLWidget's
+ // context back to the gui thread.
+ m_glwidget->doneCurrent();
+ ctx->moveToThread(qGuiApp->thread());
+
+ // Schedule composition. Note that this will use QueuedConnection, meaning
+ // that update() will be invoked on the gui thread.
+ QMetaObject::invokeMethod(m_glwidget, "update");
+}
+
+void Renderer::createGeometry()
+{
+ vertices.clear();
+ normals.clear();
+
+ qreal x1 = +0.06f;
+ qreal y1 = -0.14f;
+ qreal x2 = +0.14f;
+ qreal y2 = -0.06f;
+ qreal x3 = +0.08f;
+ qreal y3 = +0.00f;
+ qreal x4 = +0.30f;
+ qreal y4 = +0.22f;
+
+ quad(x1, y1, x2, y2, y2, x2, y1, x1);
+ quad(x3, y3, x4, y4, y4, x4, y3, x3);
+
+ extrude(x1, y1, x2, y2);
+ extrude(x2, y2, y2, x2);
+ extrude(y2, x2, y1, x1);
+ extrude(y1, x1, x1, y1);
+ extrude(x3, y3, x4, y4);
+ extrude(x4, y4, y4, x4);
+ extrude(y4, x4, y3, x3);
+
+ const qreal Pi = 3.14159f;
+ const int NumSectors = 100;
+
+ for (int i = 0; i < NumSectors; ++i) {
+ qreal angle1 = (i * 2 * Pi) / NumSectors;
+ qreal x5 = 0.30 * sin(angle1);
+ qreal y5 = 0.30 * cos(angle1);
+ qreal x6 = 0.20 * sin(angle1);
+ qreal y6 = 0.20 * cos(angle1);
+
+ qreal angle2 = ((i + 1) * 2 * Pi) / NumSectors;
+ qreal x7 = 0.20 * sin(angle2);
+ qreal y7 = 0.20 * cos(angle2);
+ qreal x8 = 0.30 * sin(angle2);
+ qreal y8 = 0.30 * cos(angle2);
+
+ quad(x5, y5, x6, y6, x7, y7, x8, y8);
+
+ extrude(x6, y6, x7, y7);
+ extrude(x8, y8, x5, y5);
+ }
+
+ for (int i = 0;i < vertices.size();i++)
+ vertices[i] *= 2.0f;
+}
+
+void Renderer::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
+{
+ vertices << QVector3D(x1, y1, -0.05f);
+ vertices << QVector3D(x2, y2, -0.05f);
+ vertices << QVector3D(x4, y4, -0.05f);
+
+ vertices << QVector3D(x3, y3, -0.05f);
+ vertices << QVector3D(x4, y4, -0.05f);
+ vertices << QVector3D(x2, y2, -0.05f);
+
+ QVector3D n = QVector3D::normal
+ (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
+
+ normals << n;
+ normals << n;
+ normals << n;
+
+ normals << n;
+ normals << n;
+ normals << n;
+
+ vertices << QVector3D(x4, y4, 0.05f);
+ vertices << QVector3D(x2, y2, 0.05f);
+ vertices << QVector3D(x1, y1, 0.05f);
+
+ vertices << QVector3D(x2, y2, 0.05f);
+ vertices << QVector3D(x4, y4, 0.05f);
+ vertices << QVector3D(x3, y3, 0.05f);
+
+ n = QVector3D::normal
+ (QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
+
+ normals << n;
+ normals << n;
+ normals << n;
+
+ normals << n;
+ normals << n;
+ normals << n;
+}
+
+void Renderer::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
+{
+ vertices << QVector3D(x1, y1, +0.05f);
+ vertices << QVector3D(x2, y2, +0.05f);
+ vertices << QVector3D(x1, y1, -0.05f);
+
+ vertices << QVector3D(x2, y2, -0.05f);
+ vertices << QVector3D(x1, y1, -0.05f);
+ vertices << QVector3D(x2, y2, +0.05f);
+
+ QVector3D n = QVector3D::normal
+ (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
+
+ normals << n;
+ normals << n;
+ normals << n;
+
+ normals << n;
+ normals << n;
+ normals << n;
+}
diff --git a/examples/opengl/threadedqopenglwidget/glwidget.h b/examples/opengl/threadedqopenglwidget/glwidget.h
new file mode 100644
index 0000000000..9d746e681b
--- /dev/null
+++ b/examples/opengl/threadedqopenglwidget/glwidget.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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 GLWIDGET_H
+#define GLWIDGET_H
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QVector3D>
+#include <QMatrix4x4>
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QElapsedTimer>
+
+class GLWidget;
+
+class Renderer : public QObject, protected QOpenGLFunctions
+{
+ Q_OBJECT
+
+public:
+ Renderer(GLWidget *w);
+ void lockRenderer() { m_renderMutex.lock(); }
+ void unlockRenderer() { m_renderMutex.unlock(); }
+ QMutex *grabMutex() { return &m_grabMutex; }
+ QWaitCondition *grabCond() { return &m_grabCond; }
+ void prepareExit() { m_exiting = true; m_grabCond.wakeAll(); }
+
+signals:
+ void contextWanted();
+
+public slots:
+ void render();
+
+private:
+ void paintQtLogo();
+ void createGeometry();
+ void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4);
+ void extrude(qreal x1, qreal y1, qreal x2, qreal y2);
+
+ bool m_inited;
+ qreal m_fAngle;
+ qreal m_fScale;
+ QVector<QVector3D> vertices;
+ QVector<QVector3D> normals;
+ QOpenGLShaderProgram program;
+ int vertexAttr;
+ int normalAttr;
+ int matrixUniform;
+ GLWidget *m_glwidget;
+ QMutex m_renderMutex;
+ QElapsedTimer m_elapsed;
+ QMutex m_grabMutex;
+ QWaitCondition m_grabCond;
+ bool m_exiting;
+};
+
+class GLWidget : public QOpenGLWidget
+{
+ Q_OBJECT
+public:
+ GLWidget(QWidget *parent);
+ ~GLWidget();
+
+protected:
+ void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE { }
+
+signals:
+ void renderRequested();
+
+public slots:
+ void grabContext();
+
+private slots:
+ void onAboutToCompose();
+ void onFrameSwapped();
+ void onAboutToResize();
+ void onResized();
+
+private:
+ QThread *m_thread;
+ Renderer *m_renderer;
+};
+
+#endif
diff --git a/examples/opengl/threadedqopenglwidget/main.cpp b/examples/opengl/threadedqopenglwidget/main.cpp
new file mode 100644
index 0000000000..046a24d576
--- /dev/null
+++ b/examples/opengl/threadedqopenglwidget/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 <QApplication>
+#include <QMainWindow>
+#include "mainwindow.h"
+#include "glwidget.h"
+
+int main( int argc, char ** argv )
+{
+ QApplication a( argc, argv );
+
+ // Two top-level windows with two QOpenGLWidget children in each.
+ // The rendering for the four QOpenGLWidgets happens on four separate threads.
+ MainWindow mw1;
+ mw1.setMinimumSize(800, 400);
+ mw1.show();
+
+ QScopedPointer<MainWindow> mw2;
+ if (!QApplication::arguments().contains(QStringLiteral("--single"))) {
+ mw2.reset(new MainWindow);
+ mw2->setMinimumSize(800, 400);
+ mw2->show();
+
+ // And a top-level.
+ GLWidget *bonus = new GLWidget(0);
+ bonus->resize(200, 200);
+ bonus->show();
+ }
+
+ return a.exec();
+}
diff --git a/examples/opengl/threadedqopenglwidget/mainwindow.cpp b/examples/opengl/threadedqopenglwidget/mainwindow.cpp
new file mode 100644
index 0000000000..29c59573cf
--- /dev/null
+++ b/examples/opengl/threadedqopenglwidget/mainwindow.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** 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 "mainwindow.h"
+#include "glwidget.h"
+
+MainWindow::MainWindow()
+{
+ GLWidget *glwidget1 = new GLWidget(this);
+ glwidget1->resize(400, 400);
+
+ GLWidget *glwidget2 = new GLWidget(this);
+ glwidget2->resize(400, 400);
+ glwidget2->move(400, 0);
+}
diff --git a/examples/opengl/hellogl_es2/mainwindow.h b/examples/opengl/threadedqopenglwidget/mainwindow.h
index 3f05fd4a78..91f7580717 100644
--- a/examples/opengl/hellogl_es2/mainwindow.h
+++ b/examples/opengl/threadedqopenglwidget/mainwindow.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
@@ -41,16 +41,14 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include <QMainWindow>
+#include <QWidget>
-class MainWindow : public QMainWindow
+class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow();
-
-private:
};
#endif
diff --git a/examples/opengl/threadedqopenglwidget/threadedqopenglwidget.pro b/examples/opengl/threadedqopenglwidget/threadedqopenglwidget.pro
new file mode 100644
index 0000000000..4e941b28c4
--- /dev/null
+++ b/examples/opengl/threadedqopenglwidget/threadedqopenglwidget.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+SOURCES += main.cpp \
+ glwidget.cpp \
+ mainwindow.cpp
+
+HEADERS += glwidget.h \
+ mainwindow.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/opengl/threadedqopenglwidget
+INSTALLS += target
diff --git a/examples/widgets/graphicsview/padnavigator/padnavigator.cpp b/examples/widgets/graphicsview/padnavigator/padnavigator.cpp
index 82671578fb..977b65395a 100644
--- a/examples/widgets/graphicsview/padnavigator/padnavigator.cpp
+++ b/examples/widgets/graphicsview/padnavigator/padnavigator.cpp
@@ -292,7 +292,7 @@ PadNavigator::PadNavigator(const QSize &size, QWidget *parent)
| QPainter::SmoothPixmapTransform
| QPainter::TextAntialiasing);
#ifndef QT_NO_OPENGL
- setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
+ setViewport(new QOpenGLWidget);
#endif
stateMachine->start();
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 3ec859dbb7..1b92a0e845 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -499,6 +499,8 @@ public:
WA_X11DoNotAcceptFocus = 126,
WA_MacNoShadow = 127,
+ WA_AlwaysStackOnTop = 128,
+
// Add new attributes before this line
WA_AttributeCount
};
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index be569bcffe..f6ea3a3a81 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -1203,6 +1203,13 @@
to this top level window. This attribute has no effect on non-X11
platforms.
+ \value WA_AlwaysStackOnTop Since Qt 5.4, this value forces QOpenGLWidget and
+ QQuickWidget to be drawn last, on top of other widgets. Ignored for other
+ type of widgets. Setting this attribute breaks the stacking order, but
+ allows having a semi-transparent OpenGL widget with other widgets visible
+ underneath. It is strongly recommended to call update() on the widget's
+ top-level window after enabling or disabling this attribute.
+
\omitvalue WA_SetLayoutDirection
\omitvalue WA_InputMethodTransparent
\omitvalue WA_WState_CompressKeys
diff --git a/src/gui/opengl/qopenglpaintdevice.cpp b/src/gui/opengl/qopenglpaintdevice.cpp
index 6750458f83..96fc992b45 100644
--- a/src/gui/opengl/qopenglpaintdevice.cpp
+++ b/src/gui/opengl/qopenglpaintdevice.cpp
@@ -63,9 +63,9 @@ QT_BEGIN_NAMESPACE
\ingroup painting-3D
- The QOpenGLPaintDevice uses the current QOpenGL context to render
- QPainter draw commands. It requires OpenGL (ES) 2.0 support or
- higher.
+ The QOpenGLPaintDevice uses the \b current QOpenGL context to render
+ QPainter draw commands. The context is captured upon construction. It
+ requires support for OpenGL (ES) 2.0 or higher.
\section1 Performance
@@ -359,9 +359,9 @@ bool QOpenGLPaintDevice::paintFlipped() const
}
/*!
- This virtual method is provided as a callback to allow re-binding a
- target frame buffer object when different QOpenGLPaintDevice instances
- are issuing draw calls alternately on the same OpenGL context.
+ This virtual method is provided as a callback to allow re-binding a target
+ frame buffer object or context when different QOpenGLPaintDevice instances
+ are issuing draw calls alternately.
QPainter::beginNativePainting will also trigger this method.
*/
diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp
index de5b6be492..d0590ca521 100644
--- a/src/gui/opengl/qopenglpaintengine.cpp
+++ b/src/gui/opengl/qopenglpaintengine.cpp
@@ -1972,6 +1972,8 @@ bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev)
if (!d->device)
return false;
+ d->device->ensureActiveTarget();
+
if (d->device->context() != QOpenGLContext::currentContext()) {
qWarning("QPainter::begin(): QOpenGLPaintDevice's context needs to be current");
return false;
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index 3728b0bddd..e5b06f499c 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -90,6 +90,7 @@ struct QBackingstoreTextureInfo
{
GLuint textureId;
QRect rect;
+ bool stacksOnTop;
};
Q_DECLARE_TYPEINFO(QBackingstoreTextureInfo, Q_MOVABLE_TYPE);
@@ -127,6 +128,12 @@ GLuint QPlatformTextureList::textureId(int index) const
return d->textures.at(index).textureId;
}
+bool QPlatformTextureList::stacksOnTop(int index) const
+{
+ Q_D(const QPlatformTextureList);
+ return d->textures.at(index).stacksOnTop;
+}
+
QRect QPlatformTextureList::geometry(int index) const
{
Q_D(const QPlatformTextureList);
@@ -148,12 +155,13 @@ bool QPlatformTextureList::isLocked() const
return d->locked;
}
-void QPlatformTextureList::appendTexture(GLuint textureId, const QRect &geometry)
+void QPlatformTextureList::appendTexture(GLuint textureId, const QRect &geometry, bool stacksOnTop)
{
Q_D(QPlatformTextureList);
QBackingstoreTextureInfo bi;
bi.textureId = textureId;
bi.rect = geometry;
+ bi.stacksOnTop = stacksOnTop;
d->textures.append(bi);
}
@@ -238,29 +246,39 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
QRect windowRect(QPoint(), window->size() * window->devicePixelRatio());
+ // Textures for renderToTexture widgets.
for (int i = 0; i < textures->count(); ++i) {
- GLuint textureId = textures->textureId(i);
- funcs->glBindTexture(GL_TEXTURE_2D, textureId);
-
- QRect targetRect = deviceRect(textures->geometry(i), window);
- QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, windowRect);
- d_ptr->blitter->blit(textureId, target, QOpenGLTextureBlitter::OriginBottomLeft);
+ if (!textures->stacksOnTop(i)) {
+ QRect targetRect = deviceRect(textures->geometry(i), window);
+ QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, windowRect);
+ d_ptr->blitter->blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft);
+ }
}
- GLuint textureId = toTexture(deviceRegion(region, window), &d_ptr->textureSize);
- if (!textureId)
- return;
-
funcs->glEnable(GL_BLEND);
funcs->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(QRect(QPoint(), d_ptr->textureSize), windowRect);
- d_ptr->blitter->setSwizzleRB(true);
- d_ptr->blitter->blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
- d_ptr->blitter->setSwizzleRB(false);
+ // Backingstore texture with the normal widgets.
+ GLuint textureId = toTexture(deviceRegion(region, window), &d_ptr->textureSize);
+ if (textureId) {
+ QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(QRect(QPoint(), d_ptr->textureSize), windowRect);
+ d_ptr->blitter->setSwizzleRB(true);
+ d_ptr->blitter->blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
+ d_ptr->blitter->setSwizzleRB(false);
+ }
+
+ // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
+ for (int i = 0; i < textures->count(); ++i) {
+ if (textures->stacksOnTop(i)) {
+ QRect targetRect = deviceRect(textures->geometry(i), window);
+ QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, windowRect);
+ d_ptr->blitter->blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft);
+ }
+ }
funcs->glDisable(GL_BLEND);
d_ptr->blitter->release();
+
context->swapBuffers(window);
}
diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h
index 4728622cac..fe0f460ed9 100644
--- a/src/gui/painting/qplatformbackingstore.h
+++ b/src/gui/painting/qplatformbackingstore.h
@@ -84,10 +84,11 @@ public:
bool isEmpty() const { return count() == 0; }
GLuint textureId(int index) const;
QRect geometry(int index) const;
+ bool stacksOnTop(int index) const;
void lock(bool on);
bool isLocked() const;
- void appendTexture(GLuint textureId, const QRect &geometry);
+ void appendTexture(GLuint textureId, const QRect &geometry, bool stacksOnTop = false);
void clear();
Q_SIGNALS:
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index d1417cfde5..30e55592c5 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -3612,6 +3612,11 @@ void QGLContext::doneCurrent()
except that you have the choice between using QPainter and
standard OpenGL rendering commands.
+ \note This class is part of the legacy \l {Qt OpenGL} module and,
+ like the other \c QGL classes, should be avoided in the new
+ applications. Instead, starting from Qt 5.4, prefer using
+ QOpenGLWidget and the \c QOpenGL classes.
+
QGLWidget provides three convenient virtual functions that you can
reimplement in your subclass to perform the typical OpenGL tasks:
@@ -3807,7 +3812,7 @@ void QGLContext::doneCurrent()
\e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
countries.}
- \sa QGLPixelBuffer, {Hello GL Example}, {2D Painting Example}, {Overpainting Example},
+ \sa QOpenGLWidget, QGLPixelBuffer, {Hello GL Example}, {2D Painting Example}, {Overpainting Example},
{Grabber Example}
*/
diff --git a/src/platformsupport/eglconvenience/qeglcompositor.cpp b/src/platformsupport/eglconvenience/qeglcompositor.cpp
index 028f92f3cf..0deb8d3c39 100644
--- a/src/platformsupport/eglconvenience/qeglcompositor.cpp
+++ b/src/platformsupport/eglconvenience/qeglcompositor.cpp
@@ -142,7 +142,6 @@ void QEGLCompositor::render(QEGLPlatformWindow *window)
for (int i = 0; i < textures->count(); ++i) {
uint textureId = textures->textureId(i);
- glBindTexture(GL_TEXTURE_2D, textureId);
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i),
targetWindowRect);
const float opacity = window->window()->opacity();
@@ -160,13 +159,21 @@ void QEGLCompositor::render(QEGLPlatformWindow *window)
const bool translucent = window->window()->requestedFormat().alphaBufferSize() > 0;
blend.set(translucent);
m_blitter->blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
- } else {
+ } else if (!textures->stacksOnTop(i)) {
// Texture from an FBO belonging to a QOpenGLWidget
blend.set(false);
m_blitter->blit(textureId, target, QOpenGLTextureBlitter::OriginBottomLeft);
}
}
+ for (int i = 0; i < textures->count(); ++i) {
+ if (textures->stacksOnTop(i)) {
+ QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
+ blend.set(true);
+ m_blitter->blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft);
+ }
+ }
+
m_blitter->setOpacity(1.0f);
}
diff --git a/src/platformsupport/eglconvenience/qeglplatformbackingstore.cpp b/src/platformsupport/eglconvenience/qeglplatformbackingstore.cpp
index 3cb31e36ec..7b627f85ae 100644
--- a/src/platformsupport/eglconvenience/qeglplatformbackingstore.cpp
+++ b/src/platformsupport/eglconvenience/qeglplatformbackingstore.cpp
@@ -181,11 +181,8 @@ void QEGLPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &r
screen->compositingContext()->makeCurrent(dstWin->window());
m_textures->clear();
- for (int i = 0; i < textures->count(); ++i) {
- uint textureId = textures->textureId(i);
- QRect geom = textures->geometry(i);
- m_textures->appendTexture(textureId, geom);
- }
+ for (int i = 0; i < textures->count(); ++i)
+ m_textures->appendTexture(textures->textureId(i), textures->geometry(i), textures->stacksOnTop(i));
updateTexture();
m_textures->appendTexture(m_bsTexture, window->geometry());
diff --git a/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp b/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp
new file mode 100644
index 0000000000..bc279e4406
--- /dev/null
+++ b/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the documentation 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$
+**
+****************************************************************************/
+
+//! [0]
+class MyGLWidget : public QOpenGLWidget
+{
+public:
+ MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }
+
+protected:
+ void initializeGL()
+ {
+ // Set up the rendering context, load shaders and other resources, etc.:
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ ...
+ }
+
+ void resizeGL(int w, int h)
+ {
+ // Update projection matrix and other size related settings:
+ m_projection.setToIdentity();
+ m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
+ ...
+ }
+
+ void paintGL()
+ {
+ // Draw the scene:
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glClear(GL_COLOR_BUFFER_BIT);
+ ...
+ }
+
+};
+//! [0]
+
+//! [1]
+class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+ ...
+ void initializeGL()
+ {
+ initializeOpenGLFunctions();
+ glClearColor(...);
+ ...
+ }
+ ...
+};
+//! [1]
+
+//! [2]
+QOpenGLWidget *widget = new QOpenGLWidget(parent);
+QSurfaceFormat format;
+format.setDepthBufferSize(24);
+format.setStencilBufferSize(8);
+format.setVersion(3, 2);
+format.setProfile(QSurfaceFormat::CoreProfile);
+widget->setFormat(format); // must be called before the widget or its parent window gets shown
+//! [2]
+
+//! [3]
+ ...
+ void paintGL()
+ {
+ QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
+ ...
+ f->glDrawArraysInstanced(...);
+ ...
+ }
+ ...
+//! [3]
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp
index 9cd684a408..7bdfae5fec 100644
--- a/src/widgets/graphicsview/qgraphicsview.cpp
+++ b/src/widgets/graphicsview/qgraphicsview.cpp
@@ -2762,7 +2762,7 @@ void QGraphicsView::setupViewport(QWidget *widget)
return;
}
- const bool isGLWidget = widget->inherits("QGLWidget");
+ const bool isGLWidget = widget->inherits("QGLWidget") || widget->inherits("QOpenGLWidget");
d->accelerateScrolling = !(isGLWidget);
diff --git a/src/widgets/kernel/kernel.pri b/src/widgets/kernel/kernel.pri
index a9b8627beb..88c1e2595b 100644
--- a/src/widgets/kernel/kernel.pri
+++ b/src/widgets/kernel/kernel.pri
@@ -78,6 +78,6 @@ wince*: {
}
contains(QT_CONFIG, opengl) {
- HEADERS += kernel/qopenglwidget_p.h
+ HEADERS += kernel/qopenglwidget.h
SOURCES += kernel/qopenglwidget.cpp
}
diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp
index 307d0bb909..907f69dfbd 100644
--- a/src/widgets/kernel/qopenglwidget.cpp
+++ b/src/widgets/kernel/qopenglwidget.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtWidgets module of the Qt Toolkit.
@@ -39,146 +39,892 @@
**
****************************************************************************/
-#include "qopenglwidget_p.h"
-#include <QOpenGLContext>
-#include <QtWidgets/private/qwidget_p.h>
-
-#include <QOpenGLFramebufferObject>
-#include <QOpenGLFunctions>
-#include <QWindow>
-#include <qpa/qplatformwindow.h>
-#include <QDebug>
+#include "qopenglwidget.h"
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QOffscreenSurface>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QWindow>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
+#include <QtGui/QOpenGLPaintDevice>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qopenglextensions_p.h>
+#include <QtGui/private/qfont_p.h>
+#include <QtWidgets/private/qwidget_p.h>
QT_BEGIN_NAMESPACE
+/*!
+ \class QOpenGLWidget
+ \inmodule QtWidgets
+ \since 5.4
+
+ \brief The QOpenGLWidget class is a widget for rendering OpenGL graphics.
+
+ QOpenGLWidget provides functionality for displaying OpenGL graphics
+ integrated into a Qt application. It is very simple to use: Make
+ your class inherit from it and use the subclass like any other
+ QWidget, except that you have the choice between using QPainter and
+ standard OpenGL rendering commands.
+
+ QOpenGLWidget provides three convenient virtual functions that you
+ can reimplement in your subclass to perform the typical OpenGL
+ tasks:
+
+ \list
+ \li paintGL() - Renders the OpenGL scene. Gets called whenever the widget
+ needs to be updated.
+ \li resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets
+ called whenever the widget has been resized (and also when it
+ is shown for the first time because all newly created widgets get a
+ resize event automatically).
+ \li initializeGL() - Sets up the OpenGL resources and state. Gets called
+ once before the first time resizeGL() or paintGL() is called.
+ \endlist
+
+ If you need to trigger a repaint from places other than paintGL() (a
+ typical example is when using \l{QTimer}{timers} to animate scenes),
+ you should call the widget's update() function to schedule an update.
+
+ Your widget's OpenGL rendering context is made current when
+ paintGL(), resizeGL(), or initializeGL() is called. If you need to
+ call the standard OpenGL API functions from other places (e.g. in
+ your widget's constructor or in your own paint functions), you
+ must call makeCurrent() first.
+
+ All rendering happens into an OpenGL framebuffer
+ object. makeCurrent() ensure that it is bound in the context. Keep
+ this in mind when creating and binding additional framebuffer
+ objects in the rendering code in paintGL(). Never re-bind the
+ framebuffer with ID 0. Instead, call defaultFramebufferObject() to
+ get the ID that should be bound.
+
+ QOpenGLWidget allows using different OpenGL versions and profiles
+ when the platform supports it. Just set the requested format via
+ setFormat(). Keep in mind however that having multiple QOpenGLWidget
+ instances in the same window requires that they all use the same
+ format, or at least formats that do not make the contexts
+ non-sharable.
+
+ \section1 Painting Techniques
+
+ As described above, subclass QOpenGLWidget to render pure 3D content in the
+ following way:
+
+ \list
+
+ \li Reimplement the initializeGL() and resizeGL() functions to
+ set up the OpenGL state and provide a perspective transformation.
+
+ \li Reimplement paintGL() to paint the 3D scene, calling only
+ OpenGL functions.
+
+ \endlist
+
+ It is also possible to draw 2D graphics onto a QOpenGLWidget subclass using QPainter:
+
+ \list
+
+ \li In paintGL(), instead of issuing OpenGL commands, construct a QPainter
+ object for use on the widget.
+
+ \li Draw primitives using QPainter's member functions.
+
+ \li Direct OpenGL commands can still be issued. However, you must make sure
+ these are enclosed by a call to the painter's beginNativePainting() and
+ endNativePainting().
+
+ \endlist
+
+ When performing drawing using QPainter only, it is also possible to perform
+ the painting like it is done for ordinary widgets: by reimplementing paintEvent().
+
+ \list
+
+ \li Reimplement the paintEvent() function.
+
+ \li Construct a QPainter object targeting the widget. Either pass the widget to the
+ constructor or the QPainter::begin() function.
+
+ \li Draw primitives using QPainter's member functions.
+
+ \li Painting finishes then the QPainter instance is destroyed. Alternatively,
+ call QPainter::end() explicitly.
+
+ \endlist
+
+ \section1 OpenGL function calls, headers and QOpenGLFunctions
+
+ When making OpenGL function calls, it is strongly recommended to avoid calling
+ the functions directly. Instead, prefer using QOpenGLFunctions (when making
+ portable applications) or the versioned variants (for example,
+ QOpenGLFunctions_3_2_Core and similar, when targeting modern, desktop-only
+ OpenGL). This way the application will work correctly in all Qt build
+ configurations, including the ones that perform dynamic OpenGL implementation
+ loading which means applications are not directly linking to an GL
+ implementation and thus direct function calls are not feasible.
+
+ In paintGL() the current context is always accessible by caling
+ QOpenGLContext::currentContext(). From this context an already initialized,
+ ready-to-be-used QOpenGLFunctions instance is retrievable by calling
+ QOpenGLContext::functions(). An alternative to prefixing every GL call is to
+ inherit from QOpenGLFunctions and call
+ QOpenGLFunctions::initializeOpenGLFunctions() in initializeGL().
+
+ As for the OpenGL headers, note that in most cases there will be no need to
+ directly include any headers like GL.h. The OpenGL-related Qt headers will
+ include qopengl.h which will in turn include an appropriate header for the
+ system. This might be an OpenGL ES 3.x or 2.0 header, the highest version that
+ is available, or a system-provided gl.h. In addition, a copy of the extension
+ headers (called glext.h on some systems) is provided as part of Qt both for
+ OpenGL and OpenGL ES. These will get included automatically on platforms where
+ feasible. This means that constants and function pointer typedefs from ARB,
+ EXT, OES extensions are automatically available.
+
+ \section1 Code examples
+
+ To get started, the simplest QOpenGLWidget subclass could like like the following:
+
+ \snippet code/doc_gui_widgets_qopenglwidget.cpp 0
+
+ Alternatively, the prefixing of each and every OpenGL call can be avoidided by deriving
+ from QOpenGLFunctions instead:
+
+ \snippet code/doc_gui_widgets_qopenglwidget.cpp 1
+
+ To get a context compatible with a given OpenGL version or profile, or to
+ request depth and stencil buffers, call setFormat():
+
+ \snippet code/doc_gui_widgets_qopenglwidget.cpp 2
+
+ With OpenGL 3.0+ contexts, when portability is not important, the versioned
+ QOpenGLFunctions variants give easy access to all the modern OpenGL functions
+ available in a given version:
+
+ \snippet code/doc_gui_widgets_qopenglwidget.cpp 3
+
+ \section1 Relation to QGLWidget
+
+ The legacy QtOpenGL module (classes prefixed with QGL) provides a widget
+ called QGLWidget. QOpenGLWidget is intended to be a modern replacement for
+ it. Therefore, especially in new applications, the general recommendation is
+ to use QOpenGLWidget.
+
+ While the API is very similar, there is an important difference between the
+ two: QOpenGLWidget always renders offscreen, using framebuffer
+ objects. QGLWidget on the other hand uses a native window and surface. The
+ latter causes issues when using it in complex user interfaces since, depending
+ on the platform, such native child widgets may have various limitations,
+ regarding stacking orders for example. QOpenGLWidget avoids this by not
+ creating a separate native window.
+
+ \section1 Threading
+
+ Performing offscreen rendering on worker threads, for example to generate
+ textures that are then used in the GUI/main thread in paintGL(), are supported
+ by exposing the widget's QOpenGLContext so that additional contexts sharing
+ with it can be created on each thread.
+
+ Drawing directly to the QOpenGLWidget's framebuffer outside the GUI/main
+ thread is possible by reimplementing paintEvent() to do nothing. The context's
+ thread affinity has to be changed via QObject::moveToThread(). After that,
+ makeCurrent() and doneCurrent() are usable on the worker thread. Be careful to
+ move the context back to the GUI/main thread afterwards.
+
+ Unlike QGLWidget, triggering a buffer swap just for the QOpenGLWidget is not
+ possible since there is no real, onscreen native surface for it. Instead, it
+ is up to the widget stack to manage composition and buffer swaps on the gui
+ thread. When a thread is done updating the framebuffer, call update() \b{on
+ the GUI/main thread} to schedule composition.
+
+ Extra care has to be taken to avoid using the framebuffer when the GUI/main
+ thread is performing compositing. The signals aboutToCompose() and
+ frameSwapped() will be emitted when the composition is starting and
+ ending. They are emitted on the GUI/main thread. This means that by using a
+ direct connection aboutToCompose() can block the GUI/main thread until the
+ worker thread has finished its rendering. After that, the worker thread must
+ perform no further rendering until the frameSwapped() signal is emitted. If
+ this is not acceptable, the worker thread has to implement a double buffering
+ mechanism. This involves drawing using an alternative render target, that is
+ fully controlled by the thread, e.g. an additional framebuffer object, and
+ blitting to the QOpenGLWidget's framebuffer at a suitable time.
+
+ \section1 Context sharing
+
+ When multiple QOpenGLWidgets are added as children to the same top-level
+ widget, their contexts will share with each other. This does not apply for
+ QOpenGLWidget instances that belong to different windows.
+
+ This means that all QOpenGLWidgets in the same window can access each other's
+ sharable resources, like textures, and there is no need for an extra "global
+ share" context, as was the case with QGLWidget.
+
+ Note that QOpenGLWidget expects a standard conformant implementation of
+ resource sharing when it comes to the underlying graphics drivers. For
+ example, some drivers, in particular for mobile and embedded hardware, have
+ issues with setting up sharing between an existing context and others that are
+ created later. Some other drivers may behave in unexpected ways when trying to
+ utilize shared resources between different threads.
+
+ \section1 Limitations
+
+ Putting other widgets underneath and making the QOpenGLWidget transparent will
+ not lead to the expected results: The widgets underneath will not be
+ visible. This is because in practice the QOpenGLWidget is drawn before all
+ other regular, non-OpenGL widgets, and so see-through type of solutions are
+ not feasible. Other type of layouts, like having widgets on top of the
+ QOpenGLWidget, will function as expected.
+
+ When absolutely necessary, this limitation can be overcome by setting the
+ Qt::WA_AlwaysStackOnTop attribute on the QOpenGLWidget. Be aware however that
+ this breaks stacking order, for example it will not be possible to have other
+ widgets on top of the QOpenGLWidget, so it should only be used in situations
+ where a semi-transparent QOpenGLWidget with other widgets visible underneath
+ is required.
+
+ \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
+ countries.}
+
+ \sa QOpenGLFunctions
+*/
+
+/*!
+ \fn void QOpenGLWidget::aboutToCompose()
+
+ This signal is emitted when the widget's top-level window is about to begin
+ composing the textures of its QOpenGLWidget children and the other widgets.
+*/
+
+/*!
+ \fn void QOpenGLWidget::frameSwapped()
+
+ This signal is emitted after the widget's top-level window has finished
+ composition and returned from its potentially blocking
+ QOpenGLContext::swapBuffers() call.
+*/
+
+/*!
+ \fn void QOpenGLWidget::aboutToResize()
+
+ This signal is emitted when the widget's size is changed and therefore the
+ framebuffer object is going to be recreated.
+*/
+
+/*!
+ \fn void QOpenGLWidget::resized()
+
+ This signal is emitted right after the framebuffer object has been recreated
+ due to resizing the widget.
+*/
+
+class QOpenGLWidgetPaintDevice : public QOpenGLPaintDevice
+{
+public:
+ QOpenGLWidgetPaintDevice(QOpenGLWidget *widget) : w(widget) { }
+ void ensureActiveTarget() Q_DECL_OVERRIDE;
+
+private:
+ QOpenGLWidget *w;
+};
+
class QOpenGLWidgetPrivate : public QWidgetPrivate
{
Q_DECLARE_PUBLIC(QOpenGLWidget)
public:
QOpenGLWidgetPrivate()
- : fbo(0), uninitialized(true)
+ : context(0),
+ fbo(0),
+ resolvedFbo(0),
+ surface(0),
+ initialized(false),
+ fakeHidden(false),
+ paintDevice(0),
+ inBackingStorePaint(false)
+ {
+ }
+
+ ~QOpenGLWidgetPrivate()
{
+ reset();
}
- GLuint textureId() const { return fbo ? fbo->texture() : 0; }
- const QSurface *surface() const { return q_func()->window()->windowHandle(); }
- QSurface *surface() { return q_func()->window()->windowHandle(); }
+ void reset();
+ void recreateFbo();
+
+ GLuint textureId() const Q_DECL_OVERRIDE;
+
void initialize();
+ void invokeUserPaint();
+ void render();
- QOpenGLContext context;
- QOpenGLFramebufferObject *fbo;
- bool uninitialized;
+ QImage grabFramebuffer() Q_DECL_OVERRIDE;
+ void beginBackingStorePainting() Q_DECL_OVERRIDE { inBackingStorePaint = true; }
+ void endBackingStorePainting() Q_DECL_OVERRIDE { inBackingStorePaint = false; }
+ void beginCompose() Q_DECL_OVERRIDE;
+ void endCompose() Q_DECL_OVERRIDE;
+ void resizeViewportFramebuffer() Q_DECL_OVERRIDE;
- int w,h;
+ QOpenGLContext *context;
+ QOpenGLFramebufferObject *fbo;
+ QOpenGLFramebufferObject *resolvedFbo;
+ QOffscreenSurface *surface;
+ bool initialized;
+ bool fakeHidden;
+ QOpenGLPaintDevice *paintDevice;
+ bool inBackingStorePaint;
+ QSurfaceFormat requestedFormat;
};
+void QOpenGLWidgetPaintDevice::ensureActiveTarget()
+{
+ QOpenGLWidgetPrivate *d = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(w));
+ if (!d->initialized)
+ return;
+
+ if (QOpenGLContext::currentContext() != d->context)
+ w->makeCurrent();
+ else
+ d->fbo->bind();
+}
+
+GLuint QOpenGLWidgetPrivate::textureId() const
+{
+ return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0);
+}
+
+void QOpenGLWidgetPrivate::reset()
+{
+ delete paintDevice;
+ paintDevice = 0;
+ delete fbo;
+ fbo = 0;
+ delete resolvedFbo;
+ resolvedFbo = 0;
+ delete context;
+ context = 0;
+ delete surface;
+ surface = 0;
+ initialized = fakeHidden = inBackingStorePaint = false;
+}
+
+void QOpenGLWidgetPrivate::recreateFbo()
+{
+ Q_Q(QOpenGLWidget);
+
+ emit q->aboutToResize();
+
+ context->makeCurrent(surface);
+
+ delete fbo;
+ delete resolvedFbo;
+
+ int samples = get(q->window())->shareContext()->format().samples();
+ QOpenGLExtensions *extfuncs = static_cast<QOpenGLExtensions *>(context->functions());
+ if (!extfuncs->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
+ samples = 0;
+
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(samples);
+
+ const QSize deviceSize = q->size() * q->devicePixelRatio();
+ fbo = new QOpenGLFramebufferObject(deviceSize, format);
+ if (samples > 0)
+ resolvedFbo = new QOpenGLFramebufferObject(deviceSize);
+
+ fbo->bind();
+
+ paintDevice->setSize(deviceSize);
+
+ emit q->resized();
+}
+
+void QOpenGLWidgetPrivate::beginCompose()
+{
+ Q_Q(QOpenGLWidget);
+ emit q->aboutToCompose();
+}
+
+void QOpenGLWidgetPrivate::endCompose()
+{
+ Q_Q(QOpenGLWidget);
+ emit q->frameSwapped();
+}
+
void QOpenGLWidgetPrivate::initialize()
{
Q_Q(QOpenGLWidget);
- if (!uninitialized)
+ if (initialized)
+ return;
+
+ // Get our toplevel's context with which we will share in order to make the
+ // texture usable by the underlying window's backingstore.
+ QOpenGLContext *shareContext = get(q->window())->shareContext();
+ if (!shareContext) {
+ qWarning("QOpenGLWidget: Cannot be used without a context shared with the toplevel.");
return;
- context.setShareContext(get(q->window())->shareContext());
- context.setFormat(surface()->format());
- context.create();
- context.makeCurrent(surface());
+ }
+
+ QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
+ ctx->setShareContext(shareContext);
+ ctx->setFormat(requestedFormat);
+ if (!ctx->create()) {
+ qWarning("QOpenGLWidget: Failed to create context");
+ return;
+ }
+
+ // The top-level window's surface is not good enough since it causes way too
+ // much trouble with regards to the QSurfaceFormat for example. So just like
+ // in QQuickWidget, use a dedicated QOffscreenSurface.
+ surface = new QOffscreenSurface;
+ surface->setFormat(ctx->format());
+ surface->create();
+
+ if (!ctx->makeCurrent(surface)) {
+ qWarning("QOpenGLWidget: Failed to make context current");
+ return;
+ }
+
+ paintDevice = new QOpenGLWidgetPaintDevice(q);
+ paintDevice->setSize(q->size() * q->devicePixelRatio());
+ paintDevice->setDevicePixelRatio(q->devicePixelRatio());
+
+ context = ctx.take();
+ initialized = true;
+
q->initializeGL();
- uninitialized = false;
}
+void QOpenGLWidgetPrivate::invokeUserPaint()
+{
+ Q_Q(QOpenGLWidget);
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glViewport(0, 0, q->width() * q->devicePixelRatio(), q->height() * q->devicePixelRatio());
+
+ q->paintGL();
+
+ if (resolvedFbo) {
+ QRect rect(QPoint(0, 0), fbo->size());
+ QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
+ }
+}
+
+void QOpenGLWidgetPrivate::render()
+{
+ Q_Q(QOpenGLWidget);
+
+ if (fakeHidden || !initialized)
+ return;
+
+ q->makeCurrent();
+ invokeUserPaint();
+ context->functions()->glFlush();
+}
+
+extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+
+QImage QOpenGLWidgetPrivate::grabFramebuffer()
+{
+ Q_Q(QOpenGLWidget);
+ if (!initialized)
+ return QImage();
+
+ render();
+ q->makeCurrent();
+ QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatio(), false, false);
+
+ return res;
+}
+
+void QOpenGLWidgetPrivate::resizeViewportFramebuffer()
+{
+ Q_Q(QOpenGLWidget);
+ if (!initialized)
+ return;
+
+ if (!fbo || q->size() != fbo->size())
+ recreateFbo();
+}
+
+/*!
+ Constructs a widget which is a child of \a parent, with widget flags set to \a f.
+ */
QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(*(new QOpenGLWidgetPrivate), parent, f)
{
Q_D(QOpenGLWidget);
- d->setRenderToTexture();
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
+ d->setRenderToTexture();
+ else
+ qWarning("QOpenGLWidget is not supported on this platform.");
}
+/*!
+ Destroys the widget
+ */
QOpenGLWidget::~QOpenGLWidget()
{
}
+/*!
+ Sets the requested surface \a format.
+
+ \note Requesting an alpha buffer via this function will not lead to the desired results
+ and should be avoided. Instead, use Qt::WA_AlwaysStackOnTop to enable semi-transparent
+ QOpenGLWidget instances with other widgets visible underneath. Keep in mind however that
+ this breaks the stacking order, so it will no longer be possible to have other widgets
+ on top of the QOpenGLWidget.
+
+ \sa format(), Qt::WA_AlwaysStackOnTop
+ */
+void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
+{
+ Q_UNUSED(format);
+ Q_D(QOpenGLWidget);
+ if (d->initialized) {
+ qWarning("QOpenGLWidget: Already initialized, setting the format has no effect");
+ return;
+ }
+
+ d->requestedFormat = format;
+}
+
+/*!
+ Returns the context and surface format used by this widget and its toplevel
+ window.
+
+ After the widget and its toplevel have both been created, resized and shown,
+ this function will return the actual format of the context. This may differ
+ from the requested format if the request could not be fulfilled by the
+ platform. It is also possible to get larger color buffer sizes than
+ requested.
+
+ When the widget's window and the related OpenGL resources are not yet
+ initialized, the return value is the format that has been set via
+ setFormat().
+
+ \sa setFormat(), context()
+ */
+QSurfaceFormat QOpenGLWidget::format() const
+{
+ Q_D(const QOpenGLWidget);
+ return d->initialized ? d->context->format() : d->requestedFormat;
+}
+
+/*!
+ \return \e true if the widget and OpenGL resources, like the context, have
+ been successfully initialized. Note that the return value is always false
+ until the widget is shown.
+*/
bool QOpenGLWidget::isValid() const
{
Q_D(const QOpenGLWidget);
- return d->context.isValid();
+ return d->initialized && d->context->isValid();
}
+/*!
+ Prepares for rendering OpenGL content for this widget by making the
+ corresponding context current and binding the framebuffer object in that
+ context.
+
+ It is not necessary to call this function in most cases, because it
+ is called automatically before invoking paintGL().
+
+ \sa context(), paintGL(), doneCurrent()
+ */
void QOpenGLWidget::makeCurrent()
{
Q_D(QOpenGLWidget);
- d->context.makeCurrent(d->surface());
+ if (!d->initialized) {
+ qWarning("QOpenGLWidget: Cannot make uninitialized widget current");
+ return;
+ }
+
+ d->context->makeCurrent(d->surface);
d->fbo->bind();
}
+/*!
+ 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().
+ */
void QOpenGLWidget::doneCurrent()
{
Q_D(QOpenGLWidget);
- d->context.doneCurrent();
+ if (!d->initialized)
+ return;
+
+ d->context->doneCurrent();
}
-QSurfaceFormat QOpenGLWidget::format() const
+/*!
+ \return The QOpenGLContext used by this widget or \c 0 if not yet initialized.
+
+ \note The context and the framebuffer object used by the widget changes when
+ reparenting the widget via setParent().
+
+ \sa QOpenGLContext::setShareContext(), defaultFramebufferObject()
+ */
+QOpenGLContext *QOpenGLWidget::context() const
{
Q_D(const QOpenGLWidget);
- return d->surface()->format();
+ return d->context;
}
+/*!
+ \return The framebuffer object handle or \c 0 if not yet initialized.
+
+ \note The framebuffer object belongs to the context returned by context()
+ and may not be accessible from other contexts.
+
+ \note The context and the framebuffer object used by the widget changes when
+ reparenting the widget via setParent(). In addition, the framebuffer object
+ changes on each resize.
+
+ \sa context()
+ */
GLuint QOpenGLWidget::defaultFramebufferObject() const
{
Q_D(const QOpenGLWidget);
return d->fbo ? d->fbo->handle() : 0;
}
+/*!
+ 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
+ is not yet available at this stage, so avoid issuing draw calls from
+ here. Defer such calls to paintGL() instead.
+
+ \sa paintGL(), resizeGL()
+*/
void QOpenGLWidget::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 is
+ also bound.
+
+ \sa initializeGL(), paintGL()
+*/
void QOpenGLWidget::resizeGL(int w, int h)
{
Q_UNUSED(w);
Q_UNUSED(h);
}
+/*!
+ This virtual function is called whenever the widget 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 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.
+
+ \sa initializeGL(), resizeGL()
+*/
void QOpenGLWidget::paintGL()
{
}
-void QOpenGLWidget::updateGL()
+/*!
+ \internal
+
+ Handles resize events that are passed in the \a event parameter.
+ Calls the virtual function resizeGL().
+*/
+void QOpenGLWidget::resizeEvent(QResizeEvent *e)
{
Q_D(QOpenGLWidget);
- if (d->uninitialized || !d->surface())
+
+ if (e->size().isEmpty()) {
+ d->fakeHidden = true;
+ return;
+ }
+ d->fakeHidden = false;
+
+ d->initialize();
+ if (!d->initialized)
return;
- makeCurrent();
- paintGL();
- d->context.functions()->glFlush();
- doneCurrent();
- update();
+ d->recreateFbo();
+ resizeGL(width(), height());
+ d->invokeUserPaint();
+ d->context->functions()->glFlush();
}
+/*!
+ Handles paint events.
-void QOpenGLWidget::resizeEvent(QResizeEvent *)
+ Calling QWidget::update() will lead to sending a paint event \a e,
+ and thus invoking this function. (NB this is asynchronous and will
+ happen at some point after returning from update()). This function
+ will then, after some preparation, call the virtual paintGL() to
+ update the contents of the QOpenGLWidget's framebuffer. The widget's
+ top-level window will then composite the framebuffer's texture with
+ the rest of the window.
+*/
+void QOpenGLWidget::paintEvent(QPaintEvent *e)
{
+ Q_UNUSED(e);
Q_D(QOpenGLWidget);
- d->w = width();
- d->h = height();
- d->initialize();
+ if (!d->initialized)
+ return;
- d->context.makeCurrent(d->surface());
- delete d->fbo; // recreate when resized
- d->fbo = new QOpenGLFramebufferObject(size() * devicePixelRatio(), QOpenGLFramebufferObject::CombinedDepthStencil);
- d->fbo->bind();
- QOpenGLFunctions *funcs = d->context.functions();
- resizeGL(width(), height());
- paintGL();
- funcs->glFlush();
+ if (updatesEnabled())
+ d->render();
+}
+
+/*!
+ Renders and 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.
+*/
+QImage QOpenGLWidget::grabFramebuffer()
+{
+ Q_D(QOpenGLWidget);
+ return d->grabFramebuffer();
+}
+
+/*!
+ \internal
+*/
+int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ Q_D(const QOpenGLWidget);
+ if (d->inBackingStorePaint)
+ return QWidget::metric(metric);
+
+ QScreen *screen = window()->windowHandle()->screen();
+ if (!screen && QGuiApplication::primaryScreen())
+ screen = QGuiApplication::primaryScreen();
+
+ const float dpmx = qt_defaultDpiX() * 100. / 2.54;
+ const float dpmy = qt_defaultDpiY() * 100. / 2.54;
+
+ switch (metric) {
+ case PdmWidth:
+ return width();
+ case PdmHeight:
+ return height();
+ case PdmDepth:
+ return 32;
+ case PdmWidthMM:
+ if (screen)
+ return width() * screen->physicalSize().width() / screen->geometry().width();
+ else
+ return width() * 1000 / dpmx;
+ case PdmHeightMM:
+ if (screen)
+ return height() * screen->physicalSize().height() / screen->geometry().height();
+ else
+ return height() * 1000 / dpmy;
+ case PdmNumColors:
+ return 0;
+ case PdmDpiX:
+ if (screen)
+ return qRound(screen->logicalDotsPerInchX());
+ else
+ return qRound(dpmx * 0.0254);
+ case PdmDpiY:
+ if (screen)
+ return qRound(screen->logicalDotsPerInchY());
+ else
+ return qRound(dpmy * 0.0254);
+ case PdmPhysicalDpiX:
+ if (screen)
+ return qRound(screen->physicalDotsPerInchX());
+ else
+ return qRound(dpmx * 0.0254);
+ case PdmPhysicalDpiY:
+ if (screen)
+ return qRound(screen->physicalDotsPerInchY());
+ else
+ return qRound(dpmy * 0.0254);
+ case PdmDevicePixelRatio:
+ if (screen)
+ return screen->devicePixelRatio();
+ else
+ return 1.0;
+ default:
+ qWarning("QOpenGLWidget::metric(): unknown metric %d", metric);
+ return 0;
+ }
+}
+
+/*!
+ \internal
+*/
+QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
+{
+ Q_D(const QOpenGLWidget);
+ if (d->inBackingStorePaint)
+ return QWidget::redirected(p);
+
+ return d->paintDevice;
+}
+
+/*!
+ \internal
+*/
+QPaintEngine *QOpenGLWidget::paintEngine() const
+{
+ Q_D(const QOpenGLWidget);
+ // QWidget needs to "punch a hole" into the backingstore. This needs the
+ // normal paint engine and device, not the GL one. So in this mode, behave
+ // like a normal widget.
+ if (d->inBackingStorePaint)
+ return QWidget::paintEngine();
+
+ if (!d->initialized)
+ return 0;
+
+ return d->paintDevice->paintEngine();
}
-void QOpenGLWidget::paintEvent(QPaintEvent *)
+/*!
+ \internal
+*/
+bool QOpenGLWidget::event(QEvent *e)
{
- qWarning("QOpenGLWidget does not support paintEvent() yet.");
- return;
+ Q_D(QOpenGLWidget);
+ switch (e->type()) {
+ case QEvent::WindowChangeInternal:
+ if (d->initialized)
+ d->reset();
+ // FALLTHROUGH
+ case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too
+ if (!d->initialized && !size().isEmpty() && window() && window()->windowHandle()) {
+ d->initialize();
+ if (d->initialized)
+ d->recreateFbo();
+ }
+ break;
+ default:
+ break;
+ }
+ return QWidget::event(e);
}
QT_END_NAMESPACE
diff --git a/src/widgets/kernel/qopenglwidget_p.h b/src/widgets/kernel/qopenglwidget.h
index 1c7f0bfeec..28d802e1f4 100644
--- a/src/widgets/kernel/qopenglwidget_p.h
+++ b/src/widgets/kernel/qopenglwidget.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtWidgets module of the Qt Toolkit.
@@ -39,21 +39,11 @@
**
****************************************************************************/
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It may change from version to version
-// without notice, or even be removed.
-//
-// We mean it.
-//
#ifndef QOPENGLWIDGET_H
#define QOPENGLWIDGET_H
-#include <QWidget>
-#include <QSurfaceFormat>
-
+#include <QtWidgets/QWidget>
+#include <QtGui/QSurfaceFormat>
#include <QtGui/qopengl.h>
QT_BEGIN_NAMESPACE
@@ -66,68 +56,43 @@ class Q_WIDGETS_EXPORT QOpenGLWidget : public QWidget
Q_DECLARE_PRIVATE(QOpenGLWidget)
public:
- explicit QOpenGLWidget(QWidget* parent=0,
- Qt::WindowFlags f=0);
-
-// This API is not finalized yet. The commented-out functions below are
-// QGLWidget functions that have not been implemented for QOpenGLWidget.
-// Some of them may not end up in the final version (which is planned for a
-// future release of Qt).
-
-// explicit QOpenGLWidget(const QSurfaceFormat& format, QWidget* parent=0,
-// Qt::WindowFlags f=0);
+ explicit QOpenGLWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);
~QOpenGLWidget();
-// void qglClearColor(const QColor& c) const;
+ void setFormat(const QSurfaceFormat &format);
+ QSurfaceFormat format() const;
bool isValid() const;
-// bool isSharing() const;
void makeCurrent();
void doneCurrent();
-// void swapBuffers();
-
- QSurfaceFormat format() const;
+ QOpenGLContext *context() const;
GLuint defaultFramebufferObject() const;
-// QPixmap renderPixmap(int w = 0, int h = 0, bool useContext = false);
- QImage grabFrameBuffer(bool withAlpha = false);
+ QImage grabFramebuffer();
-// static QImage convertToGLFormat(const QImage& img);
-
-// QPaintEngine *paintEngine() const;
-
-// void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
-// void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
-
-public Q_SLOTS:
- void updateGL();
+Q_SIGNALS:
+ void aboutToCompose();
+ void frameSwapped();
+ void aboutToResize();
+ void resized();
protected:
-// bool event(QEvent *);
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
-// void setAutoBufferSwap(bool on);
-// bool autoBufferSwap() const;
+ void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE;
+ void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE;
+ bool event(QEvent *e) Q_DECL_OVERRIDE;
- void paintEvent(QPaintEvent*);
- void resizeEvent(QResizeEvent*);
+ int metric(QPaintDevice::PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
+ QPaintDevice *redirected(QPoint *p) const Q_DECL_OVERRIDE;
+ QPaintEngine *paintEngine() const Q_DECL_OVERRIDE;
-// virtual void glInit();
-// virtual void glDraw();
-
-// QOpenGLWidget(QOpenGLWidgetPrivate &dd,
-// const QGLFormat &format = QGLFormat(),
-// QWidget *parent = 0,
-// const QOpenGLWidget* shareWidget = 0,
-// Qt::WindowFlags f = 0);
private:
Q_DISABLE_COPY(QOpenGLWidget)
-
-
};
QT_END_NAMESPACE
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index e692a985eb..43ce9a82f7 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -5492,18 +5492,22 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
//paint the background
if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground))
&& !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) {
+ beginBackingStorePainting();
QPainter p(q);
paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0);
+ endBackingStorePainting();
}
if (!sharedPainter)
setSystemClip(pdev, toBePainted.translated(offset));
if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) {
+ beginBackingStorePainting();
QPainter p(q);
QColor tint = q->palette().window().color();
tint.setAlphaF(qreal(.6));
p.fillRect(toBePainted.boundingRect(), tint);
+ endBackingStorePainting();
}
}
@@ -5513,24 +5517,30 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
<< "geometry ==" << QRect(q->mapTo(q->window(), QPoint(0, 0)), q->size());
#endif
+ bool grabbed = false;
+#ifndef QT_NO_OPENGL
if (renderToTexture) {
// This widget renders into a texture which is composed later. We just need to
// punch a hole in the backingstore, so the texture will be visible.
-#ifndef QT_NO_OPENGL
- QPainter p(q);
-
- if (backingStore) {
- p.setCompositionMode(QPainter::CompositionMode_Source);
- p.fillRect(q->rect(), Qt::transparent);
- } else {
- // We are not drawing to a backingstore: fall back to QImage
- p.drawImage(q->rect(), grabFramebuffer());
+ if (!q->testAttribute(Qt::WA_AlwaysStackOnTop)) {
+ beginBackingStorePainting();
+ QPainter p(q);
+ if (backingStore) {
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(q->rect(), Qt::transparent);
+ } else {
+ // We are not drawing to a backingstore: fall back to QImage
+ p.drawImage(q->rect(), grabFramebuffer());
+ grabbed = true;
+ }
+ endBackingStorePainting();
}
-#endif
- } else {
+ }
+#endif // QT_NO_OPENGL
+
+ if (!grabbed) {
//actually send the paint event
- QPaintEvent e(toBePainted);
- QCoreApplication::sendSpontaneousEvent(q, &e);
+ sendPaintEvent(toBePainted);
}
// Native widgets need to be marked dirty on screen so painting will be done in correct context
@@ -5586,6 +5596,13 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
}
}
+void QWidgetPrivate::sendPaintEvent(const QRegion &toBePainted)
+{
+ Q_Q(QWidget);
+ QPaintEvent e(toBePainted);
+ QCoreApplication::sendSpontaneousEvent(q, &e);
+}
+
void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset,
const QRegion &sourceRegion, QWidget::RenderFlags renderFlags)
{
@@ -11968,7 +11985,7 @@ QOpenGLContext *QWidgetPrivate::shareContext() const
}
QWidgetPrivate *that = const_cast<QWidgetPrivate *>(this);
if (!extra->topextra->shareContext) {
- QOpenGLContext *ctx = new QOpenGLContext();
+ QOpenGLContext *ctx = new QOpenGLContext;
ctx->setShareContext(QOpenGLContextPrivate::globalShareContext());
ctx->setFormat(extra->topextra->window->format());
ctx->create();
@@ -11978,6 +11995,24 @@ QOpenGLContext *QWidgetPrivate::shareContext() const
#endif // QT_NO_OPENGL
}
+#ifndef QT_NO_OPENGL
+void QWidgetPrivate::sendComposeStatus(QWidget *w, bool end)
+{
+ QWidgetPrivate *wd = QWidgetPrivate::get(w);
+ if (!wd->textureChildSeen)
+ return;
+ if (end)
+ wd->endCompose();
+ else
+ wd->beginCompose();
+ for (int i = 0; i < wd->children.size(); ++i) {
+ w = qobject_cast<QWidget *>(wd->children.at(i));
+ if (w && !w->isWindow() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
+ sendComposeStatus(w, end);
+ }
+}
+#endif // QT_NO_OPENGL
+
Q_WIDGETS_EXPORT QWidgetData *qt_qwidget_data(QWidget *widget)
{
return widget->data;
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index 6520832943..011d456a39 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -61,6 +61,7 @@
#include "QtGui/qregion.h"
#include "QtGui/qinputmethod.h"
#include "QtGui/qopengl.h"
+#include "QtGui/qsurfaceformat.h"
#include "QtWidgets/qsizepolicy.h"
#include "QtWidgets/qstyle.h"
#include "QtWidgets/qapplication.h"
@@ -392,6 +393,7 @@ public:
QWidget::RenderFlags renderFlags);
void drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags,
QPainter *sharedPainter = 0, QWidgetBackingStore *backingStore = 0);
+ void sendPaintEvent(const QRegion &toBePainted);
void paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& children, int index,
@@ -627,7 +629,11 @@ public:
#ifndef QT_NO_OPENGL
virtual GLuint textureId() const { return 0; }
- virtual QImage grabFramebuffer() const { return QImage(); }
+ virtual QImage grabFramebuffer() { return QImage(); }
+ virtual void beginBackingStorePainting() { }
+ virtual void endBackingStorePainting() { }
+ virtual void beginCompose() { }
+ virtual void endCompose() { }
void setRenderToTexture() { renderToTexture = true; setTextureChildSeen(); }
void setTextureChildSeen()
{
@@ -642,6 +648,11 @@ public:
get(parent)->setTextureChildSeen();
}
}
+ static void sendComposeStatus(QWidget *w, bool end);
+ // When using a QOpenGLWidget as viewport with QAbstractScrollArea, resize events are
+ // filtered away from the widget. This is fine for QGLWidget but bad for QOpenGLWidget
+ // since the fbo must be resized. We need an alternative way to notify.
+ virtual void resizeViewportFramebuffer() { }
#endif
// Variables.
diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp
index 9d024fd359..6f635611f0 100644
--- a/src/widgets/kernel/qwidgetbackingstore.cpp
+++ b/src/widgets/kernel/qwidgetbackingstore.cpp
@@ -74,7 +74,8 @@ extern QRegion qt_dirtyRegion(QWidget *);
* \a region is the region to be updated in \a widget coordinates.
*/
void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion &region, QBackingStore *backingStore,
- QWidget *tlw, const QPoint &tlwOffset, QPlatformTextureList *widgetTextures)
+ QWidget *tlw, const QPoint &tlwOffset, QPlatformTextureList *widgetTextures,
+ QWidgetBackingStore *widgetBackingStore)
{
#ifdef QT_NO_OPENGL
Q_UNUSED(widgetTextures);
@@ -92,33 +93,29 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion &region, QBack
QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
#endif
- //The performance hit by doing this should be negligible. However, be aware that
- //using this FPS when you have > 1 windowsurface can give you inaccurate FPS
+ if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
+ return;
static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt();
if (fpsDebug) {
- static QTime time = QTime::currentTime();
- static int frames = 0;
-
- frames++;
-
- if(time.elapsed() > 5000) {
- double fps = double(frames * 1000) /time.restart();
- fprintf(stderr,"FPS: %.1f\n",fps);
- frames = 0;
+ if (!widgetBackingStore->perfFrames++)
+ widgetBackingStore->perfTime.start();
+ if (widgetBackingStore->perfTime.elapsed() > 5000) {
+ double fps = double(widgetBackingStore->perfFrames * 1000) / widgetBackingStore->perfTime.restart();
+ qDebug("FPS: %.1f\n", fps);
+ widgetBackingStore->perfFrames = 0;
}
}
- if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
- return;
-
QPoint offset = tlwOffset;
if (widget != tlw)
offset += widget->mapTo(tlw, QPoint());
#ifndef QT_NO_OPENGL
- if (widgetTextures)
+ if (widgetTextures) {
+ widget->window()->d_func()->sendComposeStatus(widget->window(), false);
backingStore->handle()->composeAndFlush(widget->windowHandle(), region, offset, widgetTextures, tlw->d_func()->shareContext());
- else
+ widget->window()->d_func()->sendComposeStatus(widget->window(), true);
+ } else
#endif
backingStore->flush(region, widget->windowHandle(), offset);
}
@@ -270,7 +267,7 @@ void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
return;
const QPoint offset = widget->mapTo(tlw, QPoint());
- qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset);
+ qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset, 0, tlw->d_func()->maybeBackingStore());
}
#endif // QT_NO_PAINT_DEBUG
@@ -519,6 +516,8 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget,
const QPoint offset = widget->mapTo(tlw, QPoint());
if (QWidgetPrivate::get(widget)->renderToTexture) {
+ if (!widget->d_func()->inDirtyList)
+ addDirtyRenderToTextureWidget(widget);
if (!updateRequestSent || updateTime == UpdateNow)
sendUpdateRequest(tlw, updateTime);
return;
@@ -613,6 +612,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget,
}
if (QWidgetPrivate::get(widget)->renderToTexture) {
+ if (!widget->d_func()->inDirtyList)
+ addDirtyRenderToTextureWidget(widget);
if (!updateRequestSent || updateTime == UpdateNow)
sendUpdateRequest(tlw, updateTime);
return;
@@ -710,6 +711,7 @@ void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
dirtyWidgetsRemoveAll(w);
dirtyOnScreenWidgetsRemoveAll(w);
+ dirtyRenderToTextureWidgets.removeAll(w);
resetWidget(w);
QWidgetPrivate *wd = w->d_func();
@@ -744,7 +746,8 @@ QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
widgetTextures(0),
fullUpdatePending(0),
updateRequestSent(0),
- textureListWatcher(0)
+ textureListWatcher(0),
+ perfFrames(0)
{
store = tlw->backingStore();
Q_ASSERT(store);
@@ -755,9 +758,11 @@ QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
QWidgetBackingStore::~QWidgetBackingStore()
{
- for (int c = 0; c < dirtyWidgets.size(); ++c) {
+ for (int c = 0; c < dirtyWidgets.size(); ++c)
resetWidget(dirtyWidgets.at(c));
- }
+ for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
+ resetWidget(dirtyRenderToTextureWidgets.at(c));
+
#ifndef QT_NO_OPENGL
delete dirtyOnScreenWidgets;
#endif
@@ -944,7 +949,7 @@ void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedReg
// Nothing to repaint.
if (!isDirty() && store->size().isValid()) {
- qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTextures);
+ qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTextures, this);
return;
}
@@ -961,7 +966,8 @@ static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatfo
{
QWidgetPrivate *wd = QWidgetPrivate::get(widget);
if (wd->renderToTexture)
- widgetTextures->appendTexture(wd->textureId(), QRect(widget->mapTo(tlw, QPoint()), widget->size()));
+ widgetTextures->appendTexture(wd->textureId(), QRect(widget->mapTo(tlw, QPoint()), widget->size()),
+ widget->testAttribute(Qt::WA_AlwaysStackOnTop));
for (int i = 0; i < wd->children.size(); ++i) {
QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
@@ -1125,13 +1131,47 @@ void QWidgetBackingStore::doSync()
#endif
if (toClean.isEmpty()) {
- // Nothing to repaint. However, we might have newly exposed areas on the
- // screen if this function was called from sync(QWidget *, QRegion)), so
- // we have to make sure those are flushed.
+ // Nothing to repaint. However renderToTexture widgets are handled
+ // specially, they are not in the regular dirty list, in order to
+ // prevent triggering unnecessary backingstore painting when only the
+ // OpenGL content changes. Check if we have such widgets in the special
+ // dirty list.
+ for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) {
+ QWidget *w = dirtyRenderToTextureWidgets.at(i);
+ w->d_func()->sendPaintEvent(w->rect());
+ resetWidget(w);
+ }
+ dirtyRenderToTextureWidgets.clear();
+
+ // We might have newly exposed areas on the screen if this function was
+ // called from sync(QWidget *, QRegion)), so we have to make sure those
+ // are flushed. We also need to composite the renderToTexture widgets.
flush();
+
return;
}
+ // There is something other dirty than the renderToTexture widgets.
+ // Now it is time to include the renderToTexture ones among the others.
+ if (widgetTextures && widgetTextures->count()) {
+ for (int i = 0; i < widgetTextures->count(); ++i) {
+ const QRect rect = widgetTextures->geometry(i); // mapped to the tlw already
+ dirty += rect;
+ toClean += rect;
+ }
+ }
+ // The dirtyRenderToTextureWidgets list is useless here, so just reset. As
+ // unintuitive as it is, we need to send paint events to renderToTexture
+ // widgets always when something (any widget) needs to be updated, even if
+ // the renderToTexture widget itself is clean, i.e. there was no update()
+ // call for it. This is because changing any widget will cause a flush and
+ // so a potentially blocking buffer swap for the window, and skipping paints
+ // for the renderToTexture widgets would make it impossible to have smoothly
+ // animated content in them.
+ for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i)
+ resetWidget(dirtyRenderToTextureWidgets.at(i));
+ dirtyRenderToTextureWidgets.clear();
+
#ifndef QT_NO_GRAPHICSVIEW
if (tlw->d_func()->extra->proxyWidget) {
updateStaticContentsSize();
@@ -1200,15 +1240,17 @@ void QWidgetBackingStore::flush(QWidget *widget)
{
if (!dirtyOnScreen.isEmpty()) {
QWidget *target = widget ? widget : tlw;
- qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTextures);
+ qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTextures, this);
dirtyOnScreen = QRegion();
+ if (widgetTextures && widgetTextures->count())
+ return;
}
if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty()) {
#ifndef QT_NO_OPENGL
if (widgetTextures && widgetTextures->count()) {
QWidget *target = widget ? widget : tlw;
- qt_flush(target, QRegion(), store, tlw, tlwOffset, widgetTextures);
+ qt_flush(target, QRegion(), store, tlw, tlwOffset, widgetTextures, this);
}
#endif
return;
@@ -1218,7 +1260,7 @@ void QWidgetBackingStore::flush(QWidget *widget)
QWidget *w = dirtyOnScreenWidgets->at(i);
QWidgetPrivate *wd = w->d_func();
Q_ASSERT(wd->needsFlush);
- qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset);
+ qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset, 0, this);
*wd->needsFlush = QRegion();
}
dirtyOnScreenWidgets->clear();
diff --git a/src/widgets/kernel/qwidgetbackingstore_p.h b/src/widgets/kernel/qwidgetbackingstore_p.h
index 88404ce205..0da85e33d5 100644
--- a/src/widgets/kernel/qwidgetbackingstore_p.h
+++ b/src/widgets/kernel/qwidgetbackingstore_p.h
@@ -133,6 +133,7 @@ private:
QRegion dirty; // needsRepaint
QRegion dirtyFromPreviousSync;
QVector<QWidget *> dirtyWidgets;
+ QVector<QWidget *> dirtyRenderToTextureWidgets;
QVector<QWidget *> *dirtyOnScreenWidgets;
QList<QWidget *> staticWidgets;
QPlatformTextureList *widgetTextures;
@@ -143,6 +144,8 @@ private:
QPoint tlwOffset;
QPlatformTextureListWatcher *textureListWatcher;
+ QElapsedTimer perfTime;
+ int perfFrames;
void sendUpdateRequest(QWidget *widget, UpdateTime updateTime);
@@ -150,7 +153,8 @@ private:
static void unflushPaint(QWidget *widget, const QRegion &rgn);
static void qt_flush(QWidget *widget, const QRegion &region, QBackingStore *backingStore,
QWidget *tlw, const QPoint &tlwOffset,
- QPlatformTextureList *widgetTextures = 0);
+ QPlatformTextureList *widgetTextures,
+ QWidgetBackingStore *widgetBackingStore);
void doSync();
bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget);
@@ -184,6 +188,16 @@ private:
}
}
+ inline void addDirtyRenderToTextureWidget(QWidget *widget)
+ {
+ if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ Q_ASSERT(widgetPrivate->renderToTexture);
+ dirtyRenderToTextureWidgets.append(widget);
+ widgetPrivate->inDirtyList = true;
+ }
+ }
+
inline void dirtyWidgetsRemoveAll(QWidget *widget)
{
int i = 0;
diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp
index e1e933cdd8..e830ac3c76 100644
--- a/src/widgets/widgets/qabstractscrollarea.cpp
+++ b/src/widgets/widgets/qabstractscrollarea.cpp
@@ -1191,6 +1191,10 @@ bool QAbstractScrollArea::viewportEvent(QEvent *e)
case QEvent::DragMove:
case QEvent::DragLeave:
#endif
+ // QOpenGLWidget needs special support because it has to know
+ // its size has changed, so that it can resize its fbo.
+ if (e->type() == QEvent::Resize)
+ QWidgetPrivate::get(viewport())->resizeViewportFramebuffer();
return QFrame::event(e);
case QEvent::LayoutRequest:
#ifndef QT_NO_GESTURES
diff --git a/tests/auto/widgets/widgets/qopenglwidget/qopenglwidget.pro b/tests/auto/widgets/widgets/qopenglwidget/qopenglwidget.pro
new file mode 100644
index 0000000000..bbc6e987af
--- /dev/null
+++ b/tests/auto/widgets/widgets/qopenglwidget/qopenglwidget.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+CONFIG += parallel_test
+TARGET = tst_qopenglwidget
+QT += gui-private core-private testlib widgets
+
+SOURCES += tst_qopenglwidget.cpp
+
+win32-msvc2010:contains(QT_CONFIG, angle):CONFIG += insignificant_test # QTBUG-31611
diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
new file mode 100644
index 0000000000..14d06e7111
--- /dev/null
+++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** 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 <QtWidgets/QOpenGLWidget>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QPainter>
+#include <QtTest/QtTest>
+#include <QSignalSpy>
+
+class tst_QOpenGLWidget : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void create();
+ void clearAndGrab();
+ void clearAndResizeAndGrab();
+ void createNonTopLevel();
+ void painter();
+ void reparentToAlreadyCreated();
+ void reparentToNotYetCreated();
+};
+
+void tst_QOpenGLWidget::create()
+{
+ QScopedPointer<QOpenGLWidget> w(new QOpenGLWidget);
+ QVERIFY(!w->isValid());
+ QSignalSpy frameSwappedSpy(w.data(), SIGNAL(frameSwapped()));
+ w->show();
+ QTest::qWaitForWindowExposed(w.data());
+ QVERIFY(frameSwappedSpy.count() > 0);
+
+ QVERIFY(w->isValid());
+ QVERIFY(w->context());
+ QVERIFY(w->context()->format() == w->format());
+ QVERIFY(w->defaultFramebufferObject() != 0);
+}
+
+class ClearWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+public:
+ ClearWidget(QWidget *parent, int expectedWidth, int expectedHeight)
+ : QOpenGLWidget(parent),
+ m_initCalled(false), m_paintCalled(false), m_resizeCalled(false),
+ m_resizeOk(false),
+ m_w(expectedWidth), m_h(expectedHeight) { }
+
+ void initializeGL() Q_DECL_OVERRIDE {
+ m_initCalled = true;
+ initializeOpenGLFunctions();
+ }
+ void paintGL() Q_DECL_OVERRIDE {
+ m_paintCalled = true;
+ glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ void resizeGL(int w, int h) Q_DECL_OVERRIDE {
+ m_resizeCalled = true;
+ m_resizeOk = w == m_w && h == m_h;
+ }
+
+ bool m_initCalled;
+ bool m_paintCalled;
+ bool m_resizeCalled;
+ bool m_resizeOk;
+ int m_w;
+ int m_h;
+};
+
+void tst_QOpenGLWidget::clearAndGrab()
+{
+ QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
+ w->resize(800, 600);
+ w->show();
+ QTest::qWaitForWindowExposed(w.data());
+ QVERIFY(w->m_initCalled);
+ QVERIFY(w->m_resizeCalled);
+ QVERIFY(w->m_resizeOk);
+ QVERIFY(w->m_paintCalled);
+
+ QImage image = w->grabFramebuffer();
+ QVERIFY(!image.isNull());
+ QCOMPARE(image.width(), w->width());
+ QCOMPARE(image.height(), w->height());
+ QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+}
+
+void tst_QOpenGLWidget::clearAndResizeAndGrab()
+{
+ QScopedPointer<QOpenGLWidget> w(new ClearWidget(0, 640, 480));
+ w->resize(640, 480);
+ w->show();
+ QTest::qWaitForWindowExposed(w.data());
+
+ QImage image = w->grabFramebuffer();
+ QVERIFY(!image.isNull());
+ QCOMPARE(image.width(), w->width());
+ QCOMPARE(image.height(), w->height());
+ w->resize(800, 600);
+ image = w->grabFramebuffer();
+ QVERIFY(!image.isNull());
+ QCOMPARE(image.width(), 800);
+ QCOMPARE(image.height(), 600);
+ QCOMPARE(image.width(), w->width());
+ QCOMPARE(image.height(), w->height());
+ QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+}
+
+void tst_QOpenGLWidget::createNonTopLevel()
+{
+ QWidget w;
+ ClearWidget *glw = new ClearWidget(&w, 600, 700);
+ QSignalSpy frameSwappedSpy(glw, SIGNAL(frameSwapped()));
+ w.resize(400, 400);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+ QVERIFY(frameSwappedSpy.count() > 0);
+
+ QVERIFY(glw->m_resizeCalled);
+ glw->m_resizeCalled = false;
+ QVERIFY(!glw->m_resizeOk);
+ glw->resize(600, 700);
+
+ QVERIFY(glw->m_initCalled);
+ QVERIFY(glw->m_resizeCalled);
+ QVERIFY(glw->m_resizeOk);
+ QVERIFY(glw->m_paintCalled);
+
+ QImage image = glw->grabFramebuffer();
+ QVERIFY(!image.isNull());
+ QCOMPARE(image.width(), glw->width());
+ QCOMPARE(image.height(), glw->height());
+ QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+
+ glw->doneCurrent();
+ QVERIFY(!QOpenGLContext::currentContext());
+ glw->makeCurrent();
+ QVERIFY(QOpenGLContext::currentContext() == glw->context() && glw->context());
+}
+
+class PainterWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+public:
+ PainterWidget(QWidget *parent)
+ : QOpenGLWidget(parent), m_clear(false) { }
+
+ void initializeGL() Q_DECL_OVERRIDE {
+ initializeOpenGLFunctions();
+ }
+ void paintGL() Q_DECL_OVERRIDE {
+ QPainter p(this);
+ QCOMPARE(p.device()->width(), width());
+ QCOMPARE(p.device()->height(), height());
+ p.fillRect(QRect(QPoint(0, 0), QSize(width(), height())), Qt::blue);
+ if (m_clear) {
+ p.beginNativePainting();
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ p.endNativePainting();
+ }
+ }
+ bool m_clear;
+};
+
+void tst_QOpenGLWidget::painter()
+{
+ QWidget w;
+ PainterWidget *glw = new PainterWidget(&w);
+ w.resize(640, 480);
+ glw->resize(320, 200);
+ w.show();
+ QTest::qWaitForWindowExposed(&w);
+
+ QImage image = glw->grabFramebuffer();
+ QCOMPARE(image.width(), glw->width());
+ QCOMPARE(image.height(), glw->height());
+ QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255));
+
+ glw->m_clear = true;
+ image = glw->grabFramebuffer();
+ QVERIFY(image.pixel(20, 10) == qRgb(0, 255, 0));
+}
+
+void tst_QOpenGLWidget::reparentToAlreadyCreated()
+{
+ QWidget w1;
+ PainterWidget *glw = new PainterWidget(&w1);
+ w1.resize(640, 480);
+ glw->resize(320, 200);
+ w1.show();
+ QTest::qWaitForWindowExposed(&w1);
+
+ QWidget w2;
+ w2.show();
+ QTest::qWaitForWindowExposed(&w2);
+
+ glw->setParent(&w2);
+ glw->show();
+
+ QImage image = glw->grabFramebuffer();
+ QCOMPARE(image.width(), 320);
+ QCOMPARE(image.height(), 200);
+ QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255));
+}
+
+void tst_QOpenGLWidget::reparentToNotYetCreated()
+{
+ QWidget w1;
+ PainterWidget *glw = new PainterWidget(&w1);
+ w1.resize(640, 480);
+ glw->resize(320, 200);
+ w1.show();
+ QTest::qWaitForWindowExposed(&w1);
+
+ QWidget w2;
+ glw->setParent(&w2);
+ w2.show();
+ QTest::qWaitForWindowExposed(&w2);
+
+ QImage image = glw->grabFramebuffer();
+ QCOMPARE(image.width(), 320);
+ QCOMPARE(image.height(), 200);
+ QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255));
+}
+
+QTEST_MAIN(tst_QOpenGLWidget)
+
+#include "tst_qopenglwidget.moc"
diff --git a/tests/auto/widgets/widgets/widgets.pro b/tests/auto/widgets/widgets/widgets.pro
index 29d1f7746c..423b3952d4 100644
--- a/tests/auto/widgets/widgets/widgets.pro
+++ b/tests/auto/widgets/widgets/widgets.pro
@@ -52,3 +52,5 @@ SUBDIRS=\
qmainwindow \
qtextedit \
qtoolbar \
+
+contains(QT_CONFIG, opengl): SUBDIRS += qopenglwidget
diff --git a/tests/manual/qopenglwidget/openglwidget/main.cpp b/tests/manual/qopenglwidget/openglwidget/main.cpp
index 68f9be7199..0268d47767 100644
--- a/tests/manual/qopenglwidget/openglwidget/main.cpp
+++ b/tests/manual/qopenglwidget/openglwidget/main.cpp
@@ -45,20 +45,32 @@
#include <QMdiArea>
#include <QLCDNumber>
#include <QTimer>
-
+#include <QSurfaceFormat>
+#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
+ QSurfaceFormat format;
+ if (QCoreApplication::arguments().contains(QLatin1String("--multisample")))
+ format.setSamples(4);
+ if (QCoreApplication::arguments().contains(QLatin1String("--coreprofile"))) {
+ format.setVersion(3, 2);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ }
+ qDebug() << "Requesting" << format;
+
QMdiArea w;
w.resize(400,400);
OpenGLWidget *glw = new OpenGLWidget;
+ glw->setFormat(format);
w.addSubWindow(glw);
glw->setMinimumSize(100,100);
OpenGLWidget *glw2 = new OpenGLWidget;
+ glw2->setFormat(format);
glw2->setMinimumSize(100,100);
w.addSubWindow(glw2);
@@ -69,5 +81,8 @@ int main(int argc, char *argv[])
w.show();
+ if (glw->isValid())
+ qDebug() << "Got" << glw->format();
+
return a.exec();
}
diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
index bec89b6b41..516de35ff8 100644
--- a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
+++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
@@ -58,7 +58,7 @@
#include <QtCore/qmath.h>
#include <qopengl.h>
-class OpenGLWidgetPrivate
+class OpenGLWidgetPrivate : protected QOpenGLFunctions
{
public:
OpenGLWidgetPrivate(QWidget *q)
@@ -91,7 +91,7 @@ OpenGLWidget::OpenGLWidget(QWidget *parent)
{
d = new OpenGLWidgetPrivate(this);
QTimer *timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
+ connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(30);
}
@@ -137,6 +137,7 @@ static const char *fragmentShaderSource =
void OpenGLWidgetPrivate::initialize()
{
+ initializeOpenGLFunctions();
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
@@ -157,7 +158,7 @@ void OpenGLWidgetPrivate::render()
m_program->bind();
QMatrix4x4 matrix;
- matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
+ matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
matrix.translate(0, 0, -2);
matrix.rotate(100.0f * m_frame / 30/*screen()->refreshRate()*/, 0, 1, 0);
diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.h b/tests/manual/qopenglwidget/openglwidget/openglwidget.h
index eaba27df26..ada01d4cc1 100644
--- a/tests/manual/qopenglwidget/openglwidget/openglwidget.h
+++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.h
@@ -42,7 +42,7 @@
#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H
-#include <QtWidgets/private/qopenglwidget_p.h>
+#include <QtWidgets/QOpenGLWidget>
class OpenGLWidgetPrivate;
class OpenGLWidget : public QOpenGLWidget