From 745a71196c8893a35a80a679674bee396606f79b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs <laszlo.agocs@digia.com> Date: Mon, 31 Mar 2014 15:59:18 +0200 Subject: [PATCH] Make QQuickRenderControl public QQuickRenderControl allows rendering Qt Quick 2 scenes into framebuffer objects which can then be used in arbitrary ways in Qt-based or 3rd party OpenGL renderers. [ChangeLog][QtQuick] Introduced QQuickRenderControl as a public API. Task-number: QTBUG-37944 Change-Id: I84262243b261b35cefdf67ec6bba8127a0f29275 Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com> --- examples/quick/quick.pro | 3 +- examples/quick/rendercontrol/demo.qml | 198 +++++++++ .../doc/images/rendercontrol-example.jpg | Bin 0 -> 44196 bytes .../rendercontrol/doc/src/rendercontrol.qdoc | 33 ++ examples/quick/rendercontrol/main.cpp | 51 +++ .../quick/rendercontrol/rendercontrol.pro | 11 + .../quick/rendercontrol/rendercontrol.qrc | 5 + examples/quick/rendercontrol/window.cpp | 403 ++++++++++++++++++ examples/quick/rendercontrol/window.h | 100 +++++ .../src/concepts/visualcanvas/scenegraph.qdoc | 20 +- src/quick/items/items.pri | 9 +- src/quick/items/qquickrendercontrol.cpp | 239 +++++++---- src/quick/items/qquickrendercontrol.h | 84 ++++ src/quick/items/qquickrendercontrol_p.h | 68 ++- src/quick/items/qquickwindow.cpp | 18 +- src/quick/items/qquickwindow.h | 2 +- src/quickwidgets/qquickwidget.cpp | 27 +- 17 files changed, 1116 insertions(+), 155 deletions(-) create mode 100644 examples/quick/rendercontrol/demo.qml create mode 100644 examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg create mode 100644 examples/quick/rendercontrol/doc/src/rendercontrol.qdoc create mode 100644 examples/quick/rendercontrol/main.cpp create mode 100644 examples/quick/rendercontrol/rendercontrol.pro create mode 100644 examples/quick/rendercontrol/rendercontrol.qrc create mode 100644 examples/quick/rendercontrol/window.cpp create mode 100644 examples/quick/rendercontrol/window.h create mode 100644 src/quick/items/qquickrendercontrol.h diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro index 421f95a162..a412c53a65 100644 --- a/examples/quick/quick.pro +++ b/examples/quick/quick.pro @@ -22,7 +22,8 @@ SUBDIRS = quick-accessibility \ imageprovider \ window \ particles \ - demos + demos \ + rendercontrol # Widget dependent examples qtHaveModule(widgets) { diff --git a/examples/quick/rendercontrol/demo.qml b/examples/quick/rendercontrol/demo.qml new file mode 100644 index 0000000000..e7ede91540 --- /dev/null +++ b/examples/quick/rendercontrol/demo.qml @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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: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$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle { + id: root + color: "green" + + Rectangle { + width: 400 + height: 400 + anchors.centerIn: parent + color: "red" + NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } + } + + Text { + anchors.centerIn: parent + text: "Qt Quick in a texture" + font.pointSize: 40 + color: "cyan" + } + + ParticleSystem { + id: particles + anchors.fill: parent + + ImageParticle { + id: smoke + system: particles + anchors.fill: parent + groups: ["A", "B"] + source: "qrc:///particleresources/glowdot.png" + colorVariation: 0 + color: "#00111111" + } + ImageParticle { + id: flame + anchors.fill: parent + system: particles + groups: ["C", "D"] + source: "qrc:///particleresources/glowdot.png" + colorVariation: 0.1 + color: "#00ff400f" + } + + Emitter { + id: fire + system: particles + group: "C" + + y: parent.height + width: parent.width + + emitRate: 350 + lifeSpan: 3500 + + acceleration: PointDirection { y: -17; xVariation: 3 } + velocity: PointDirection {xVariation: 3} + + size: 24 + sizeVariation: 8 + endSize: 4 + } + + TrailEmitter { + id: fireSmoke + group: "B" + system: particles + follow: "C" + width: root.width + height: root.height - 68 + + emitRatePerParticle: 1 + lifeSpan: 2000 + + velocity: PointDirection {y:-17*6; yVariation: -17; xVariation: 3} + acceleration: PointDirection {xVariation: 3} + + size: 36 + sizeVariation: 8 + endSize: 16 + } + + TrailEmitter { + id: fireballFlame + anchors.fill: parent + system: particles + group: "D" + follow: "E" + + emitRatePerParticle: 120 + lifeSpan: 180 + emitWidth: TrailEmitter.ParticleSize + emitHeight: TrailEmitter.ParticleSize + emitShape: EllipseShape{} + + size: 16 + sizeVariation: 4 + endSize: 4 + } + + TrailEmitter { + id: fireballSmoke + anchors.fill: parent + system: particles + group: "A" + follow: "E" + + emitRatePerParticle: 128 + lifeSpan: 2400 + emitWidth: TrailEmitter.ParticleSize + emitHeight: TrailEmitter.ParticleSize + emitShape: EllipseShape{} + + velocity: PointDirection {yVariation: 16; xVariation: 16} + acceleration: PointDirection {y: -16} + + size: 24 + sizeVariation: 8 + endSize: 8 + } + + Emitter { + id: balls + system: particles + group: "E" + + y: parent.height + width: parent.width + + emitRate: 2 + lifeSpan: 7000 + + velocity: PointDirection {y:-17*4*2; xVariation: 6*6} + acceleration: PointDirection {y: 17*2; xVariation: 6*6} + + size: 8 + sizeVariation: 4 + } + + Turbulence { //A bit of turbulence makes the smoke look better + anchors.fill: parent + groups: ["A","B"] + strength: 32 + system: particles + } + } + + onWidthChanged: particles.reset() + onHeightChanged: particles.reset() + + MouseArea { + anchors.fill: parent + onPressed: root.color = "gray" + onReleased: root.color = "green" + } +} diff --git a/examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg b/examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a899ebe7f5cdd38ce23e54cfa0a1ed3d960e68ce GIT binary patch literal 44196 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 literal 0 HcmV?d00001 diff --git a/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc b/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc new file mode 100644 index 0000000000..f8a9849a8a --- /dev/null +++ b/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** 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:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \title QQuickRenderControl Example + \example rendercontrol + \brief Shows how to render a Qt Quick scene into a texture that is then used by a non-Quick based OpenGL renderer + \image rendercontrol-example.jpg +*/ diff --git a/examples/quick/rendercontrol/main.cpp b/examples/quick/rendercontrol/main.cpp new file mode 100644 index 0000000000..d362278ddf --- /dev/null +++ b/examples/quick/rendercontrol/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** 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 <QGuiApplication> +#include "window.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + Window window; + window.resize(1024, 768); + window.show(); + return app.exec(); +} diff --git a/examples/quick/rendercontrol/rendercontrol.pro b/examples/quick/rendercontrol/rendercontrol.pro new file mode 100644 index 0000000000..ed25a11c1b --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol.pro @@ -0,0 +1,11 @@ +TEMPLATE = app + +QT += quick qml + +SOURCES += main.cpp window.cpp +HEADERS += window.h + +RESOURCES += rendercontrol.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/rendercontrol +INSTALLS += target diff --git a/examples/quick/rendercontrol/rendercontrol.qrc b/examples/quick/rendercontrol/rendercontrol.qrc new file mode 100644 index 0000000000..2246eeb842 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/rendercontrol"> + <file>demo.qml</file> + </qresource> +</RCC> diff --git a/examples/quick/rendercontrol/window.cpp b/examples/quick/rendercontrol/window.cpp new file mode 100644 index 0000000000..396e9f8afa --- /dev/null +++ b/examples/quick/rendercontrol/window.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** 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 "window.h" +#include <QOpenGLContext> +#include <QOpenGLFunctions> +#include <QOpenGLFramebufferObject> +#include <QOpenGLShaderProgram> +#include <QOpenGLVertexArrayObject> +#include <QOpenGLBuffer> +#include <QOpenGLVertexArrayObject> +#include <QOffscreenSurface> +#include <QQmlEngine> +#include <QQmlComponent> +#include <QQuickItem> +#include <QQuickWindow> +#include <QQuickRenderControl> +#include <QCoreApplication> + +Window::Window() + : m_rootItem(0), + m_fbo(0), + m_program(0), + m_vbo(0), + m_quickInitialized(false), + m_quickReady(false) +{ + setSurfaceType(QSurface::OpenGLSurface); + + QSurfaceFormat format; + // Qt Quick may need a depth and stencil buffer. Always make sure these are available. + format.setDepthBufferSize(16); + format.setStencilBufferSize(8); + setFormat(format); + + m_context = new QOpenGLContext; + m_context->setFormat(format); + m_context->create(); + + m_offscreenSurface = new QOffscreenSurface; + // Pass m_context->format(), not format. Format does not specify and color buffer + // sizes, while the context, that has just been created, reports a format that has + // these values filled in. Pass this to the offscreen surface to make sure it will be + // compatible with the context's configuration. + m_offscreenSurface->setFormat(m_context->format()); + m_offscreenSurface->create(); + + m_renderControl = new QQuickRenderControl(this); + + // Create a QQuickWindow that is associated with out render control. Note that this + // window never gets created or shown, meaning that it will never get an underlying + // native (platform) window. + m_quickWindow = new QQuickWindow(m_renderControl); + + // Create a QML engine. + m_qmlEngine = new QQmlEngine; + if (!m_qmlEngine->incubationController()) + m_qmlEngine->setIncubationController(m_quickWindow->incubationController()); + + // When Quick says there is a need to render, we will not render immediately. Instead, + // a timer with a small interval is used to get better performance. + m_updateTimer.setSingleShot(true); + m_updateTimer.setInterval(5); + connect(&m_updateTimer, &QTimer::timeout, this, &Window::updateQuick); + + // Now hook up the signals. For simplicy we don't differentiate between + // renderRequested (only render is needed, no sync) and sceneChanged (polish and sync + // is needed too). + connect(m_quickWindow, &QQuickWindow::sceneGraphInitialized, this, &Window::createFbo); + connect(m_quickWindow, &QQuickWindow::sceneGraphInvalidated, this, &Window::destroyFbo); + connect(m_renderControl, &QQuickRenderControl::renderRequested, this, &Window::requestUpdate); + connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, &Window::requestUpdate); +} + +Window::~Window() +{ + // Make sure the context is current while doing cleanup. + m_context->makeCurrent(this); + + // Delete the render control first since it will free the scenegraph resources. + // Destroy the QQuickWindow only afterwards. + delete m_renderControl; + + delete m_qmlComponent; + delete m_quickWindow; + delete m_qmlEngine; + delete m_fbo; + delete m_program; + delete m_vbo; + delete m_vao; + + m_context->doneCurrent(); + + delete m_offscreenSurface; + delete m_context; +} + +void Window::createFbo() +{ + // The scene graph has been initialized. It is now time to create an FBO and associate + // it with the QQuickWindow. + m_fbo = new QOpenGLFramebufferObject(size(), QOpenGLFramebufferObject::CombinedDepthStencil); + m_quickWindow->setRenderTarget(m_fbo); +} + +void Window::destroyFbo() +{ + delete m_fbo; + m_fbo = 0; +} + +void Window::requestUpdate() +{ + if (!m_updateTimer.isActive()) + m_updateTimer.start(); +} + +void Window::run() +{ + disconnect(m_qmlComponent, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(run())); + + if (m_qmlComponent->isError()) { + QList<QQmlError> errorList = m_qmlComponent->errors(); + foreach (const QQmlError &error, errorList) + qWarning() << error.url() << error.line() << error; + return; + } + + QObject *rootObject = m_qmlComponent->create(); + if (m_qmlComponent->isError()) { + QList<QQmlError> errorList = m_qmlComponent->errors(); + foreach (const QQmlError &error, errorList) + qWarning() << error.url() << error.line() << error; + return; + } + + m_rootItem = qobject_cast<QQuickItem *>(rootObject); + if (!m_rootItem) { + qWarning("run: Not a QQuickItem"); + delete rootObject; + return; + } + + // The root item is ready. Associate it with the window. + m_rootItem->setParentItem(m_quickWindow->contentItem()); + + // Update item and rendering related geometries. + updateSizes(); + + // Initialize the render control and our OpenGL resources. + m_context->makeCurrent(m_offscreenSurface); + m_renderControl->initialize(m_context); + + static const char *vertexShaderSource = + "attribute highp vec4 vertex;\n" + "attribute lowp vec2 coord;\n" + "varying lowp vec2 v_coord;\n" + "uniform highp mat4 matrix;\n" + "void main() {\n" + " v_coord = coord;\n" + " gl_Position = matrix * vertex;\n" + "}\n"; + static const char *fragmentShaderSource = + "varying lowp vec2 v_coord;\n" + "uniform sampler2D sampler;\n" + "void main() {\n" + " gl_FragColor = vec4(texture2D(sampler, v_coord).rgb, 1.0);\n" + "}\n"; + m_program = new QOpenGLShaderProgram; + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); + m_program->bindAttributeLocation("vertex", 0); + m_program->bindAttributeLocation("coord", 1); + m_program->link(); + m_matrixLoc = m_program->uniformLocation("matrix"); + + m_vao = new QOpenGLVertexArrayObject; + m_vao->create(); + m_vao->bind(); + + m_vbo = new QOpenGLBuffer; + m_vbo->create(); + m_vbo->bind(); + + GLfloat v[] = { + -0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5, + 0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5, + -0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5, + 0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5, + + 0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5, + 0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5, + -0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5, + -0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5, + + 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, + -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, + -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5 + }; + GLfloat texCoords[] = { + 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f, + 1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f, + 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, + 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f, + + 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, + 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f, + 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f, + 1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f, + + 0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f, + 1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f, + 1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f, + 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f + }; + + const int vertexCount = 36; + m_vbo->allocate(sizeof(GLfloat) * vertexCount * 5); + m_vbo->write(0, v, sizeof(GLfloat) * vertexCount * 3); + m_vbo->write(sizeof(GLfloat) * vertexCount * 3, texCoords, sizeof(GLfloat) * vertexCount * 2); + m_vbo->release(); + + if (m_vao->isCreated()) + setupVertexAttribs(); + + // Must unbind before changing the current context. Hence the absence of + // QOpenGLVertexArrayObject::Binder here. + m_vao->release(); + + m_context->doneCurrent(); + m_quickInitialized = true; +} + +void Window::updateSizes() +{ + // Behave like SizeRootObjectToView. + m_rootItem->setWidth(width()); + m_rootItem->setHeight(height()); + + m_quickWindow->setGeometry(0, 0, width(), height()); + + m_proj.setToIdentity(); + m_proj.perspective(45, width() / float(height()), 0.01f, 100.0f); +} + +void Window::setupVertexAttribs() +{ + m_vbo->bind(); + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_context->functions()->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + m_context->functions()->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, + (const void *)(36 * 3 * sizeof(GLfloat))); + m_vbo->release(); +} + +void Window::startQuick(const QString &filename) +{ + m_qmlComponent = new QQmlComponent(m_qmlEngine, QUrl(filename)); + if (m_qmlComponent->isLoading()) + connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &Window::run); + else + run(); +} + +void Window::exposeEvent(QExposeEvent *) +{ + if (isExposed()) { + render(); + if (!m_quickInitialized) + startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); + } +} + +void Window::resizeEvent(QResizeEvent *) +{ + // If this is a resize after the scene is up and running, recreate the fbo and the + // Quick item and scene. + if (m_rootItem && m_context->makeCurrent(m_offscreenSurface)) { + delete m_fbo; + createFbo(); + m_context->doneCurrent(); + updateSizes(); + } +} + +void Window::updateQuick() +{ + if (!m_context->makeCurrent(m_offscreenSurface)) + return; + + // Polish, synchronize and render the next frame (into our fbo). In this example + // everything happens on the same thread and therefore all three steps are performed + // in succession from here. In a threaded setup the render() call would happen on a + // separate thread. + m_renderControl->polishItems(); + m_renderControl->sync(); + m_renderControl->render(); + + m_quickWindow->resetOpenGLState(); + QOpenGLFramebufferObject::bindDefault(); + + m_quickReady = true; + + // Get something onto the screen. + render(); +} + +void Window::render() +{ + if (!m_context->makeCurrent(this)) + return; + + QOpenGLFunctions *f = m_context->functions(); + f->glClearColor(0.0f, 0.1f, 0.25f, 1.0f); + f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (m_quickReady) { + f->glFrontFace(GL_CW); // because our cube's vertex data is such + f->glEnable(GL_CULL_FACE); + f->glEnable(GL_DEPTH_TEST); + + f->glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + + m_program->bind(); + QOpenGLVertexArrayObject::Binder vaoBinder(m_vao); + // If VAOs are not supported, set the vertex attributes every time. + if (!m_vao->isCreated()) + setupVertexAttribs(); + + static GLfloat angle = 0; + QMatrix4x4 m; + m.translate(0, 0, -2); + m.rotate(90, 0, 0, 1); + m.rotate(angle, 0.5, 1, 0); + angle += 0.5f; + + m_program->setUniformValue(m_matrixLoc, m_proj * m); + + // Draw the cube. + f->glDrawArrays(GL_TRIANGLES, 0, 36); + + m_program->release(); + f->glDisable(GL_DEPTH_TEST); + f->glDisable(GL_CULL_FACE); + } + + m_context->swapBuffers(this); +} + +void Window::mousePressEvent(QMouseEvent *e) +{ + // Use the constructor taking localPos and screenPos. That puts localPos into the + // event's localPos and windowPos, and screenPos into the event's screenPos. This way + // the windowPos in e is ignored and is replaced by localPos. This is necessary + // because QQuickWindow thinks of itself as a top-level window always. + QMouseEvent mappedEvent(e->type(), e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(m_quickWindow, &mappedEvent); +} + +void Window::mouseReleaseEvent(QMouseEvent *e) +{ + QMouseEvent mappedEvent(e->type(), e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(m_quickWindow, &mappedEvent); +} diff --git a/examples/quick/rendercontrol/window.h b/examples/quick/rendercontrol/window.h new file mode 100644 index 0000000000..2723aeb011 --- /dev/null +++ b/examples/quick/rendercontrol/window.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the 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 <QWindow> +#include <QMatrix4x4> +#include <QTimer> + +QT_FORWARD_DECLARE_CLASS(QOpenGLContext) +QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject) +QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) +QT_FORWARD_DECLARE_CLASS(QOpenGLBuffer) +QT_FORWARD_DECLARE_CLASS(QOpenGLVertexArrayObject) +QT_FORWARD_DECLARE_CLASS(QOffscreenSurface) +QT_FORWARD_DECLARE_CLASS(QQuickRenderControl) +QT_FORWARD_DECLARE_CLASS(QQuickWindow) +QT_FORWARD_DECLARE_CLASS(QQmlEngine) +QT_FORWARD_DECLARE_CLASS(QQmlComponent) +QT_FORWARD_DECLARE_CLASS(QQuickItem) + +class Window : public QWindow +{ + Q_OBJECT + +public: + Window(); + ~Window(); + +protected: + void exposeEvent(QExposeEvent *e) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE; + +private slots: + void render(); + void updateQuick(); + void run(); + void createFbo(); + void destroyFbo(); + void requestUpdate(); + +private: + void startQuick(const QString &filename); + void setupVertexAttribs(); + void updateSizes(); + + QOpenGLContext *m_context; + QOffscreenSurface *m_offscreenSurface; + QQuickRenderControl *m_renderControl; + QQuickWindow *m_quickWindow; + QQmlEngine *m_qmlEngine; + QQmlComponent *m_qmlComponent; + QQuickItem *m_rootItem; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; + QOpenGLBuffer *m_vbo; + QOpenGLVertexArrayObject *m_vao; + bool m_quickInitialized; + bool m_quickReady; + int m_matrixLoc; + QMatrix4x4 m_proj; + QTimer m_updateTimer; +}; diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 7a54b7a021..1f8147c4d1 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -244,11 +244,11 @@ animations, process events, etc. \endlist -The threaded renderer is currently used by default on Linux, Mac OS X -and EGLFS based QPA platforms, but this is subject to change. It is -possible to force use of the threaded renderer by setting \c -{QML_FORCE_THREADED_RENDERER=1} in the environment. - +The threaded renderer is currently used by default on Linux with +non-Mesa based drivers, OS X and EGLFS based QPA platforms, but this +is subject to change. It is possible to force use of the threaded +renderer by setting \c {QSG_RENDER_LOOP=threaded} in the +environment. \section2 Non-threaded Render Loop @@ -267,6 +267,16 @@ sequence in the non-threaded renderer. \image sg-renderloop-singlethreaded.jpg +\section2 Custom control over rendering with QQuickRenderControl + +When using QQuickRenderControl, the responsibility for driving the +rendering loop is transferred to the application. In this case no +built-in render loop is used. Instead, it is up to the application to +invoke the polish, synchronize and rendering steps at the appropriate +time. It is possible to implement either a threaded or non-threaded +behavior similar to the ones shown above. + + \section2 Mixing Scene Graph and OpenGL The scene graph offers two methods for integrating OpenGL content: diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index add909d0cb..042ff80abc 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -10,7 +10,6 @@ HEADERS += \ $$PWD/qquickrectangle_p_p.h \ $$PWD/qquickwindow.h \ $$PWD/qquickwindow_p.h \ - $$PWD/qquickrendercontrol_p.h \ $$PWD/qquickfocusscope_p.h \ $$PWD/qquickitemsmodule_p.h \ $$PWD/qquickpainteditem.h \ @@ -76,7 +75,9 @@ HEADERS += \ $$PWD/qquickscreen_p.h \ $$PWD/qquickwindowmodule_p.h \ $$PWD/qquickframebufferobject.h \ - $$PWD/qquickitemgrabresult.h + $$PWD/qquickitemgrabresult.h \ + $$PWD/qquickrendercontrol.h \ + $$PWD/qquickrendercontrol_p.h SOURCES += \ $$PWD/qquickevents.cpp \ @@ -84,7 +85,6 @@ SOURCES += \ $$PWD/qquickitem.cpp \ $$PWD/qquickrectangle.cpp \ $$PWD/qquickwindow.cpp \ - $$PWD/qquickrendercontrol.cpp \ $$PWD/qquickfocusscope.cpp \ $$PWD/qquickitemsmodule.cpp \ $$PWD/qquickpainteditem.cpp \ @@ -130,7 +130,8 @@ SOURCES += \ $$PWD/qquickwindowmodule.cpp \ $$PWD/qquickscreen.cpp \ $$PWD/qquickframebufferobject.cpp \ - $$PWD/qquickitemgrabresult.cpp + $$PWD/qquickitemgrabresult.cpp \ + $$PWD/qquickrendercontrol.cpp SOURCES += \ $$PWD/qquickshadereffect.cpp \ diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index b8d572114a..311a1b95f8 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.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 QtQuick module of the Qt Toolkit. @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include "qquickrendercontrol.h" #include "qquickrendercontrol_p.h" #include <QtCore/QCoreApplication> @@ -53,7 +54,6 @@ #include <QtQuick/QQuickWindow> #include <QtQuick/private/qquickwindow_p.h> -#include <QtQuick/private/qsgcontext_p.h> #include <private/qqmlprofilerservice_p.h> #include <QtCore/private/qobject_p.h> @@ -61,59 +61,139 @@ QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); -class QQuickRenderControlPrivate : public QObjectPrivate -{ -public: - QQuickRenderControlPrivate() - : window(0) - { - sg = QSGContext::createDefaultContext(); - rc = new QSGRenderContext(sg); - } +/*! + \class QQuickRenderControl - ~QQuickRenderControlPrivate() - { - delete rc; - delete sg; - } + \brief The QQuickRenderControl class provides a mechanism for rendering the Qt + Quick scenegraph onto an offscreen render target in a fully + application-controlled manner. - QQuickWindow *window; - QSGContext *sg; - QSGRenderContext *rc; -}; + \since 5.4 -/*! - \class QQuickRenderControl - \brief The QQuickRenderControl class provides a mechanism for rendering the Qt Quick scenegraph. + QQuickWindow and QQuickView and their associated internal render loops render + the Qt Quick scene onto a native window. In some cases, for example when + integrating with 3rd party OpenGL renderers, it might be beneficial to get the + scene into a texture that can then be used in arbitrary ways by the external + rendering engine. QQuickRenderControl makes this possible in a hardware + accelerated manner, unlike the performance-wise limited alternative of using + QQuickWindow::grabWindow() + + When using a QQuickRenderControl, the QQuickWindow does not have to be shown + or even created at all. This means there will not be an underlying native + window for it. Instead, the QQuickWindow instance is associated with the + render control, using the overload of the QQuickWindow constructor, and an + OpenGL framebuffer object by calling QQuickWindow::setRenderTarget(). + + Management of the context and framebuffer object is up to the application. The + context that will be used by Qt Quick must be created before calling + initialize(). The creation of the framebuffer object can be deferred, see + below. Qt 5.4 introduces the ability for QOpenGLContext to adopt existing + native contexts. Together with QQuickRenderControl this makes it possible to + create a QOpenGLContext that shares with an external rendering engine's + existing context. This new QOpenGLContext can then be used to render the Qt + Quick scene into a texture that is accessible by the other engine's context + too. + + Loading and instantiation of the QML components happen by using a + QQmlEngine. Once the root object is created, it will need to be parented to + the QQuickWindow's contentItem(). + + Applications will usually have to connect to 4 important signals: - \internal + \list + + \li QQuickWindow::sceneGraphInitialized() Emitted at some point after calling + QQuickRenderControl::initialize(). Upon this signal, the application is + expected to create its framebuffer object and associate it with the + QQuickWindow. + + \li QQuickWindow::sceneGraphInvalidated() When the scenegraph resources are + released, the framebuffer object can be destroyed too. + + \li QQuickRenderControl::renderRequested() Indicates that the scene has to be + rendered by calling render(). After making the context current, applications + are expected to call render(). + + \li QQuickRenderControl::sceneChanged() Inidcates that the scene has changed + meaning that, before rendering, polishing and synchronizing is also necessary. + + \endlist + + To send events, for example mouse or keyboard events, to the scene, use + QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver. \inmodule QtQuick */ -QQuickRenderControl::QQuickRenderControl() - : QObject(*(new QQuickRenderControlPrivate), 0) +QSGContext *QQuickRenderControlPrivate::sg = 0; + +QQuickRenderControlPrivate::QQuickRenderControlPrivate() + : initialized(0), + window(0) { + if (!sg) { + qAddPostRoutine(cleanup); + sg = QSGContext::createDefaultContext(); + } + rc = new QSGRenderContext(sg); } -QQuickRenderControl::~QQuickRenderControl() +QQuickRenderControlPrivate::~QQuickRenderControlPrivate() { + delete rc; } -void QQuickRenderControl::windowDestroyed() +void QQuickRenderControlPrivate::cleanup() +{ + delete sg; + sg = 0; +} + +QQuickRenderControl::QQuickRenderControl(QObject *parent) + : QObject(*(new QQuickRenderControlPrivate), parent) +{ +} + +/*! + Destroys the instance. Releases all scenegraph resources. + + \sa stop() + */ +QQuickRenderControl::~QQuickRenderControl() { Q_D(QQuickRenderControl); - if (d->window == 0) { - d->rc->invalidate(); + + stop(); + + if (d->window) + QQuickWindowPrivate::get(d->window)->renderControl = 0; +} + +void QQuickRenderControlPrivate::windowDestroyed() +{ + if (window == 0) { + rc->invalidate(); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } } +/*! + Initializes the scene graph resources. The context \a gl has to + be the current context. + */ void QQuickRenderControl::initialize(QOpenGLContext *gl) { Q_D(QQuickRenderControl); - if (!d->window) + + if (!d->window) { + qWarning("QQuickRenderControl::initialize called with no associated window"); + return; + } + + if (QOpenGLContext::currentContext() != gl) { + qWarning("QQuickRenderControl::initialize called with incorrect current context"); return; + } // It is the caller's responsiblity to make a context/surface current. // It cannot be done here since the surface to use may not be the @@ -121,17 +201,14 @@ void QQuickRenderControl::initialize(QOpenGLContext *gl) // window/surface at all. d->rc->initialize(gl); -} -void QQuickRenderControl::invalidate() -{ - Q_D(QQuickRenderControl); - d->rc->invalidate(); + d->initialized = true; } /*! This function should be called as late as possible before - sync(). In a threaded scenario, rendering can happen in parallel with this function. + sync(). In a threaded scenario, rendering can happen in parallel + with this function. */ void QQuickRenderControl::polishItems() { @@ -147,9 +224,13 @@ void QQuickRenderControl::polishItems() } /*! - Synchronize GUI and scenegraph. Returns true if the scene graph was changed. + This function is used to synchronize the QML scene with the rendering scene + graph. - This function is a synchronization point. Rendering can not happen in parallel. + If a dedicated render thread is used, the GUI thread should be blocked for the + duration of this call. + + \return \e true if the synchronization changed the scene graph. */ bool QQuickRenderControl::sync() { @@ -165,15 +246,36 @@ bool QQuickRenderControl::sync() } /*! - Stop rendering and release resources. This function is typically - called when the window is hidden. Requires a current context. + Stop rendering and release resources. Requires a current context. + + This is the equivalent of the cleanup operations that happen with a + real QQuickWindow when the window becomes hidden. + + This function takes QQuickWindow::persistentSceneGraph() into + account, meaning that context-specific resources are not released + when persistency is enabled. + + This function is called from the destructor. Therefore there will + typically be no need to call it directly. Pay attention however to + the fact that this requires the context, that was passed to + initialize(), to be the current one at the time of destroying the + QQuickRenderControl instance. + + Once stop() has been called, it is possible to reuse the + QQuickRenderControl instance by calling initialize() again. */ void QQuickRenderControl::stop() { Q_D(QQuickRenderControl); + if (!d->initialized) + return; + if (!d->window) return; + if (!QOpenGLContext::currentContext()) + return; + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); cd->fireAboutToStop(); cd->cleanupNodesOnShutdown(); @@ -182,10 +284,12 @@ void QQuickRenderControl::stop() d->rc->invalidate(); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } + + d->initialized = false; } /*! - Render the scenegraph using the current context. + Renders the scenegraph using the current context. */ void QQuickRenderControl::render() { @@ -197,7 +301,6 @@ void QQuickRenderControl::render() cd->renderSceneGraph(d->window->size()); } - /*! \fn void QQuickRenderControl::renderRequested() @@ -212,7 +315,9 @@ void QQuickRenderControl::render() true, then render() needs to be called. */ - +/*! + Grabs the contents of the scene and returns it as an image. + */ QImage QQuickRenderControl::grab() { Q_D(QQuickRenderControl); @@ -224,47 +329,16 @@ QImage QQuickRenderControl::grab() return grabContent; } -QSGContext *QQuickRenderControl::sceneGraphContext() const -{ - Q_D(const QQuickRenderControl); - return d->sg; -} - -QSGRenderContext *QQuickRenderControl::renderContext(QSGContext *) const +void QQuickRenderControlPrivate::update() { - Q_D(const QQuickRenderControl); - return d->rc; + Q_Q(QQuickRenderControl); + emit q->renderRequested(); } -void QQuickRenderControl::setWindow(QQuickWindow *window) +void QQuickRenderControlPrivate::maybeUpdate() { - Q_D(QQuickRenderControl); - d->window = window; -} - -/*! - Returns the offscreen window. - */ - -QQuickWindow *QQuickRenderControl::window() const -{ - Q_D(const QQuickRenderControl); - return d->window; -} - -/*! - Create an offscreen QQuickWindow for this render control, - unless the render control already has a window(). - - Returns the offscreen window if one is created, otherwise returns null. - The caller takes ownership of the window, and is responsible for deleting it. - */ -QQuickWindow *QQuickRenderControl::createOffscreenWindow() -{ - Q_D(QQuickRenderControl); - if (!d->window) - return new QQuickWindow(this); - return 0; + Q_Q(QQuickRenderControl); + emit q->sceneChanged(); } /*! @@ -284,7 +358,6 @@ QQuickWindow *QQuickRenderControl::createOffscreenWindow() inside its window. */ - QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset) { if (!win) @@ -295,6 +368,4 @@ QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset) return 0; } - - QT_END_NAMESPACE diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h new file mode 100644 index 0000000000..e5c04fee23 --- /dev/null +++ b/src/quick/items/qquickrendercontrol.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick 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 QQUICKRENDERCONTROL_H +#define QQUICKRENDERCONTROL_H + +#include <QtQuick/qtquickglobal.h> +#include <QtGui/QImage> + +QT_BEGIN_NAMESPACE + +class QQuickWindow; +class QOpenGLContext; +class QQuickRenderControlPrivate; + +class Q_QUICK_EXPORT QQuickRenderControl : public QObject +{ + Q_OBJECT + +public: + QQuickRenderControl(QObject *parent = 0); + ~QQuickRenderControl(); + + void initialize(QOpenGLContext *gl); + void stop(); + + void polishItems(); + void render(); + bool sync(); + + QImage grab(); + + static QWindow *renderWindowFor(QQuickWindow *win, QPoint *offset = 0); + virtual QWindow *renderWindow(QPoint *offset) { Q_UNUSED(offset); return 0; } + +Q_SIGNALS: + void renderRequested(); + void sceneChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickRenderControl) +}; + +QT_END_NAMESPACE + +#endif // QQUICKRENDERCONTROL_H diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h index cc30e37724..18b1b370b3 100644 --- a/src/quick/items/qquickrendercontrol_p.h +++ b/src/quick/items/qquickrendercontrol_p.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 QtQuick module of the Qt Toolkit. @@ -42,55 +42,45 @@ #ifndef QQUICKRENDERCONTROL_P_H #define QQUICKRENDERCONTROL_P_H -#include <QtGui/QImage> -#include <private/qtquickglobal_p.h> +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// -QT_BEGIN_NAMESPACE +#include "qquickrendercontrol.h" +#include <QtQuick/private/qsgcontext_p.h> -class QQuickWindow; -class QSGContext; -class QSGRenderContext; -class QAnimationDriver; -class QOpenGLContext; -class QQuickRenderControlPrivate; +QT_BEGIN_NAMESPACE -class Q_QUICK_PRIVATE_EXPORT QQuickRenderControl : public QObject +class QQuickRenderControlPrivate : public QObjectPrivate { - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickRenderControl) public: - QQuickRenderControl(); - ~QQuickRenderControl(); + Q_DECLARE_PUBLIC(QQuickRenderControl) - QQuickWindow *window() const; - QQuickWindow *createOffscreenWindow(); - virtual QWindow *renderWindow(QPoint *offset) { Q_UNUSED(offset); return 0; } - static QWindow *renderWindowFor(QQuickWindow *win, QPoint *offset = 0); + QQuickRenderControlPrivate(); + ~QQuickRenderControlPrivate(); - void windowDestroyed(); + static QQuickRenderControlPrivate *get(QQuickRenderControl *renderControl) { + return renderControl->d_func(); + } - void initialize(QOpenGLContext *gl); - void invalidate(); - void polishItems(); - void render(); - bool sync(); - void stop(); + static void cleanup(); - QImage grab(); - -Q_SIGNALS: - void renderRequested(); - void sceneChanged(); + void windowDestroyed(); -private: - friend class QQuickWindowPrivate; - friend class QQuickWindow; - void setWindow(QQuickWindow *window); - inline void update() { /*emit*/ renderRequested(); } - inline void maybeUpdate() { /*emit*/ sceneChanged(); } + void update(); + void maybeUpdate(); - QSGContext *sceneGraphContext() const; - QSGRenderContext *renderContext(QSGContext *) const; + bool initialized; + QQuickWindow *window; + static QSGContext *sg; + QSGRenderContext *rc; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 82897b3f5e..de8eb115dc 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -287,7 +287,7 @@ void QQuickWindow::update() if (d->windowManager) d->windowManager->update(this); else if (d->renderControl) - d->renderControl->update(); + QQuickRenderControlPrivate::get(d->renderControl)->update(); } void forcePolishHelper(QQuickItem *item) @@ -438,7 +438,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) customRenderMode = qgetenv("QSG_VISUALIZE"); renderControl = control; if (renderControl) - renderControl->setWindow(q); + QQuickRenderControlPrivate::get(renderControl)->window = q; if (!renderControl) windowManager = QSGRenderLoop::instance(); @@ -447,8 +447,9 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) QSGContext *sg; if (renderControl) { - sg = renderControl->sceneGraphContext(); - context = renderControl->renderContext(sg); + QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl); + sg = renderControlPriv->sg; + context = renderControlPriv->rc; } else { windowManager->addWindow(q); sg = windowManager->sceneGraphContext(); @@ -1087,7 +1088,7 @@ QQuickWindow::~QQuickWindow() d->animationController->deleteLater(); if (d->renderControl) { - d->renderControl->windowDestroyed(); + QQuickRenderControlPrivate::get(d->renderControl)->windowDestroyed(); } else if (d->windowManager) { d->windowManager->removeWindow(this); d->windowManager->windowDestroyed(this); @@ -1125,8 +1126,9 @@ void QQuickWindow::releaseResources() /*! - Sets whether the OpenGL context can be released to \a - persistent. The default value is true. + Sets whether the OpenGL context should be preserved, and cannot be + released until the last window is deleted, to \a persistent. The + default value is true. The OpenGL context can be released to free up graphics resources when the window is obscured, hidden or not rendering. When this @@ -2798,7 +2800,7 @@ void QQuickWindow::maybeUpdate() { Q_D(QQuickWindow); if (d->renderControl) - d->renderControl->maybeUpdate(); + QQuickRenderControlPrivate::get(d->renderControl)->maybeUpdate(); else if (d->windowManager) d->windowManager->maybeUpdate(this); } diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index c95ec5b46d..4ed663ee6e 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -87,6 +87,7 @@ public: Q_ENUMS(SceneGraphError) QQuickWindow(QWindow *parent = 0); + explicit QQuickWindow(QQuickRenderControl *renderControl); virtual ~QQuickWindow(); @@ -201,7 +202,6 @@ private: friend class QQuickWidget; friend class QQuickRenderControl; friend class QQuickAnimatorController; - explicit QQuickWindow(QQuickRenderControl*); Q_DISABLE_COPY(QQuickWindow) }; diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index f91d512ab0..dc6b872fb2 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -72,7 +72,7 @@ class QQuickWidgetRenderControl : public QQuickRenderControl { public: QQuickWidgetRenderControl(QQuickWidget *quickwidget) : m_quickWidget(quickwidget) {} - QWindow *renderWindow(QPoint *offset) { + QWindow *renderWindow(QPoint *offset) Q_DECL_OVERRIDE { if (offset) *offset = m_quickWidget->mapTo(m_quickWidget->window(), QPoint()); return m_quickWidget->window()->windowHandle(); @@ -86,7 +86,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) Q_Q(QQuickWidget); renderControl = new QQuickWidgetRenderControl(q); - offscreenWindow = renderControl->createOffscreenWindow(); + offscreenWindow = new QQuickWindow(renderControl); offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); // Do not call create() on offscreenWindow. @@ -162,8 +162,8 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate() // context and offscreenSurface are current at this stage, if the context was created. Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface)); + delete renderControl; // always delete the rendercontrol first delete offscreenWindow; - delete renderControl; delete resolvedFbo; delete fbo; @@ -877,7 +877,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) d->createContext(); createFramebufferObject(); - d->offscreenWindow->resizeEvent(e); + QCoreApplication::sendEvent(d->offscreenWindow, e); d->offscreenWindow->setGeometry(0, 0, e->size().width(), e->size().height()); QOpenGLContext *context = d->offscreenWindow->openglContext(); @@ -898,7 +898,7 @@ void QQuickWidget::keyPressEvent(QKeyEvent *e) Q_D(QQuickWidget); Q_QUICK_PROFILE(addEvent<QQuickProfiler::Key>()); - d->offscreenWindow->keyPressEvent(e); + QCoreApplication::sendEvent(d->offscreenWindow, e); } /*! \reimp */ @@ -907,7 +907,7 @@ void QQuickWidget::keyReleaseEvent(QKeyEvent *e) Q_D(QQuickWidget); Q_QUICK_PROFILE(addEvent<QQuickProfiler::Key>()); - d->offscreenWindow->keyReleaseEvent(e); + QCoreApplication::sendEvent(d->offscreenWindow, e); } /*! \reimp */ @@ -921,7 +921,7 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e) // the windowPos in e is ignored and is replaced by localPos. This is necessary // because QQuickWindow thinks of itself as a top-level window always. QMouseEvent mappedEvent(e->type(), e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); - d->offscreenWindow->mouseMoveEvent(&mappedEvent); + QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); } /*! \reimp */ @@ -934,10 +934,10 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e) // See QTBUG-25831 QMouseEvent pressEvent(QEvent::MouseButtonPress, e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); - d->offscreenWindow->mousePressEvent(&pressEvent); + QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent); QMouseEvent mappedEvent(e->type(), e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); - d->offscreenWindow->mouseDoubleClickEvent(&mappedEvent); + QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); } /*! \reimp */ @@ -963,7 +963,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e) Q_QUICK_PROFILE(addEvent<QQuickProfiler::Mouse>()); QMouseEvent mappedEvent(e->type(), e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); - d->offscreenWindow->mousePressEvent(&mappedEvent); + QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); } /*! \reimp */ @@ -973,7 +973,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e) Q_QUICK_PROFILE(addEvent<QQuickProfiler::Mouse>()); QMouseEvent mappedEvent(e->type(), e->localPos(), e->screenPos(), e->button(), e->buttons(), e->modifiers()); - d->offscreenWindow->mouseReleaseEvent(&mappedEvent); + QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); } #ifndef QT_NO_WHEELEVENT @@ -984,7 +984,7 @@ void QQuickWidget::wheelEvent(QWheelEvent *e) Q_QUICK_PROFILE(addEvent<QQuickProfiler::Mouse>()); // Wheel events only have local and global positions, no need to map. - d->offscreenWindow->wheelEvent(e); + QCoreApplication::sendEvent(d->offscreenWindow, e); } #endif @@ -1027,7 +1027,8 @@ bool QQuickWidget::event(QEvent *e) case QEvent::TouchUpdate: case QEvent::TouchCancel: // Touch events only have local and global positions, no need to map. - return d->offscreenWindow->event(e); + return QCoreApplication::sendEvent(d->offscreenWindow, e); + case QEvent::WindowChangeInternal: d->handleWindowChange(); break; -- GitLab