diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro index 421f95a1629ec4cdac0d6b43df3b65d8b1896d81..a412c53a65db853b32e480a0fd20cb88bd19afb2 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 0000000000000000000000000000000000000000..e7ede915404dccee9d84edd68ab8c3ae938733c8 --- /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 Binary files /dev/null and b/examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg differ diff --git a/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc b/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc new file mode 100644 index 0000000000000000000000000000000000000000..f8a9849a8a25ad63272da59ebc5b8357986497e5 --- /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 0000000000000000000000000000000000000000..d362278ddf7c9c4baf0d1918bb22b562841bbc4f --- /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 0000000000000000000000000000000000000000..ed25a11c1b915f91e186e1be3d5a11cb6a6e252c --- /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 0000000000000000000000000000000000000000..2246eeb8423dfddd5e8b06c69745ac7ea28fd98f --- /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 0000000000000000000000000000000000000000..396e9f8afa565b1df232882084b80919f2319a80 --- /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 0000000000000000000000000000000000000000..2723aeb0111c655919a873928d59aa831c1ecb9c --- /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 7a54b7a021fa6b31c01875515dbd0ebafc743e37..1f8147c4d119c069ffb851caf8ab8527d1b086e6 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 add909d0cb34fd3f9dd41bb5177bd98adda64f4e..042ff80abc4c2f9d4af70ebf551534f406f6e03a 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 b8d572114aea0206ffd2265badb786003577e459..311a1b95f88f502065c8fb0c3ba80c2a5c65ca3f 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 0000000000000000000000000000000000000000..e5c04fee2352fafa3886bd7318539deee62e7604 --- /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 cc30e37724cfa4a682fd4327921b73b83d4a6273..18b1b370b3963642912fa6b08b0af9a696370417 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 82897b3f5e420df8ed05907ebc0bfbc5e49c1bb7..de8eb115dc0762040f65ebbd5188534de5ca1449 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 c95ec5b46d550e34511a3b900febb2715c866037..4ed663ee6ee7298cc0659c64cc59780f97ef0679 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 f91d512ab06a220e4907d7f24fd31e042a49e84e..dc6b872fb28e3c6007ddc0cc208312cc95476869 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;