Introduce QOpenGLWindow
[ChangeLog] Added QOpenGLWindow. This serves as a convenience class for creating windows showing OpenGL content via an API similar to QGLWidget and without any widget dependencies. Done-with: Jorgen Lind <jorgen.lind@digia.com> Task-number: QTBUG-36899 Change-Id: I52e9bc61acb129dbfd3841b3adeffab2dbcf7f05 Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
This commit is contained in:
parent
b53e08e335
commit
e48737ae77
@ -6,7 +6,8 @@ contains(QT_CONFIG, dynamicgl) {
|
||||
SUBDIRS = hellowindow \
|
||||
contextinfo \
|
||||
qopenglwidget \
|
||||
threadedqopenglwidget
|
||||
threadedqopenglwidget \
|
||||
qopenglwindow
|
||||
} else: !contains(QT_CONFIG, opengles2) {
|
||||
SUBDIRS = 2dpainting \
|
||||
grabber \
|
||||
@ -23,6 +24,7 @@ contains(QT_CONFIG, dynamicgl) {
|
||||
cube \
|
||||
textures \
|
||||
qopenglwidget \
|
||||
threadedqopenglwidget
|
||||
threadedqopenglwidget \
|
||||
qopenglwindow
|
||||
|
||||
EXAMPLE_FILES = shared
|
||||
|
24
examples/opengl/qopenglwindow/background.frag
Normal file
24
examples/opengl/qopenglwindow/background.frag
Normal file
@ -0,0 +1,24 @@
|
||||
uniform highp int currentTime;
|
||||
uniform highp vec2 windowSize;
|
||||
|
||||
float noise(vec2 co)
|
||||
{
|
||||
return 0.5 * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
float curvSpeed()
|
||||
{
|
||||
return mod(float(currentTime), 1000000.0) / 500.0;
|
||||
}
|
||||
|
||||
float curv()
|
||||
{
|
||||
return 1.0 - abs((gl_FragCoord.y / (windowSize.y / 10.0) - 5.0) - sin((gl_FragCoord.x / (windowSize.x/20.0)) - curvSpeed()));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float coordNoise = noise(gl_FragCoord.xy);
|
||||
float proximity = smoothstep(0.5, 1.0, (curv() + 1.0) * (coordNoise ));
|
||||
gl_FragColor = vec4(coordNoise, coordNoise, coordNoise, 1.0) * proximity;
|
||||
}
|
201
examples/opengl/qopenglwindow/background_renderer.cpp
Normal file
201
examples/opengl/qopenglwindow/background_renderer.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "background_renderer.h"
|
||||
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QTime>
|
||||
|
||||
#include <QtGui/QOpenGLShaderProgram>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtGui/QOpenGLFunctions>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static const char vertex_shader[] =
|
||||
"attribute highp vec3 vertexCoord;"
|
||||
"void main() {"
|
||||
" gl_Position = vec4(vertexCoord,1.0);"
|
||||
"}";
|
||||
|
||||
static const char fragment_shader[] =
|
||||
"void main() {"
|
||||
" gl_FragColor = vec4(0.0,1.0,0.0,1.0);"
|
||||
"}";
|
||||
|
||||
static const float vertices[] = { -1, -1, 0,
|
||||
-1, 1, 0,
|
||||
1, -1, 0,
|
||||
1, 1, 0 };
|
||||
|
||||
FragmentToy::FragmentToy(const QString &fragmentSource, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_recompile_shaders(true)
|
||||
{
|
||||
if (QFile::exists(fragmentSource)) {
|
||||
QFileInfo info(fragmentSource);
|
||||
m_fragment_file_last_modified = info.lastModified();
|
||||
m_fragment_file = fragmentSource;
|
||||
#ifndef QT_NO_FILESYSTEMWATCHER
|
||||
m_watcher.addPath(info.canonicalFilePath());
|
||||
QObject::connect(&m_watcher, &QFileSystemWatcher::fileChanged, this, &FragmentToy::fileChanged);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FragmentToy::draw(const QSize &windowSize)
|
||||
{
|
||||
if (!m_program)
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glClearColor(0,0,0,0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (!m_vao.isCreated())
|
||||
m_vao.create();
|
||||
|
||||
m_vao.bind();
|
||||
|
||||
if (!m_vertex_buffer.isCreated()) {
|
||||
m_vertex_buffer.create();
|
||||
m_vertex_buffer.bind();
|
||||
m_vertex_buffer.allocate(vertices, sizeof(vertices));
|
||||
m_vertex_buffer.release();
|
||||
}
|
||||
|
||||
if (!m_program) {
|
||||
m_program.reset(new QOpenGLShaderProgram);
|
||||
m_program->create();
|
||||
m_vertex_shader.reset(new QOpenGLShader(QOpenGLShader::Vertex));
|
||||
if (!m_vertex_shader->compileSourceCode(vertex_shader)) {
|
||||
qWarning() << "Failed to compile the vertex shader:" << m_vertex_shader->log();
|
||||
}
|
||||
if (!m_program->addShader(m_vertex_shader.data())) {
|
||||
qWarning() << "Failed to add vertex shader to program:" << m_program->log();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_fragment_shader) {
|
||||
QByteArray data;
|
||||
if (m_fragment_file.size()) {
|
||||
QFile file(m_fragment_file);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
data = file.readAll();
|
||||
} else {
|
||||
qWarning() << "Failed to load input file, falling back to default";
|
||||
data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
|
||||
}
|
||||
} else {
|
||||
QFile qrcFile(":/background.frag");
|
||||
if (qrcFile.open(QIODevice::ReadOnly))
|
||||
data = qrcFile.readAll();
|
||||
else
|
||||
data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
|
||||
}
|
||||
if (data.size()) {
|
||||
m_fragment_shader.reset(new QOpenGLShader(QOpenGLShader::Fragment));
|
||||
if (!m_fragment_shader->compileSourceCode(data)) {
|
||||
qWarning() << "Failed to compile fragment shader:" << m_fragment_shader->log();
|
||||
m_fragment_shader.reset(Q_NULLPTR);
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Unknown error, no fragment shader";
|
||||
}
|
||||
|
||||
if (m_fragment_shader) {
|
||||
if (!m_program->addShader(m_fragment_shader.data())) {
|
||||
qWarning() << "Failed to add fragment shader to program:" << m_program->log();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_recompile_shaders) {
|
||||
m_recompile_shaders = false;
|
||||
|
||||
if (m_program->link()) {
|
||||
m_vertex_coord_pos = m_program->attributeLocation("vertexCoord");
|
||||
} else {
|
||||
qWarning() << "Failed to link shader program" << m_program->log();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_program->bind();
|
||||
|
||||
m_vertex_buffer.bind();
|
||||
m_program->setAttributeBuffer("vertexCoord", GL_FLOAT, 0, 3, 0);
|
||||
m_program->enableAttributeArray("vertexCoord");
|
||||
m_vertex_buffer.release();
|
||||
|
||||
float radiens = (QTime::currentTime().msecsSinceStartOfDay() / 60) / (2 * M_PI);
|
||||
m_program->setUniformValue("currentTime", (uint) QDateTime::currentDateTime().toMSecsSinceEpoch());
|
||||
m_program->setUniformValue("radiens", radiens);
|
||||
m_program->setUniformValue("windowSize", windowSize);
|
||||
|
||||
QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
m_program->release();
|
||||
m_vao.release();
|
||||
}
|
||||
|
||||
void FragmentToy::fileChanged(const QString &path)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
if (QFile::exists(m_fragment_file)) {
|
||||
QFileInfo fragment_source(m_fragment_file);
|
||||
if (fragment_source.lastModified() > m_fragment_file_last_modified) {
|
||||
m_fragment_file_last_modified = fragment_source.lastModified();
|
||||
m_recompile_shaders = true;
|
||||
if (m_program) {
|
||||
m_program->removeShader(m_fragment_shader.data());
|
||||
m_fragment_shader.reset(Q_NULLPTR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_recompile_shaders = true;
|
||||
if (m_program) {
|
||||
m_program->removeShader(m_fragment_shader.data());
|
||||
m_fragment_shader.reset(Q_NULLPTR);
|
||||
}
|
||||
}
|
||||
}
|
78
examples/opengl/qopenglwindow/background_renderer.h
Normal file
78
examples/opengl/qopenglwindow/background_renderer.h
Normal file
@ -0,0 +1,78 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef FRAGMENT_TOY_H
|
||||
#define FRAGMENT_TOY_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QFileSystemWatcher>
|
||||
#include <QtGui/QOpenGLVertexArrayObject>
|
||||
#include <QtGui/QOpenGLBuffer>
|
||||
#include <QtGui/QOpenGLShaderProgram>
|
||||
#include <QtGui/QOpenGLFunctions>
|
||||
|
||||
class FragmentToy : public QObject, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FragmentToy(const QString &fragmentSource, QObject *parent = 0);
|
||||
|
||||
void draw(const QSize &windowSize);
|
||||
|
||||
private:
|
||||
void fileChanged(const QString &path);
|
||||
bool m_recompile_shaders;
|
||||
#ifndef QT_NO_FILESYSTEMWATCHER
|
||||
QFileSystemWatcher m_watcher;
|
||||
#endif
|
||||
QString m_fragment_file;
|
||||
QDateTime m_fragment_file_last_modified;
|
||||
|
||||
QScopedPointer<QOpenGLShaderProgram> m_program;
|
||||
QScopedPointer<QOpenGLShader> m_vertex_shader;
|
||||
QScopedPointer<QOpenGLShader> m_fragment_shader;
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
QOpenGLBuffer m_vertex_buffer;
|
||||
GLuint m_vertex_coord_pos;
|
||||
};
|
||||
|
||||
#endif //FRAGMENT_TOY_H
|
159
examples/opengl/qopenglwindow/main.cpp
Normal file
159
examples/opengl/qopenglwindow/main.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtGui/QOpenGLWindow>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QMatrix4x4>
|
||||
#include <QtGui/QStaticText>
|
||||
|
||||
#include "background_renderer.h"
|
||||
|
||||
static QPainterPath painterPathForTriangle()
|
||||
{
|
||||
static const QPointF bottomLeft(-1.0, -1.0);
|
||||
static const QPointF top(0.0, 1.0);
|
||||
static const QPointF bottomRight(1.0, -1.0);
|
||||
|
||||
QPainterPath path(bottomLeft);
|
||||
path.lineTo(top);
|
||||
path.lineTo(bottomRight);
|
||||
path.closeSubpath();
|
||||
return path;
|
||||
}
|
||||
|
||||
class OpenGLWindow : public QOpenGLWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Use NoPartialUpdate. This means that all the rendering goes directly to
|
||||
// the window surface, no additional framebuffer object stands in the
|
||||
// middle. This is fine since we will clear the entire framebuffer on each
|
||||
// paint. Under the hood this means that the behavior is equivalent to the
|
||||
// manual makeCurrent - perform OpenGL calls - swapBuffers loop that is
|
||||
// typical in pure QWindow-based applications.
|
||||
OpenGLWindow()
|
||||
: QOpenGLWindow(QOpenGLWindow::NoPartialUpdate)
|
||||
, m_fragment_toy("background.frag")
|
||||
, m_text_layout("The triangle and this text is rendered with QPainter")
|
||||
{
|
||||
m_view.lookAt(QVector3D(3,1,1),
|
||||
QVector3D(0,0,0),
|
||||
QVector3D(0,1,0));
|
||||
connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
|
||||
|
||||
QLinearGradient gradient(QPointF(-1,-1), QPointF(1,1));
|
||||
gradient.setColorAt(0, Qt::red);
|
||||
gradient.setColorAt(1, Qt::green);
|
||||
|
||||
m_brush = QBrush(gradient);
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintGL() Q_DECL_OVERRIDE
|
||||
{
|
||||
m_fragment_toy.draw(size());
|
||||
|
||||
QPainter p(this);
|
||||
p.setWorldTransform(m_window_normalised_matrix.toTransform());
|
||||
|
||||
QMatrix4x4 mvp = m_projection * m_view * m_model_triangle;
|
||||
p.setTransform(mvp.toTransform(), true);
|
||||
|
||||
p.fillPath(painterPathForTriangle(), m_brush);
|
||||
|
||||
QTransform text_transform = (m_window_painter_matrix * m_view * m_model_text).toTransform();
|
||||
p.setTransform(text_transform, false);
|
||||
p.setPen(QPen(Qt::white));
|
||||
m_text_layout.prepare(text_transform);
|
||||
qreal x = - (m_text_layout.size().width() / 2);
|
||||
qreal y = 0;
|
||||
p.drawStaticText(x, y, m_text_layout);
|
||||
|
||||
m_model_triangle.rotate(-1, 0, 1, 0);
|
||||
m_model_text.rotate(1, 0, 1, 0);
|
||||
}
|
||||
|
||||
void resizeGL(int w, int h) Q_DECL_OVERRIDE
|
||||
{
|
||||
m_window_normalised_matrix.setToIdentity();
|
||||
m_window_normalised_matrix.translate(w / 2.0, h / 2.0);
|
||||
m_window_normalised_matrix.scale(w / 2.0, -h / 2.0);
|
||||
|
||||
m_window_painter_matrix.setToIdentity();
|
||||
m_window_painter_matrix.translate(w / 2.0, h / 2.0);
|
||||
|
||||
m_text_layout.setTextWidth(std::max(w * 0.2, 80.0));
|
||||
|
||||
m_projection.setToIdentity();
|
||||
m_projection.perspective(45.f, qreal(w) / qreal(h), 0.1f, 100.f);
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
QMatrix4x4 m_window_normalised_matrix;
|
||||
QMatrix4x4 m_window_painter_matrix;
|
||||
QMatrix4x4 m_projection;
|
||||
QMatrix4x4 m_view;
|
||||
QMatrix4x4 m_model_triangle;
|
||||
QMatrix4x4 m_model_text;
|
||||
QBrush m_brush;
|
||||
|
||||
FragmentToy m_fragment_toy;
|
||||
QStaticText m_text_layout;
|
||||
};
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
OpenGLWindow window;
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setDepthBufferSize(24);
|
||||
fmt.setStencilBufferSize(8);
|
||||
window.setFormat(fmt);
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
15
examples/opengl/qopenglwindow/qopenglwindow.pro
Normal file
15
examples/opengl/qopenglwindow/qopenglwindow.pro
Normal file
@ -0,0 +1,15 @@
|
||||
TEMPLATE = app
|
||||
TARGET = qopenglwindow
|
||||
INCLUDEPATH += .
|
||||
|
||||
RESOURCES += shaders.qrc
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
background_renderer.cpp
|
||||
|
||||
HEADERS += \
|
||||
background_renderer.h
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwindow
|
||||
INSTALLS += target
|
5
examples/opengl/qopenglwindow/shaders.qrc
Normal file
5
examples/opengl/qopenglwindow/shaders.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>background.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -69,7 +69,9 @@ HEADERS += \
|
||||
kernel/qplatformscreenpageflipper.h \
|
||||
kernel/qplatformsystemtrayicon.h \
|
||||
kernel/qplatformsessionmanager.h \
|
||||
kernel/qpixelformat.h
|
||||
kernel/qpixelformat.h \
|
||||
kernel/qpaintdevicewindow.h \
|
||||
kernel/qpaintdevicewindow_p.h
|
||||
|
||||
SOURCES += \
|
||||
kernel/qgenericpluginfactory.cpp \
|
||||
@ -121,17 +123,20 @@ SOURCES += \
|
||||
kernel/qplatformsystemtrayicon.cpp \
|
||||
kernel/qplatformsessionmanager.cpp \
|
||||
kernel/qplatformmenu.cpp \
|
||||
kernel/qpixelformat.cpp
|
||||
kernel/qpixelformat.cpp \
|
||||
kernel/qpaintdevicewindow.cpp
|
||||
|
||||
contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles2) {
|
||||
HEADERS += \
|
||||
kernel/qplatformopenglcontext.h \
|
||||
kernel/qopenglcontext.h \
|
||||
kernel/qopenglcontext_p.h
|
||||
kernel/qopenglcontext_p.h \
|
||||
kernel/qopenglwindow.h
|
||||
|
||||
SOURCES += \
|
||||
kernel/qplatformopenglcontext.cpp \
|
||||
kernel/qopenglcontext.cpp
|
||||
kernel/qopenglcontext.cpp \
|
||||
kernel/qopenglwindow.cpp
|
||||
}
|
||||
|
||||
win32:HEADERS+=kernel/qwindowdefs_win.h
|
||||
|
615
src/gui/kernel/qopenglwindow.cpp
Normal file
615
src/gui/kernel/qopenglwindow.cpp
Normal file
@ -0,0 +1,615 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qopenglwindow.h"
|
||||
#include "qpaintdevicewindow_p.h"
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
#include <QtGui/QOpenGLPaintDevice>
|
||||
#include <QtGui/QOpenGLFunctions>
|
||||
#include <QtGui/private/qopengltextureblitter_p.h>
|
||||
#include <QtGui/private/qopenglextensions_p.h>
|
||||
#include <QtGui/QMatrix4x4>
|
||||
#include <QtGui/QOffscreenSurface>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QOpenGLWindow
|
||||
\inmodule QtGui
|
||||
\since 5.4
|
||||
\brief The QOpenGLWindow class is a convenience subclass of QWindow to perform OpenGL painting.
|
||||
|
||||
QOpenGLWindow is an enhanced QWindow that allows easily creating windows that
|
||||
perform OpenGL rendering using an API that is compatible with QOpenGLWidget
|
||||
and is similar to the legacy QGLWidget. Unlike QOpenGLWidget, QOpenGLWindow
|
||||
has no dependency on the widgets module and offers better performance.
|
||||
|
||||
A typical application will subclass QOpenGLWindow and reimplement the following
|
||||
virtual functions:
|
||||
|
||||
\list
|
||||
|
||||
\li initializeGL() to perform OpenGL resource initialization
|
||||
|
||||
\li resizeGL() to set up the transformation matrices and other window size dependent resources
|
||||
|
||||
\li paintGL() to issue OpenGL commands or draw using QPainter
|
||||
|
||||
\endlist
|
||||
|
||||
To schedule a repaint, call the update() function. Note that this will not
|
||||
immediately result in a call to paintGL(). Calling update() multiple times in
|
||||
a row will not change the behavior in any way.
|
||||
|
||||
This is a slot so it can be connected to a \l QTimer::timeout() signal to
|
||||
perform animation. Note however that in the modern OpenGL world it is a much
|
||||
better choice to rely on synchronization to the vertical refresh rate of the
|
||||
display. See \l{QSurfaceFormat::setSwapInterval()}{setSwapInterval()} on a
|
||||
description of the swap interval. With a swap interval of \c 1, which is the
|
||||
case on most systems by default, the
|
||||
\l{QOpenGLContext::swapBuffers()}{swapBuffers()} call, that is executed
|
||||
internally by QOpenGLWindow after each repaint, will block and wait for
|
||||
vsync. This means that whenever the swap is done, an update can be scheduled
|
||||
again by calling update(), without relying on timers.
|
||||
|
||||
To request a specific configuration for the context, use setFormat()
|
||||
like for any other QWindow. This allows, among others, requesting a
|
||||
given OpenGL version and profile, or enabling depth and stencil
|
||||
buffers.
|
||||
|
||||
Unlike QWindow, QOpenGLWindow allows opening a painter on itself and perform
|
||||
QPainter-based drawing.
|
||||
|
||||
QOpenGLWindow supports multiple update behaviors. The default,
|
||||
\c NoPartialUpdate is equivalent to a regular, OpenGL-based QWindow or the
|
||||
legacy QGLWidget. In contrast, \c PartialUpdateBlit and \c PartialUpdateBlend are
|
||||
more in line with QOpenGLWidget's way of working, where there is always an
|
||||
extra, dedicated framebuffer object present. These modes allow, by
|
||||
sacrificing some performance, redrawing only a smaller area on each paint and
|
||||
having the rest of the content preserved from of the previous frame. This is
|
||||
useful for applications than render incrementally using QPainter, because
|
||||
this way they do not have to redraw the entire window content on each
|
||||
paintGL() call.
|
||||
|
||||
For more information on graphics in Qt, see \l {Graphics}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QOpenGLWindow::UpdateBehavior
|
||||
|
||||
This enum describes the update strategy of the QOpenGLWindow.
|
||||
|
||||
\value NoPartialUpdate Indicates that the entire window surface will
|
||||
redrawn on each update and so no additional framebuffers are needed.
|
||||
This is the setting used in most cases and is equivalent to how drawing
|
||||
directly via QWindow would function.
|
||||
|
||||
\value PartialUpdateBlit Indicates that the drawing performed in paintGL()
|
||||
does not cover the entire window. In this case an extra framebuffer object
|
||||
is created under the hood, and rendering performed in paintGL() will target
|
||||
this framebuffer. This framebuffer is then blitted onto the window surface's
|
||||
default framebuffer after each paint. This allows having QPainter-based drawing
|
||||
code in paintGL() which only repaints a smaller area at a time, because, unlike
|
||||
NoPartialUpdate, the previous content is preserved.
|
||||
|
||||
\value PartialUpdateBlend Similar to PartialUpdateBlit, but instead of using
|
||||
framebuffer blits, the contents of the extra framebuffer is rendered by
|
||||
drawing a textured quad with blending enabled. This, unlike PartialUpdateBlit,
|
||||
allows alpha blended content and works even when the glBlitFramebuffer is
|
||||
not available. Performance-wise this setting is likely to be somewhat slower
|
||||
than PartialUpdateBlit.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QOpenGLWindow::frameSwapped()
|
||||
|
||||
This signal is emitted after the potentially blocking
|
||||
\l{QOpenGLContext::swapBuffers()}{buffer swap} has been done. Applications
|
||||
that wish to continuously repaint synchronized to the vertical refresh,
|
||||
should issue an update() upon this signal. This allows for a much smoother
|
||||
experience compared to the traditional usage of timers.
|
||||
*/
|
||||
|
||||
// GLES2 builds won't have these constants with the suffixless names
|
||||
#ifndef GL_READ_FRAMEBUFFER
|
||||
#define GL_READ_FRAMEBUFFER 0x8CA8
|
||||
#endif
|
||||
#ifndef GL_DRAW_FRAMEBUFFER
|
||||
#define GL_DRAW_FRAMEBUFFER 0x8CA9
|
||||
#endif
|
||||
|
||||
class QOpenGLWindowPaintDevice : public QOpenGLPaintDevice
|
||||
{
|
||||
public:
|
||||
QOpenGLWindowPaintDevice(QOpenGLWindow *window) : m_window(window) { }
|
||||
void ensureActiveTarget() Q_DECL_OVERRIDE;
|
||||
|
||||
QOpenGLWindow *m_window;
|
||||
};
|
||||
|
||||
class QOpenGLWindowPrivate : public QPaintDeviceWindowPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QOpenGLWindow)
|
||||
public:
|
||||
QOpenGLWindowPrivate(QOpenGLWindow::UpdateBehavior updateBehavior)
|
||||
: updateBehavior(updateBehavior)
|
||||
, hasFboBlit(false)
|
||||
{
|
||||
}
|
||||
|
||||
~QOpenGLWindowPrivate()
|
||||
{
|
||||
Q_Q(QOpenGLWindow);
|
||||
if (q->isValid()) {
|
||||
q->makeCurrent(); // this works even when the platformwindow is destroyed
|
||||
paintDevice.reset(0);
|
||||
fbo.reset(0);
|
||||
blitter.destroy();
|
||||
q->doneCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
static QOpenGLWindowPrivate *get(QOpenGLWindow *w) { return w->d_func(); }
|
||||
|
||||
void bindFBO()
|
||||
{
|
||||
if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
|
||||
fbo->bind();
|
||||
else
|
||||
QOpenGLFramebufferObject::bindDefault();
|
||||
}
|
||||
|
||||
void beginPaint(const QRegion ®ion) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(region);
|
||||
Q_Q(QOpenGLWindow);
|
||||
|
||||
if (!context) {
|
||||
context.reset(new QOpenGLContext);
|
||||
context->setFormat(q->requestedFormat());
|
||||
if (!context->create())
|
||||
qWarning("QOpenGLWindow::beginPaint: Failed to create context");
|
||||
if (!context->makeCurrent(q))
|
||||
qWarning("QOpenGLWindow::beginPaint: Failed to make context current");
|
||||
|
||||
paintDevice.reset(new QOpenGLWindowPaintDevice(q));
|
||||
if (updateBehavior == QOpenGLWindow::PartialUpdateBlit)
|
||||
hasFboBlit = QOpenGLFramebufferObject::hasOpenGLFramebufferBlit();
|
||||
|
||||
q->initializeGL();
|
||||
} else {
|
||||
context->makeCurrent(q);
|
||||
}
|
||||
|
||||
if (updateBehavior > QOpenGLWindow::NoPartialUpdate) {
|
||||
if (!fbo || fbo->size() != q->size()) {
|
||||
QOpenGLFramebufferObjectFormat fboFormat;
|
||||
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||
if (q->requestedFormat().samples() > 0) {
|
||||
if (updateBehavior != QOpenGLWindow::PartialUpdateBlend)
|
||||
fboFormat.setSamples(q->requestedFormat().samples());
|
||||
else
|
||||
qWarning("QOpenGLWindow: PartialUpdateBlend does not support multisampling");
|
||||
}
|
||||
fbo.reset(new QOpenGLFramebufferObject(q->size(), fboFormat));
|
||||
markWindowAsDirty();
|
||||
}
|
||||
} else {
|
||||
markWindowAsDirty();
|
||||
}
|
||||
|
||||
const int deviceWidth = q->width() * q->devicePixelRatio();
|
||||
const int deviceHeight = q->height() * q->devicePixelRatio();
|
||||
paintDevice->setSize(QSize(deviceWidth, deviceHeight));
|
||||
context->functions()->glViewport(0, 0, deviceWidth, deviceHeight);
|
||||
|
||||
context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject());
|
||||
|
||||
q->paintUnderGL();
|
||||
|
||||
if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
|
||||
fbo->bind();
|
||||
}
|
||||
|
||||
void endPaint() Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_Q(QOpenGLWindow);
|
||||
|
||||
if (updateBehavior > QOpenGLWindow::NoPartialUpdate)
|
||||
fbo->release();
|
||||
|
||||
context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject());
|
||||
|
||||
if (updateBehavior == QOpenGLWindow::PartialUpdateBlit && hasFboBlit) {
|
||||
QOpenGLExtensions extensions(context.data());
|
||||
extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle());
|
||||
extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, context->defaultFramebufferObject());
|
||||
extensions.glBlitFramebuffer(0, 0, q->width(), q->height(),
|
||||
0, 0, q->width(), q->height(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
} else if (updateBehavior > QOpenGLWindow::NoPartialUpdate) {
|
||||
if (updateBehavior == QOpenGLWindow::PartialUpdateBlend) {
|
||||
context->functions()->glEnable(GL_BLEND);
|
||||
context->functions()->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
if (!blitter.isCreated())
|
||||
blitter.create();
|
||||
|
||||
QRect windowRect(QPoint(0, 0), q->size());
|
||||
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(windowRect, windowRect);
|
||||
blitter.bind();
|
||||
blitter.blit(fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
|
||||
blitter.release();
|
||||
|
||||
if (updateBehavior == QOpenGLWindow::PartialUpdateBlend)
|
||||
context->functions()->glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
q->paintOverGL();
|
||||
}
|
||||
|
||||
void flush(const QRegion ®ion) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(region);
|
||||
Q_Q(QOpenGLWindow);
|
||||
context->swapBuffers(q);
|
||||
emit q->frameSwapped();
|
||||
}
|
||||
|
||||
QOpenGLWindow::UpdateBehavior updateBehavior;
|
||||
bool hasFboBlit;
|
||||
QScopedPointer<QOpenGLContext> context;
|
||||
QScopedPointer<QOpenGLFramebufferObject> fbo;
|
||||
QScopedPointer<QOpenGLWindowPaintDevice> paintDevice;
|
||||
QOpenGLTextureBlitter blitter;
|
||||
QColor backgroundColor;
|
||||
QScopedPointer<QOffscreenSurface> offscreenSurface;
|
||||
};
|
||||
|
||||
void QOpenGLWindowPaintDevice::ensureActiveTarget()
|
||||
{
|
||||
QOpenGLWindowPrivate::get(m_window)->bindFBO();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a new QOpenGLWindow with the given \a parent and \a updateBehavior.
|
||||
|
||||
\sa QOpenGLWindow::UpdateBehavior
|
||||
*/
|
||||
QOpenGLWindow::QOpenGLWindow(QOpenGLWindow::UpdateBehavior updateBehavior, QWindow *parent)
|
||||
: QPaintDeviceWindow(*(new QOpenGLWindowPrivate(updateBehavior)), parent)
|
||||
{
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the update behavior for this QOpenGLWindow.
|
||||
*/
|
||||
QOpenGLWindow::UpdateBehavior QOpenGLWindow::updateBehavior() const
|
||||
{
|
||||
Q_D(const QOpenGLWindow);
|
||||
return d->updateBehavior;
|
||||
}
|
||||
|
||||
/*!
|
||||
\return \c true if the window's OpenGL resources, like the context, have
|
||||
been successfully initialized. Note that the return value is always \c false
|
||||
until the window becomes exposed (shown).
|
||||
*/
|
||||
bool QOpenGLWindow::isValid() const
|
||||
{
|
||||
Q_D(const QOpenGLWindow);
|
||||
return d->context && d->context->isValid();
|
||||
}
|
||||
|
||||
/*!
|
||||
Prepares for rendering OpenGL content for this window by making the
|
||||
corresponding context current and binding the framebuffer object, if there is
|
||||
one, in that context context.
|
||||
|
||||
It is not necessary to call this function in most cases, because it is called
|
||||
automatically before invoking paintGL(). It is provided nonetheless to support
|
||||
advanced, multi-threaded scenarios where a thread different than the GUI or main
|
||||
thread may want to update the surface or framebuffer contents. See QOpenGLContext
|
||||
for more information on threading related issues.
|
||||
|
||||
This function is suitable for calling also when the underlying platform window
|
||||
is already destroyed. This means that it is safe to call this function from
|
||||
a QOpenGLWindow subclass' destructor. If there is no native window anymore,
|
||||
an offscreen surface is used instead. This ensures that OpenGL resource
|
||||
cleanup operations in the destructor will always work, as long as
|
||||
this function is called first.
|
||||
|
||||
\sa QOpenGLContext, context(), paintGL(), doneCurrent()
|
||||
*/
|
||||
void QOpenGLWindow::makeCurrent()
|
||||
{
|
||||
Q_D(QOpenGLWindow);
|
||||
|
||||
if (!isValid())
|
||||
return;
|
||||
|
||||
// The platform window may be destroyed at this stage and therefore
|
||||
// makeCurrent() may not safely be called with 'this'.
|
||||
if (handle()) {
|
||||
d->context->makeCurrent(this);
|
||||
} else {
|
||||
if (!d->offscreenSurface) {
|
||||
d->offscreenSurface.reset(new QOffscreenSurface);
|
||||
d->offscreenSurface->setFormat(d->context->format());
|
||||
d->offscreenSurface->create();
|
||||
}
|
||||
d->context->makeCurrent(d->offscreenSurface.data());
|
||||
}
|
||||
|
||||
d->bindFBO();
|
||||
}
|
||||
|
||||
/*!
|
||||
Releases the context.
|
||||
|
||||
It is not necessary to call this function in most cases, since the widget
|
||||
will make sure the context is bound and released properly when invoking
|
||||
paintGL().
|
||||
|
||||
\sa makeCurrent()
|
||||
*/
|
||||
void QOpenGLWindow::doneCurrent()
|
||||
{
|
||||
Q_D(QOpenGLWindow);
|
||||
|
||||
if (!isValid())
|
||||
return;
|
||||
|
||||
d->context->doneCurrent();
|
||||
}
|
||||
|
||||
/*!
|
||||
\return The QOpenGLContext used by this window or \c 0 if not yet initialized.
|
||||
*/
|
||||
QOpenGLContext *QOpenGLWindow::context() const
|
||||
{
|
||||
Q_D(const QOpenGLWindow);
|
||||
return d->context.data();
|
||||
}
|
||||
|
||||
/*!
|
||||
The framebuffer object handle used by this window.
|
||||
|
||||
When the update behavior is set to \c NoPartialUpdate, there is no separate
|
||||
framebuffer object. In this case the returned value is the ID of the
|
||||
default framebuffer.
|
||||
|
||||
Otherwise the value of the ID of the framebuffer object or \c 0 if not
|
||||
yet initialized.
|
||||
*/
|
||||
GLuint QOpenGLWindow::defaultFramebufferObject() const
|
||||
{
|
||||
Q_D(const QOpenGLWindow);
|
||||
if (d->updateBehavior > NoPartialUpdate && d->fbo)
|
||||
return d->fbo->handle();
|
||||
else if (QOpenGLContext *ctx = QOpenGLContext::currentContext())
|
||||
return ctx->defaultFramebufferObject();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
|
||||
|
||||
/*!
|
||||
Returns a 32-bit RGB image of the framebuffer.
|
||||
|
||||
\note This is a potentially expensive operation because it relies on
|
||||
glReadPixels() to read back the pixels. This may be slow and can stall the
|
||||
GPU pipeline.
|
||||
|
||||
\note When used together with update behavior \c NoPartialUpdate, the returned
|
||||
image may not contain the desired content when called after the front and back
|
||||
buffers have been swapped (unless preserved swap is enabled in the underlying
|
||||
windowing system interface). In this mode the function reads from the back
|
||||
buffer and the contents of that may not match the content on the screen (the
|
||||
front buffer). In this case the only place where this function can safely be
|
||||
used is paintGL() or paintOverGL().
|
||||
*/
|
||||
QImage QOpenGLWindow::grabFramebuffer()
|
||||
{
|
||||
if (!isValid())
|
||||
return QImage();
|
||||
|
||||
makeCurrent();
|
||||
return qt_gl_read_framebuffer(size() * devicePixelRatio(), false, false);
|
||||
}
|
||||
|
||||
/*!
|
||||
This virtual function is called once before the first call to paintGL() or
|
||||
resizeGL(). Reimplement it in a subclass.
|
||||
|
||||
This function should set up any required OpenGL resources and state.
|
||||
|
||||
There is no need to call makeCurrent() because this has already been done
|
||||
when this function is called. Note however that the framebuffer, in case
|
||||
partial update mode is used, is not yet available at this stage, so avoid
|
||||
issuing draw calls from here. Defer such calls to paintGL() instead.
|
||||
|
||||
\sa paintGL(), resizeGL()
|
||||
*/
|
||||
void QOpenGLWindow::initializeGL()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
This virtual function is called whenever the widget has been resized.
|
||||
Reimplement it in a subclass. The new size is passed in \a w and \a h.
|
||||
|
||||
There is no need to call makeCurrent() because this has already been done
|
||||
when this function is called. Additionally, the framebuffer, if there is one,
|
||||
is bound too.
|
||||
|
||||
\sa initializeGL(), paintGL()
|
||||
*/
|
||||
void QOpenGLWindow::resizeGL(int w, int h)
|
||||
{
|
||||
Q_UNUSED(w);
|
||||
Q_UNUSED(h);
|
||||
}
|
||||
|
||||
/*!
|
||||
This virtual function is called whenever the window contents needs to be
|
||||
painted. Reimplement it in a subclass.
|
||||
|
||||
There is no need to call makeCurrent() because this has already
|
||||
been done when this function is called.
|
||||
|
||||
Before invoking this function, the context and the framebuffer, if there is
|
||||
one, are bound, and the viewport is set up by a call to glViewport(). No
|
||||
other state is set and no clearing or drawing is performed by the framework.
|
||||
|
||||
\note When using a partial update behavior, like \c PartialUpdateBlend, the
|
||||
output of the previous paintGL() call is preserved and, after the additional
|
||||
drawing perfomed in the current invocation of the function, the content is
|
||||
blitted or blended over the content drawn directly to the window in
|
||||
paintUnderGL().
|
||||
|
||||
\sa initializeGL(), resizeGL(), paintUnderGL(), paintOverGL(), UpdateBehavior
|
||||
*/
|
||||
void QOpenGLWindow::paintGL()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
The virtual function is called before each invocation of paintGL().
|
||||
|
||||
When the update mode is set to \c NoPartialUpdate, there is no difference
|
||||
between this function and paintGL(), performing rendering in either of them
|
||||
leads to the same result.
|
||||
|
||||
The difference becomes significant when using \c PartialUpdateBlend, where an
|
||||
extra framebuffer object is used. There, paintGL() targets this additional
|
||||
framebuffer object, which preserves its contents, while paintUnderGL() and
|
||||
paintOverGL() target the default framebuffer, i.e. directly the window
|
||||
surface, the contents of which is lost after each displayed frame.
|
||||
|
||||
\note Avoid relying on this function when the update behavior is
|
||||
\c PartialUpdateBlit. This mode involves blitting the extra framebuffer used by
|
||||
paintGL() onto the default framebuffer after each invocation of paintGL(),
|
||||
thus overwriting all drawing generated in this function.
|
||||
|
||||
\sa paintGL(), paintOverGL(), UpdateBehavior
|
||||
*/
|
||||
void QOpenGLWindow::paintUnderGL()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
This virtual function is called after each invocation of paintGL().
|
||||
|
||||
When the update mode is set to NoPartialUpdate, there is no difference
|
||||
between this function and paintGL(), performing rendering in either of them
|
||||
leads to the same result.
|
||||
|
||||
Like paintUnderGL(), rendering in this function targets the default
|
||||
framebuffer of the window, regardless of the update behavior. It gets called
|
||||
after paintGL() has returned and the blit (PartialUpdateBlit) or quad drawing
|
||||
(PartialUpdateBlend) has been done.
|
||||
|
||||
\sa paintGL(), paintUnderGL(), UpdateBehavior
|
||||
*/
|
||||
void QOpenGLWindow::paintOverGL()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Paint \a event handler. Calls paintGL().
|
||||
|
||||
\sa paintGL()
|
||||
*/
|
||||
void QOpenGLWindow::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
paintGL();
|
||||
}
|
||||
|
||||
/*!
|
||||
Resize \a event handler. Calls resizeGL().
|
||||
|
||||
\sa resizeGL()
|
||||
*/
|
||||
void QOpenGLWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
resizeGL(width(), height());
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
int QOpenGLWindow::metric(PaintDeviceMetric metric) const
|
||||
{
|
||||
Q_D(const QOpenGLWindow);
|
||||
|
||||
switch (metric) {
|
||||
case PdmDepth:
|
||||
if (d->paintDevice)
|
||||
return d->paintDevice->depth();
|
||||
break;
|
||||
case PdmDevicePixelRatio:
|
||||
if (d->paintDevice)
|
||||
return d->paintDevice->devicePixelRatio();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QPaintDeviceWindow::metric(metric);
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QPaintDevice *QOpenGLWindow::redirected(QPoint *) const
|
||||
{
|
||||
Q_D(const QOpenGLWindow);
|
||||
if (QOpenGLContext::currentContext() == d->context.data())
|
||||
return d->paintDevice.data();
|
||||
return 0;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
100
src/gui/kernel/qopenglwindow.h
Normal file
100
src/gui/kernel/qopenglwindow.h
Normal file
@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QOPENGLWINDOW_H
|
||||
#define QOPENGLWINDOW_H
|
||||
|
||||
#include <QtGui/QPaintDeviceWindow>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QOpenGLWindowPrivate;
|
||||
|
||||
class Q_GUI_EXPORT QOpenGLWindow : public QPaintDeviceWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QOpenGLWindow)
|
||||
|
||||
public:
|
||||
enum UpdateBehavior {
|
||||
NoPartialUpdate,
|
||||
PartialUpdateBlit,
|
||||
PartialUpdateBlend
|
||||
};
|
||||
|
||||
explicit QOpenGLWindow(UpdateBehavior updateBehavior = NoPartialUpdate, QWindow *parent = 0);
|
||||
|
||||
UpdateBehavior updateBehavior() const;
|
||||
bool isValid() const;
|
||||
|
||||
void makeCurrent();
|
||||
void doneCurrent();
|
||||
|
||||
QOpenGLContext *context() const;
|
||||
|
||||
GLuint defaultFramebufferObject() const;
|
||||
|
||||
QImage grabFramebuffer();
|
||||
|
||||
Q_SIGNALS:
|
||||
void frameSwapped();
|
||||
|
||||
protected:
|
||||
virtual void initializeGL();
|
||||
virtual void resizeGL(int w, int h);
|
||||
virtual void paintGL();
|
||||
virtual void paintUnderGL();
|
||||
virtual void paintOverGL();
|
||||
|
||||
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
|
||||
void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
|
||||
int metric(PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
|
||||
QPaintDevice *redirected(QPoint *) const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QOpenGLWindow)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
214
src/gui/kernel/qpaintdevicewindow.cpp
Normal file
214
src/gui/kernel/qpaintdevicewindow.cpp
Normal file
@ -0,0 +1,214 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qpaintdevicewindow_p.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QScreen>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QPaintDeviceWindow
|
||||
\inmodule QtGui
|
||||
\since 5.4
|
||||
\brief Convenience subclass of QWindow that is also a QPaintDevice.
|
||||
|
||||
QPaintDeviceWindow is like a regular QWindow, with the added functionality
|
||||
of being a paint device too. Whenever the content needs to be updated,
|
||||
the virtual paintEvent() function is called. Subclasses, that reimplement
|
||||
this function, can then simply open a QPainter on the window.
|
||||
|
||||
\note This class cannot directly be used in applications. It rather serves
|
||||
as a base for subclasses like QOpenGLWindow.
|
||||
|
||||
\sa QOpenGLWindow
|
||||
*/
|
||||
|
||||
/*!
|
||||
Marks the entire window as dirty and schedules a repaint.
|
||||
|
||||
\note Subsequent calls to this function before the next paint
|
||||
event will get ignored.
|
||||
*/
|
||||
void QPaintDeviceWindow::update()
|
||||
{
|
||||
update(QRect(QPoint(0,0), size()));
|
||||
}
|
||||
|
||||
/*!
|
||||
Marks the \a rect of the window as dirty and schedules a repaint.
|
||||
|
||||
\note Subsequent calls to this function before the next paint
|
||||
event will get ignored.
|
||||
*/
|
||||
void QPaintDeviceWindow::update(const QRect &rect)
|
||||
{
|
||||
Q_D(QPaintDeviceWindow);
|
||||
d->dirtyRegion += rect;
|
||||
d->triggerUpdate();
|
||||
}
|
||||
|
||||
/*!
|
||||
Marks the \a region of the window as dirty and schedules a repaint.
|
||||
|
||||
\note Subsequent calls to this function before the next paint
|
||||
event will get ignored.
|
||||
*/
|
||||
void QPaintDeviceWindow::update(const QRegion ®ion)
|
||||
{
|
||||
Q_D(QPaintDeviceWindow);
|
||||
d->dirtyRegion += region;
|
||||
d->triggerUpdate();
|
||||
}
|
||||
|
||||
/*!
|
||||
Handles paint events passed in the \a event parameter.
|
||||
|
||||
The default implementation does nothing. Reimplement this function to
|
||||
perform painting. If necessary, the dirty area is retrievable from
|
||||
the \a event.
|
||||
*/
|
||||
void QPaintDeviceWindow::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
int QPaintDeviceWindow::metric(PaintDeviceMetric metric) const
|
||||
{
|
||||
QScreen *screen = this->screen();
|
||||
if (!screen && QGuiApplication::primaryScreen())
|
||||
screen = QGuiApplication::primaryScreen();
|
||||
|
||||
switch (metric) {
|
||||
case PdmWidth:
|
||||
return width();
|
||||
case PdmWidthMM:
|
||||
if (screen)
|
||||
return width() * screen->physicalSize().width() / screen->geometry().width();
|
||||
break;
|
||||
case PdmHeight:
|
||||
return height();
|
||||
case PdmHeightMM:
|
||||
if (screen)
|
||||
return height() * screen->physicalSize().height() / screen->geometry().height();
|
||||
break;
|
||||
case PdmDpiX:
|
||||
if (screen)
|
||||
return qRound(screen->logicalDotsPerInchX());
|
||||
break;
|
||||
case PdmDpiY:
|
||||
if (screen)
|
||||
return qRound(screen->logicalDotsPerInchY());
|
||||
break;
|
||||
case PdmPhysicalDpiX:
|
||||
if (screen)
|
||||
return qRound(screen->physicalDotsPerInchX());
|
||||
break;
|
||||
case PdmPhysicalDpiY:
|
||||
if (screen)
|
||||
return qRound(screen->physicalDotsPerInchY());
|
||||
break;
|
||||
case PdmDevicePixelRatio:
|
||||
if (screen)
|
||||
return screen->devicePixelRatio();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QPaintDevice::metric(metric);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QPaintDeviceWindow::exposeEvent(QExposeEvent *exposeEvent)
|
||||
{
|
||||
Q_UNUSED(exposeEvent);
|
||||
Q_D(QPaintDeviceWindow);
|
||||
if (isExposed()) {
|
||||
d->markWindowAsDirty();
|
||||
// Do not rely on exposeEvent->region() as it has some issues for the
|
||||
// time being, namely that it is sometimes in local coordinates,
|
||||
// sometimes relative to the parent, depending on the platform plugin.
|
||||
// We require local coords here.
|
||||
d->doFlush(QRect(QPoint(0, 0), size()));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QPaintDeviceWindow::event(QEvent *event)
|
||||
{
|
||||
Q_D(QPaintDeviceWindow);
|
||||
|
||||
if (event->type() == QEvent::UpdateRequest) {
|
||||
d->paintEventSent = false;
|
||||
d->handleUpdateEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QWindow::event(event);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QPaintDeviceWindow::QPaintDeviceWindow(QPaintDeviceWindowPrivate &dd, QWindow *parent)
|
||||
: QWindow(dd, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QPaintEngine *QPaintDeviceWindow::paintEngine() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
85
src/gui/kernel/qpaintdevicewindow.h
Normal file
85
src/gui/kernel/qpaintdevicewindow.h
Normal file
@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QPAINTDEVICEWINDOW_H
|
||||
#define QPAINTDEVICEWINDOW_H
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QPaintDevice>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QPaintDeviceWindowPrivate;
|
||||
class QPaintEvent;
|
||||
|
||||
class Q_GUI_EXPORT QPaintDeviceWindow : public QWindow, public QPaintDevice
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QPaintDeviceWindow)
|
||||
|
||||
public:
|
||||
void update(const QRect &rect);
|
||||
void update(const QRegion ®ion);
|
||||
|
||||
using QWindow::width;
|
||||
using QWindow::height;
|
||||
using QWindow::devicePixelRatio;
|
||||
|
||||
public Q_SLOTS:
|
||||
void update();
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event);
|
||||
|
||||
int metric(PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
|
||||
void exposeEvent(QExposeEvent *) Q_DECL_OVERRIDE;
|
||||
bool event(QEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
QPaintDeviceWindow(QPaintDeviceWindowPrivate &dd, QWindow *parent);
|
||||
|
||||
private:
|
||||
QPaintEngine *paintEngine() const Q_DECL_OVERRIDE;
|
||||
Q_DISABLE_COPY(QPaintDeviceWindow)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
130
src/gui/kernel/qpaintdevicewindow_p.h
Normal file
130
src/gui/kernel/qpaintdevicewindow_p.h
Normal file
@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QPAINTDEVICEWINDOW_P_H
|
||||
#define QPAINTDEVICEWINDOW_P_H
|
||||
|
||||
#include <QtGui/QPaintDeviceWindow>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtGui/private/qwindow_p.h>
|
||||
#include <QtGui/QPaintEvent>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_GUI_EXPORT QPaintDeviceWindowPrivate : public QWindowPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QPaintDeviceWindow)
|
||||
|
||||
public:
|
||||
QPaintDeviceWindowPrivate() : paintEventSent(false) { }
|
||||
|
||||
virtual void beginPaint(const QRegion ®ion)
|
||||
{
|
||||
Q_UNUSED(region);
|
||||
}
|
||||
|
||||
virtual void endPaint()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void flush(const QRegion ®ion)
|
||||
{
|
||||
Q_UNUSED(region);
|
||||
}
|
||||
|
||||
bool paint(const QRegion ®ion)
|
||||
{
|
||||
Q_Q(QPaintDeviceWindow);
|
||||
QRegion toPaint = region & dirtyRegion;
|
||||
if (toPaint.isEmpty())
|
||||
return false;
|
||||
|
||||
// Clear the region now. The overridden functions may call update().
|
||||
dirtyRegion -= toPaint;
|
||||
|
||||
beginPaint(toPaint);
|
||||
|
||||
QPaintEvent paintEvent(toPaint);
|
||||
q->paintEvent(&paintEvent);
|
||||
|
||||
endPaint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void triggerUpdate()
|
||||
{
|
||||
Q_Q(QPaintDeviceWindow);
|
||||
if (!paintEventSent) {
|
||||
QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
|
||||
paintEventSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
void doFlush(const QRegion ®ion)
|
||||
{
|
||||
QRegion toFlush = region;
|
||||
if (paint(toFlush))
|
||||
flush(toFlush);
|
||||
}
|
||||
|
||||
void handleUpdateEvent()
|
||||
{
|
||||
if (dirtyRegion.isEmpty())
|
||||
return;
|
||||
doFlush(dirtyRegion);
|
||||
}
|
||||
|
||||
void markWindowAsDirty()
|
||||
{
|
||||
Q_Q(QPaintDeviceWindow);
|
||||
dirtyRegion += QRect(QPoint(0, 0), q->size());
|
||||
}
|
||||
|
||||
private:
|
||||
QRegion dirtyRegion;
|
||||
bool paintEventSent;
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif //QPAINTDEVICEWINDOW_P_H
|
@ -21,6 +21,7 @@ SUBDIRS=\
|
||||
qwindow \
|
||||
qguiapplication \
|
||||
qpixelformat \
|
||||
qopenglwindow
|
||||
|
||||
!qtHaveModule(widgets): SUBDIRS -= \
|
||||
qmouseevent_modal \
|
||||
@ -28,3 +29,5 @@ SUBDIRS=\
|
||||
|
||||
!qtHaveModule(network): SUBDIRS -= \
|
||||
qguieventloop
|
||||
|
||||
!contains(QT_CONFIG, opengl(es2)?): SUBDIRS -= qopenglwindow
|
||||
|
8
tests/auto/gui/kernel/qopenglwindow/qopenglwindow.pro
Normal file
8
tests/auto/gui/kernel/qopenglwindow/qopenglwindow.pro
Normal file
@ -0,0 +1,8 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qopenglwindow
|
||||
|
||||
QT += core-private gui-private testlib
|
||||
|
||||
SOURCES += tst_qopenglwindow.cpp
|
||||
|
||||
win32:CONFIG+=insignificant_test # QTBUG-28264
|
300
tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
Normal file
300
tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtGui/QOpenGLWindow>
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtGui/QOpenGLFunctions>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
class tst_QOpenGLWindow : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void create();
|
||||
void basic();
|
||||
void painter();
|
||||
void partial_data();
|
||||
void partial();
|
||||
void underOver();
|
||||
};
|
||||
|
||||
void tst_QOpenGLWindow::create()
|
||||
{
|
||||
QOpenGLWindow w;
|
||||
QVERIFY(!w.isValid());
|
||||
|
||||
w.resize(640, 480);
|
||||
w.show();
|
||||
|
||||
QTest::qWaitForWindowExposed(&w);
|
||||
|
||||
QVERIFY(w.isValid());
|
||||
}
|
||||
|
||||
class Window : public QOpenGLWindow
|
||||
{
|
||||
public:
|
||||
void reset() {
|
||||
initCount = resizeCount = paintCount = 0;
|
||||
}
|
||||
|
||||
void initializeGL() Q_DECL_OVERRIDE {
|
||||
++initCount;
|
||||
}
|
||||
|
||||
void resizeGL(int w, int h) Q_DECL_OVERRIDE {
|
||||
++resizeCount;
|
||||
QCOMPARE(w, size().width());
|
||||
QCOMPARE(h, size().height());
|
||||
}
|
||||
|
||||
void paintGL() Q_DECL_OVERRIDE {
|
||||
++paintCount;
|
||||
|
||||
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
||||
QVERIFY(ctx);
|
||||
QCOMPARE(ctx, context());
|
||||
QOpenGLFunctions *f = ctx->functions();
|
||||
QVERIFY(f);
|
||||
|
||||
f->glClearColor(1, 0, 0, 1);
|
||||
f->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
img = grabFramebuffer();
|
||||
}
|
||||
|
||||
int initCount;
|
||||
int resizeCount;
|
||||
int paintCount;
|
||||
QImage img;
|
||||
};
|
||||
|
||||
void tst_QOpenGLWindow::basic()
|
||||
{
|
||||
Window w;
|
||||
w.reset();
|
||||
w.resize(640, 480);
|
||||
w.show();
|
||||
QTest::qWaitForWindowExposed(&w);
|
||||
|
||||
// Check that the virtuals are invoked.
|
||||
QCOMPARE(w.initCount, 1);
|
||||
int resCount = w.resizeCount;
|
||||
QVERIFY(resCount >= 1);
|
||||
QVERIFY(w.paintCount >= 1);
|
||||
|
||||
// Check that something has been drawn;
|
||||
QCOMPARE(w.img.size(), w.size());
|
||||
QVERIFY(w.img.pixel(5, 5) == qRgb(255, 0, 0));
|
||||
|
||||
// Check that the viewport was properly set.
|
||||
w.makeCurrent();
|
||||
GLint v[4] = { 0, 0, 0, 0 };
|
||||
w.context()->functions()->glGetIntegerv(GL_VIEWPORT, v);
|
||||
QCOMPARE(v[0], 0);
|
||||
QCOMPARE(v[1], 0);
|
||||
QCOMPARE(v[2], GLint(w.width() * w.devicePixelRatio()));
|
||||
QCOMPARE(v[3], GLint(w.height() * w.devicePixelRatio()));
|
||||
w.doneCurrent();
|
||||
|
||||
// Check that a future resize triggers resizeGL.
|
||||
w.resize(800, 600);
|
||||
int maxWait = 1000;
|
||||
while (w.resizeCount == resCount && maxWait-- >= 0)
|
||||
QTest::qWait(10);
|
||||
QVERIFY(w.resizeCount > resCount);
|
||||
}
|
||||
|
||||
class PainterWindow : public QOpenGLWindow
|
||||
{
|
||||
public:
|
||||
void paintGL() Q_DECL_OVERRIDE {
|
||||
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
||||
QVERIFY(ctx);
|
||||
QCOMPARE(ctx, context());
|
||||
QOpenGLFunctions *f = ctx->functions();
|
||||
QVERIFY(f);
|
||||
|
||||
QPainter p(this);
|
||||
p.beginNativePainting();
|
||||
f->glClearColor(1, 0, 0, 1);
|
||||
f->glClear(GL_COLOR_BUFFER_BIT);
|
||||
p.endNativePainting();
|
||||
p.fillRect(QRect(0, 0, 100, 100), Qt::blue);
|
||||
p.end();
|
||||
|
||||
img = grabFramebuffer();
|
||||
}
|
||||
|
||||
QImage img;
|
||||
};
|
||||
|
||||
void tst_QOpenGLWindow::painter()
|
||||
{
|
||||
PainterWindow w;
|
||||
w.resize(400, 400);
|
||||
w.show();
|
||||
QTest::qWaitForWindowExposed(&w);
|
||||
|
||||
QCOMPARE(w.img.size(), w.size());
|
||||
QVERIFY(w.img.pixel(5, 5) == qRgb(0, 0, 255));
|
||||
QVERIFY(w.img.pixel(200, 5) == qRgb(255, 0, 0));
|
||||
}
|
||||
|
||||
class PartialPainterWindow : public QOpenGLWindow
|
||||
{
|
||||
public:
|
||||
PartialPainterWindow(QOpenGLWindow::UpdateBehavior u)
|
||||
: QOpenGLWindow(u), x(0) { }
|
||||
|
||||
void paintGL() Q_DECL_OVERRIDE {
|
||||
++paintCount;
|
||||
|
||||
QPainter p(this);
|
||||
if (!x)
|
||||
p.fillRect(QRect(0, 0, width(), height()), Qt::green);
|
||||
|
||||
p.fillRect(QRect(x, 0, 10, 10), Qt::blue);
|
||||
x += 20;
|
||||
}
|
||||
|
||||
int paintCount;
|
||||
int x;
|
||||
};
|
||||
|
||||
void tst_QOpenGLWindow::partial_data()
|
||||
{
|
||||
QTest::addColumn<int>("behavior");
|
||||
QTest::newRow("blit") << int(QOpenGLWindow::PartialUpdateBlit);
|
||||
QTest::newRow("blend") << int(QOpenGLWindow::PartialUpdateBlend);
|
||||
}
|
||||
|
||||
void tst_QOpenGLWindow::partial()
|
||||
{
|
||||
QFETCH(int, behavior);
|
||||
QOpenGLWindow::UpdateBehavior u = QOpenGLWindow::UpdateBehavior(behavior);
|
||||
PartialPainterWindow w(u);
|
||||
w.resize(800, 400);
|
||||
w.show();
|
||||
QTest::qWaitForWindowExposed(&w);
|
||||
|
||||
// Add a couple of small blue rects.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
w.paintCount = 0;
|
||||
w.update();
|
||||
int maxWait = 1000;
|
||||
while (w.paintCount == 0 && maxWait-- >= 0)
|
||||
QTest::qWait(10);
|
||||
}
|
||||
|
||||
// Now since the painting went to an extra framebuffer, all the rects should
|
||||
// be present since everything is preserved between the frames.
|
||||
QImage img = w.grabFramebuffer();
|
||||
QCOMPARE(img.size(), w.size());
|
||||
QCOMPARE(img.pixel(5, 5), qRgb(0, 0, 255));
|
||||
QCOMPARE(img.pixel(15, 5), qRgb(0, 255, 0));
|
||||
QCOMPARE(img.pixel(25, 5), qRgb(0, 0, 255));
|
||||
}
|
||||
|
||||
class PaintUnderOverWindow : public QOpenGLWindow
|
||||
{
|
||||
public:
|
||||
PaintUnderOverWindow() : QOpenGLWindow(PartialUpdateBlend), m_state(None) { }
|
||||
enum State {
|
||||
None,
|
||||
PaintUnder,
|
||||
Paint,
|
||||
PaintOver,
|
||||
Error
|
||||
} m_state;
|
||||
|
||||
void paintUnderGL() Q_DECL_OVERRIDE {
|
||||
if (m_state == None || m_state == PaintOver)
|
||||
m_state = PaintUnder;
|
||||
else
|
||||
m_state = Error;
|
||||
|
||||
GLuint fbo = 0xFFFF;
|
||||
QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &fbo);
|
||||
QVERIFY(fbo == 0);
|
||||
}
|
||||
|
||||
void paintGL() Q_DECL_OVERRIDE {
|
||||
if (m_state == PaintUnder)
|
||||
m_state = Paint;
|
||||
else
|
||||
m_state = Error;
|
||||
|
||||
// Using PartialUpdateBlend so paintGL() targets a user fbo, not the default.
|
||||
GLuint fbo = 0xFFFF;
|
||||
QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &fbo);
|
||||
QVERIFY(fbo != 0);
|
||||
QCOMPARE(fbo, defaultFramebufferObject());
|
||||
}
|
||||
|
||||
void paintOverGL() Q_DECL_OVERRIDE {
|
||||
if (m_state == Paint)
|
||||
m_state = PaintOver;
|
||||
else
|
||||
m_state = Error;
|
||||
|
||||
GLuint fbo = 0xFFFF;
|
||||
QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &fbo);
|
||||
QVERIFY(fbo == 0);
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QOpenGLWindow::underOver()
|
||||
{
|
||||
PaintUnderOverWindow w;
|
||||
w.resize(400, 400);
|
||||
w.show();
|
||||
QTest::qWaitForWindowExposed(&w);
|
||||
|
||||
// under -> paint -> over -> under -> paint -> ... is the only acceptable sequence
|
||||
QCOMPARE(w.m_state, PaintUnderOverWindow::PaintOver);
|
||||
}
|
||||
|
||||
#include <tst_qopenglwindow.moc>
|
||||
|
||||
QTEST_MAIN(tst_QOpenGLWindow)
|
Loading…
x
Reference in New Issue
Block a user