From 1a1028a1e4ca3d0d932141f265d01284e013a76a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 23 Mar 2020 19:19:20 +0100 Subject: Move legacy rendercontrol example into a subdirectory ...called rendercontrol_opengl under examples/quick/rendercontrol. This example is going to be migrated to support operating with RHI-on-OpenGL later on. Additionally, we can this way introduce more rendercontrol examples in the future, for example to show how to do things with Vulkan, Metal, D3D. Task-number: QTBUG-78595 Change-Id: I7f5243b1f86e62949400107bf12bfa07b17b1031 Reviewed-by: Eirik Aavitsland --- examples/quick/rendercontrol/CMakeLists.txt | 48 +-- examples/quick/rendercontrol/cuberenderer.cpp | 226 ---------- examples/quick/rendercontrol/cuberenderer.h | 85 ---- examples/quick/rendercontrol/demo.qml | 208 ---------- .../doc/images/rendercontrol-example.jpg | Bin 44196 -> 0 bytes .../quick/rendercontrol/doc/src/rendercontrol.qdoc | 33 -- examples/quick/rendercontrol/main.cpp | 86 ---- examples/quick/rendercontrol/rendercontrol.pro | 19 +- examples/quick/rendercontrol/rendercontrol.qrc | 5 - .../rendercontrol_opengl/CMakeLists.txt | 49 +++ .../rendercontrol_opengl/cuberenderer.cpp | 226 ++++++++++ .../rendercontrol_opengl/cuberenderer.h | 85 ++++ .../rendercontrol/rendercontrol_opengl/demo.qml | 208 ++++++++++ .../doc/images/rendercontrol-example.jpg | Bin 0 -> 44196 bytes .../doc/src/rendercontrol.qdoc | 33 ++ .../rendercontrol/rendercontrol_opengl/main.cpp | 86 ++++ .../rendercontrol_opengl/rendercontrol.qrc | 5 + .../rendercontrol_opengl/rendercontrol_opengl.pro | 17 + .../rendercontrol_opengl/window_multithreaded.cpp | 454 +++++++++++++++++++++ .../rendercontrol_opengl/window_multithreaded.h | 152 +++++++ .../rendercontrol_opengl/window_singlethreaded.cpp | 352 ++++++++++++++++ .../rendercontrol_opengl/window_singlethreaded.h | 114 ++++++ .../quick/rendercontrol/window_multithreaded.cpp | 454 --------------------- .../quick/rendercontrol/window_multithreaded.h | 152 ------- .../quick/rendercontrol/window_singlethreaded.cpp | 352 ---------------- .../quick/rendercontrol/window_singlethreaded.h | 114 ------ 26 files changed, 1785 insertions(+), 1778 deletions(-) delete mode 100644 examples/quick/rendercontrol/cuberenderer.cpp delete mode 100644 examples/quick/rendercontrol/cuberenderer.h delete mode 100644 examples/quick/rendercontrol/demo.qml delete mode 100644 examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg delete mode 100644 examples/quick/rendercontrol/doc/src/rendercontrol.qdoc delete mode 100644 examples/quick/rendercontrol/main.cpp delete mode 100644 examples/quick/rendercontrol/rendercontrol.qrc create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.h create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/demo.qml create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/doc/images/rendercontrol-example.jpg create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol.qdoc create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/main.cpp create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol.qrc create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.cpp create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.h create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.cpp create mode 100644 examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h delete mode 100644 examples/quick/rendercontrol/window_multithreaded.cpp delete mode 100644 examples/quick/rendercontrol/window_multithreaded.h delete mode 100644 examples/quick/rendercontrol/window_singlethreaded.cpp delete mode 100644 examples/quick/rendercontrol/window_singlethreaded.h (limited to 'examples/quick') diff --git a/examples/quick/rendercontrol/CMakeLists.txt b/examples/quick/rendercontrol/CMakeLists.txt index 8f1067a7b5..bfcd71de24 100644 --- a/examples/quick/rendercontrol/CMakeLists.txt +++ b/examples/quick/rendercontrol/CMakeLists.txt @@ -1,49 +1,3 @@ # Generated from rendercontrol.pro. -cmake_minimum_required(VERSION 3.14) -project(rendercontrol LANGUAGES CXX) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -set(INSTALL_EXAMPLEDIR "examples/quick/rendercontrol") - -find_package(Qt6 COMPONENTS Core) -find_package(Qt6 COMPONENTS Gui) -find_package(Qt6 COMPONENTS Quick) -find_package(Qt6 COMPONENTS Qml) - -add_qt_gui_executable(rendercontrol - cuberenderer.cpp cuberenderer.h - main.cpp - window_multithreaded.cpp window_multithreaded.h - window_singlethreaded.cpp window_singlethreaded.h -) -target_link_libraries(rendercontrol PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick -) - - -# Resources: -set(rendercontrol_resource_files - "demo.qml" -) - -qt6_add_resources(rendercontrol "rendercontrol" - PREFIX - "/rendercontrol" - FILES - ${rendercontrol_resource_files} -) - -install(TARGETS rendercontrol - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" -) +add_subdirectory(rendercontrol_opengl) diff --git a/examples/quick/rendercontrol/cuberenderer.cpp b/examples/quick/rendercontrol/cuberenderer.cpp deleted file mode 100644 index f3ecfc2566..0000000000 --- a/examples/quick/rendercontrol/cuberenderer.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd 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 "cuberenderer.h" -#include -#include -#include -#include -#include -#include -#include -#include - -CubeRenderer::CubeRenderer(QOffscreenSurface *offscreenSurface) - : m_offscreenSurface(offscreenSurface), - m_context(nullptr), - m_program(nullptr), - m_vbo(nullptr), - m_vao(nullptr), - m_matrixLoc(0) -{ -} - -CubeRenderer::~CubeRenderer() -{ - // Use a temporary offscreen surface to do the cleanup. - // There may not be a native window surface available anymore at this stage. - m_context->makeCurrent(m_offscreenSurface); - - delete m_program; - delete m_vbo; - delete m_vao; - - m_context->doneCurrent(); - delete m_context; -} - -void CubeRenderer::init(QWindow *w, QOpenGLContext *share) -{ - m_context = new QOpenGLContext; - m_context->setShareContext(share); - m_context->setFormat(w->requestedFormat()); - m_context->create(); - if (!m_context->makeCurrent(w)) - return; - - QOpenGLFunctions *f = m_context->functions(); - f->glClearColor(0.0f, 0.1f, 0.25f, 1.0f); - f->glViewport(0, 0, w->width() * w->devicePixelRatio(), w->height() * w->devicePixelRatio()); - - 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->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); - m_program->addCacheableShaderFromSourceCode(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(); - QOpenGLVertexArrayObject::Binder vaoBinder(m_vao); - - 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(); -} - -void CubeRenderer::resize(int w, int h) -{ - m_proj.setToIdentity(); - m_proj.perspective(45, w / float(h), 0.01f, 100.0f); -} - -void CubeRenderer::setupVertexAttribs() -{ - m_vbo->bind(); - m_program->enableAttributeArray(0); - m_program->enableAttributeArray(1); - m_context->functions()->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); - m_context->functions()->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, - (const void *)(36 * 3 * sizeof(GLfloat))); - m_vbo->release(); -} - -void CubeRenderer::render(QWindow *w, QOpenGLContext *share, uint texture) -{ - if (!m_context) - init(w, share); - - if (!m_context->makeCurrent(w)) - return; - - QOpenGLFunctions *f = m_context->functions(); - f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - if (texture) { - f->glBindTexture(GL_TEXTURE_2D, texture); - f->glFrontFace(GL_CW); // because our cube's vertex data is such - f->glEnable(GL_CULL_FACE); - f->glEnable(GL_DEPTH_TEST); - - 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_context->swapBuffers(w); -} diff --git a/examples/quick/rendercontrol/cuberenderer.h b/examples/quick/rendercontrol/cuberenderer.h deleted file mode 100644 index c7655a3c8e..0000000000 --- a/examples/quick/rendercontrol/cuberenderer.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef CUBERENDERER_H -#define CUBERENDERER_H - -#include - -QT_FORWARD_DECLARE_CLASS(QOpenGLContext) -QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) -QT_FORWARD_DECLARE_CLASS(QOpenGLBuffer) -QT_FORWARD_DECLARE_CLASS(QOpenGLVertexArrayObject) -QT_FORWARD_DECLARE_CLASS(QWindow) -QT_FORWARD_DECLARE_CLASS(QOffscreenSurface) - -class CubeRenderer -{ -public: - CubeRenderer(QOffscreenSurface *offscreenSurface); - ~CubeRenderer(); - - void resize(int w, int h); - void render(QWindow *w, QOpenGLContext *share, uint texture); - -private: - void init(QWindow *w, QOpenGLContext *share); - void setupVertexAttribs(); - - QOffscreenSurface *m_offscreenSurface; - QOpenGLContext *m_context; - QOpenGLShaderProgram *m_program; - QOpenGLBuffer *m_vbo; - QOpenGLVertexArrayObject *m_vao; - int m_matrixLoc; - QMatrix4x4 m_proj; -}; - -#endif diff --git a/examples/quick/rendercontrol/demo.qml b/examples/quick/rendercontrol/demo.qml deleted file mode 100644 index 0312c2ec1c..0000000000 --- a/examples/quick/rendercontrol/demo.qml +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd 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$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtQuick.Particles 2.0 - -Rectangle { - id: root - - gradient: Gradient { - GradientStop { position: 0; color: mouse.pressed ? "lightsteelblue" : "steelblue" } - GradientStop { position: 1; color: "black" } - } - - Text { - anchors.centerIn: parent - text: "Qt Quick in a texture" - font.pointSize: 40 - color: "white" - - SequentialAnimation on rotation { - PauseAnimation { duration: 2500 } - NumberAnimation { from: 0; to: 360; duration: 5000; easing.type: Easing.InOutCubic } - loops: Animation.Infinite - } - } - - 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 { - id: mouse - anchors.fill: parent - } -} diff --git a/examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg b/examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg deleted file mode 100644 index a899ebe7f5..0000000000 Binary files a/examples/quick/rendercontrol/doc/images/rendercontrol-example.jpg and /dev/null differ diff --git a/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc b/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc deleted file mode 100644 index 026a6f5a09..0000000000 --- a/examples/quick/rendercontrol/doc/src/rendercontrol.qdoc +++ /dev/null @@ -1,33 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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: https://www.gnu.org/licenses/fdl-1.3.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 deleted file mode 100644 index 71903045cc..0000000000 --- a/examples/quick/rendercontrol/main.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd 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 -#include -#include -#include "window_singlethreaded.h" -#include "window_multithreaded.h" - -int main(int argc, char **argv) -{ - QGuiApplication app(argc, argv); - - QCoreApplication::setApplicationName("Qt Render Control Example"); - QCoreApplication::setOrganizationName("QtProject"); - QCoreApplication::setApplicationVersion(QT_VERSION_STR); - QCommandLineParser parser; - parser.setApplicationDescription(QCoreApplication::applicationName()); - parser.addHelpOption(); - parser.addVersionOption(); - QCommandLineOption threadedOption("threaded", "Threaded Rendering"); - parser.addOption(threadedOption); - - parser.process(app); - - QScopedPointer window; - if (parser.isSet(threadedOption)) { - qWarning("Using separate Qt Quick render thread"); - window.reset(new WindowMultiThreaded); - } else { - qWarning("Using single-threaded rendering"); - window.reset(new WindowSingleThreaded); - } - - window->resize(1024, 768); - window->show(); - - return app.exec(); -} diff --git a/examples/quick/rendercontrol/rendercontrol.pro b/examples/quick/rendercontrol/rendercontrol.pro index 3301a773a4..cdb431c8fd 100644 --- a/examples/quick/rendercontrol/rendercontrol.pro +++ b/examples/quick/rendercontrol/rendercontrol.pro @@ -1,17 +1,4 @@ -TEMPLATE = app +TEMPLATE = subdirs -QT += quick qml - -SOURCES += main.cpp \ - window_singlethreaded.cpp \ - window_multithreaded.cpp \ - cuberenderer.cpp - -HEADERS += window_singlethreaded.h \ - window_multithreaded.h \ - cuberenderer.h - -RESOURCES += rendercontrol.qrc - -target.path = $$[QT_INSTALL_EXAMPLES]/quick/rendercontrol -INSTALLS += target +SUBDIRS = \ + rendercontrol_opengl diff --git a/examples/quick/rendercontrol/rendercontrol.qrc b/examples/quick/rendercontrol/rendercontrol.qrc deleted file mode 100644 index 2246eeb842..0000000000 --- a/examples/quick/rendercontrol/rendercontrol.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - demo.qml - - diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt b/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt new file mode 100644 index 0000000000..fb1c6ecf12 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt @@ -0,0 +1,49 @@ +# Generated from rendercontrol_opengl.pro. + +cmake_minimum_required(VERSION 3.14) +project(rendercontrol_opengl LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(INSTALL_EXAMPLEDIR "examples/quick/rendercontrol/rendercontrol_opengl") + +find_package(Qt6 COMPONENTS Core) +find_package(Qt6 COMPONENTS Gui) +find_package(Qt6 COMPONENTS Quick) +find_package(Qt6 COMPONENTS Qml) + +add_qt_gui_executable(rendercontrol_opengl + cuberenderer.cpp cuberenderer.h + main.cpp + window_multithreaded.cpp window_multithreaded.h + window_singlethreaded.cpp window_singlethreaded.h +) +target_link_libraries(rendercontrol_opengl PUBLIC + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick +) + + +# Resources: +set(rendercontrol_resource_files + "demo.qml" +) + +qt6_add_resources(rendercontrol_opengl "rendercontrol" + PREFIX + "/rendercontrol" + FILES + ${rendercontrol_resource_files} +) + +install(TARGETS rendercontrol_opengl + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp new file mode 100644 index 0000000000..f3ecfc2566 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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 "cuberenderer.h" +#include +#include +#include +#include +#include +#include +#include +#include + +CubeRenderer::CubeRenderer(QOffscreenSurface *offscreenSurface) + : m_offscreenSurface(offscreenSurface), + m_context(nullptr), + m_program(nullptr), + m_vbo(nullptr), + m_vao(nullptr), + m_matrixLoc(0) +{ +} + +CubeRenderer::~CubeRenderer() +{ + // Use a temporary offscreen surface to do the cleanup. + // There may not be a native window surface available anymore at this stage. + m_context->makeCurrent(m_offscreenSurface); + + delete m_program; + delete m_vbo; + delete m_vao; + + m_context->doneCurrent(); + delete m_context; +} + +void CubeRenderer::init(QWindow *w, QOpenGLContext *share) +{ + m_context = new QOpenGLContext; + m_context->setShareContext(share); + m_context->setFormat(w->requestedFormat()); + m_context->create(); + if (!m_context->makeCurrent(w)) + return; + + QOpenGLFunctions *f = m_context->functions(); + f->glClearColor(0.0f, 0.1f, 0.25f, 1.0f); + f->glViewport(0, 0, w->width() * w->devicePixelRatio(), w->height() * w->devicePixelRatio()); + + 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->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); + m_program->addCacheableShaderFromSourceCode(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(); + QOpenGLVertexArrayObject::Binder vaoBinder(m_vao); + + 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(); +} + +void CubeRenderer::resize(int w, int h) +{ + m_proj.setToIdentity(); + m_proj.perspective(45, w / float(h), 0.01f, 100.0f); +} + +void CubeRenderer::setupVertexAttribs() +{ + m_vbo->bind(); + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_context->functions()->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + m_context->functions()->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, + (const void *)(36 * 3 * sizeof(GLfloat))); + m_vbo->release(); +} + +void CubeRenderer::render(QWindow *w, QOpenGLContext *share, uint texture) +{ + if (!m_context) + init(w, share); + + if (!m_context->makeCurrent(w)) + return; + + QOpenGLFunctions *f = m_context->functions(); + f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (texture) { + f->glBindTexture(GL_TEXTURE_2D, texture); + f->glFrontFace(GL_CW); // because our cube's vertex data is such + f->glEnable(GL_CULL_FACE); + f->glEnable(GL_DEPTH_TEST); + + 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_context->swapBuffers(w); +} diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.h b/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.h new file mode 100644 index 0000000000..c7655a3c8e --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUBERENDERER_H +#define CUBERENDERER_H + +#include + +QT_FORWARD_DECLARE_CLASS(QOpenGLContext) +QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) +QT_FORWARD_DECLARE_CLASS(QOpenGLBuffer) +QT_FORWARD_DECLARE_CLASS(QOpenGLVertexArrayObject) +QT_FORWARD_DECLARE_CLASS(QWindow) +QT_FORWARD_DECLARE_CLASS(QOffscreenSurface) + +class CubeRenderer +{ +public: + CubeRenderer(QOffscreenSurface *offscreenSurface); + ~CubeRenderer(); + + void resize(int w, int h); + void render(QWindow *w, QOpenGLContext *share, uint texture); + +private: + void init(QWindow *w, QOpenGLContext *share); + void setupVertexAttribs(); + + QOffscreenSurface *m_offscreenSurface; + QOpenGLContext *m_context; + QOpenGLShaderProgram *m_program; + QOpenGLBuffer *m_vbo; + QOpenGLVertexArrayObject *m_vao; + int m_matrixLoc; + QMatrix4x4 m_proj; +}; + +#endif diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml b/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml new file mode 100644 index 0000000000..0312c2ec1c --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle { + id: root + + gradient: Gradient { + GradientStop { position: 0; color: mouse.pressed ? "lightsteelblue" : "steelblue" } + GradientStop { position: 1; color: "black" } + } + + Text { + anchors.centerIn: parent + text: "Qt Quick in a texture" + font.pointSize: 40 + color: "white" + + SequentialAnimation on rotation { + PauseAnimation { duration: 2500 } + NumberAnimation { from: 0; to: 360; duration: 5000; easing.type: Easing.InOutCubic } + loops: Animation.Infinite + } + } + + 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 { + id: mouse + anchors.fill: parent + } +} diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/doc/images/rendercontrol-example.jpg b/examples/quick/rendercontrol/rendercontrol_opengl/doc/images/rendercontrol-example.jpg new file mode 100644 index 0000000000..a899ebe7f5 Binary files /dev/null and b/examples/quick/rendercontrol/rendercontrol_opengl/doc/images/rendercontrol-example.jpg differ diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol.qdoc b/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol.qdoc new file mode 100644 index 0000000000..9b6b075a5b --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol.qdoc @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \title QQuickRenderControl Example + \example rendercontrol/rendercontrol_opengl + \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/rendercontrol_opengl/main.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp new file mode 100644 index 0000000000..71903045cc --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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 +#include +#include +#include "window_singlethreaded.h" +#include "window_multithreaded.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + QCoreApplication::setApplicationName("Qt Render Control Example"); + QCoreApplication::setOrganizationName("QtProject"); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::applicationName()); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption threadedOption("threaded", "Threaded Rendering"); + parser.addOption(threadedOption); + + parser.process(app); + + QScopedPointer window; + if (parser.isSet(threadedOption)) { + qWarning("Using separate Qt Quick render thread"); + window.reset(new WindowMultiThreaded); + } else { + qWarning("Using single-threaded rendering"); + window.reset(new WindowSingleThreaded); + } + + window->resize(1024, 768); + window->show(); + + return app.exec(); +} diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol.qrc b/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol.qrc new file mode 100644 index 0000000000..2246eeb842 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol.qrc @@ -0,0 +1,5 @@ + + + demo.qml + + diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro b/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro new file mode 100644 index 0000000000..900f5d14dd --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro @@ -0,0 +1,17 @@ +TEMPLATE = app + +QT += quick qml + +SOURCES += main.cpp \ + window_singlethreaded.cpp \ + window_multithreaded.cpp \ + cuberenderer.cpp + +HEADERS += window_singlethreaded.h \ + window_multithreaded.h \ + cuberenderer.h + +RESOURCES += rendercontrol.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/rendercontrol/rendercontrol_opengl +INSTALLS += target diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.cpp new file mode 100644 index 0000000000..01d6b8b5b2 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.cpp @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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_multithreaded.h" +#include "cuberenderer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + This implementation runs the Qt Quick scenegraph's sync and render phases on a + separate, dedicated thread. Rendering the cube using our custom OpenGL engine + happens on that thread as well. This is similar to the built-in threaded + render loop, but does not support all the features. There is no support for + getting Animators running on the render thread for example. + + We choose to use QObject's event mechanism to communicate with the QObject + living on the render thread. An alternative would be to subclass QThread and + reimplement run() with a custom event handling approach, like + QSGThreadedRenderLoop does. That would potentially lead to better results but + is also more complex. +*/ + +static const QEvent::Type INIT = QEvent::Type(QEvent::User + 1); +static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); +static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); +static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); + +static const QEvent::Type UPDATE = QEvent::Type(QEvent::User + 5); + +QuickRenderer::QuickRenderer() + : m_context(nullptr), + m_surface(nullptr), + m_fbo(nullptr), + m_window(nullptr), + m_quickWindow(nullptr), + m_renderControl(nullptr), + m_cubeRenderer(nullptr), + m_quit(false) +{ +} + +void QuickRenderer::requestInit() +{ + QCoreApplication::postEvent(this, new QEvent(INIT)); +} + +void QuickRenderer::requestRender() +{ + QCoreApplication::postEvent(this, new QEvent(RENDER)); +} + +void QuickRenderer::requestResize() +{ + QCoreApplication::postEvent(this, new QEvent(RESIZE)); +} + +void QuickRenderer::requestStop() +{ + QCoreApplication::postEvent(this, new QEvent(STOP)); +} + +bool QuickRenderer::event(QEvent *e) +{ + QMutexLocker lock(&m_mutex); + + switch (int(e->type())) { + case INIT: + init(); + return true; + case RENDER: + render(&lock); + return true; + case RESIZE: + if (m_cubeRenderer) + m_cubeRenderer->resize(m_window->width(), m_window->height()); + return true; + case STOP: + cleanup(); + return true; + default: + return QObject::event(e); + } +} + +void QuickRenderer::init() +{ + m_context->makeCurrent(m_surface); + + // Pass our offscreen surface to the cube renderer just so that it will + // have something is can make current during cleanup. QOffscreenSurface, + // just like QWindow, must always be created on the gui thread (as it might + // be backed by an actual QWindow). + m_cubeRenderer = new CubeRenderer(m_surface); + m_cubeRenderer->resize(m_window->width(), m_window->height()); + + m_renderControl->initialize(m_context); +} + +void QuickRenderer::cleanup() +{ + m_context->makeCurrent(m_surface); + + m_renderControl->invalidate(); + + delete m_fbo; + m_fbo = nullptr; + + delete m_cubeRenderer; + m_cubeRenderer = nullptr; + + m_context->doneCurrent(); + m_context->moveToThread(QCoreApplication::instance()->thread()); + + m_cond.wakeOne(); +} + +void QuickRenderer::ensureFbo() +{ + if (m_fbo && m_fbo->size() != m_window->size() * m_window->devicePixelRatio()) { + delete m_fbo; + m_fbo = nullptr; + } + + if (!m_fbo) { + m_fbo = new QOpenGLFramebufferObject(m_window->size() * m_window->devicePixelRatio(), + QOpenGLFramebufferObject::CombinedDepthStencil); + m_quickWindow->setRenderTarget(m_fbo); + } +} + +void QuickRenderer::render(QMutexLocker *lock) +{ + Q_ASSERT(QThread::currentThread() != m_window->thread()); + + if (!m_context->makeCurrent(m_surface)) { + qWarning("Failed to make context current on render thread"); + return; + } + + ensureFbo(); + + // Synchronization and rendering happens here on the render thread. + m_renderControl->sync(); + + // The gui thread can now continue. + m_cond.wakeOne(); + lock->unlock(); + + // Meanwhile on this thread continue with the actual rendering (into the FBO first). + m_renderControl->render(); + m_context->functions()->glFlush(); + + // The cube renderer uses its own context, no need to bother with the state here. + + // Get something onto the screen using our custom OpenGL engine. + QMutexLocker quitLock(&m_quitMutex); + if (!m_quit) + m_cubeRenderer->render(m_window, m_context, m_fbo->texture()); +} + +void QuickRenderer::aboutToQuit() +{ + QMutexLocker lock(&m_quitMutex); + m_quit = true; +} + +class RenderControl : public QQuickRenderControl +{ +public: + RenderControl(QWindow *w) : m_window(w) { } + QWindow *renderWindow(QPoint *offset) override; + +private: + QWindow *m_window; +}; + +WindowMultiThreaded::WindowMultiThreaded() + : m_qmlComponent(nullptr), + m_rootItem(nullptr), + m_quickInitialized(false), + m_psrRequested(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 RenderControl(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()); + + m_quickRenderer = new QuickRenderer; + m_quickRenderer->setContext(m_context); + + // These live on the gui thread. Just give access to them on the render thread. + m_quickRenderer->setSurface(m_offscreenSurface); + m_quickRenderer->setWindow(this); + m_quickRenderer->setQuickWindow(m_quickWindow); + m_quickRenderer->setRenderControl(m_renderControl); + + m_quickRendererThread = new QThread; + + // Notify the render control that some scenegraph internals have to live on + // m_quickRenderThread. + m_renderControl->prepareThread(m_quickRendererThread); + + // The QOpenGLContext and the QObject representing the rendering logic on + // the render thread must live on that thread. + m_context->moveToThread(m_quickRendererThread); + m_quickRenderer->moveToThread(m_quickRendererThread); + + m_quickRendererThread->start(); + + // 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_renderControl, &QQuickRenderControl::renderRequested, this, &WindowMultiThreaded::requestUpdate); + connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, &WindowMultiThreaded::requestUpdate); +} + +WindowMultiThreaded::~WindowMultiThreaded() +{ + // Release resources and move the context ownership back to this thread. + m_quickRenderer->mutex()->lock(); + m_quickRenderer->requestStop(); + m_quickRenderer->cond()->wait(m_quickRenderer->mutex()); + m_quickRenderer->mutex()->unlock(); + + m_quickRendererThread->quit(); + m_quickRendererThread->wait(); + + delete m_renderControl; + delete m_qmlComponent; + delete m_quickWindow; + delete m_qmlEngine; + + delete m_offscreenSurface; + delete m_context; +} + +void WindowMultiThreaded::requestUpdate() +{ + if (m_quickInitialized && !m_psrRequested) { + m_psrRequested = true; + QCoreApplication::postEvent(this, new QEvent(UPDATE)); + } +} + +bool WindowMultiThreaded::event(QEvent *e) +{ + if (e->type() == UPDATE) { + polishSyncAndRender(); + m_psrRequested = false; + return true; + } else if (e->type() == QEvent::Close) { + // Avoid rendering on the render thread when the window is about to + // close. Once a QWindow is closed, the underlying platform window will + // go away, even though the QWindow instance itself is still + // valid. Operations like swapBuffers() are futile and only result in + // warnings afterwards. Prevent this. + m_quickRenderer->aboutToQuit(); + } + return QWindow::event(e); +} + +void WindowMultiThreaded::polishSyncAndRender() +{ + Q_ASSERT(QThread::currentThread() == thread()); + + // Polishing happens on the gui thread. + m_renderControl->polishItems(); + // Sync happens on the render thread with the gui thread (this one) blocked. + QMutexLocker lock(m_quickRenderer->mutex()); + m_quickRenderer->requestRender(); + // Wait until sync is complete. + m_quickRenderer->cond()->wait(m_quickRenderer->mutex()); + // Rendering happens on the render thread without blocking the gui (main) + // thread. This is good because the blocking swap (waiting for vsync) + // happens on the render thread, not blocking other work. +} + +void WindowMultiThreaded::run() +{ + disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowMultiThreaded::run); + + if (m_qmlComponent->isError()) { + const QList errorList = m_qmlComponent->errors(); + for (const QQmlError &error : errorList) + qWarning() << error.url() << error.line() << error; + return; + } + + QObject *rootObject = m_qmlComponent->create(); + if (m_qmlComponent->isError()) { + const QList errorList = m_qmlComponent->errors(); + for (const QQmlError &error : errorList) + qWarning() << error.url() << error.line() << error; + return; + } + + m_rootItem = qobject_cast(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(); + + m_quickInitialized = true; + + // Initialize the render thread and perform the first polish/sync/render. + m_quickRenderer->requestInit(); + polishSyncAndRender(); +} + +void WindowMultiThreaded::updateSizes() +{ + // Behave like SizeRootObjectToView. + m_rootItem->setWidth(width()); + m_rootItem->setHeight(height()); + + m_quickWindow->setGeometry(0, 0, width(), height()); +} + +void WindowMultiThreaded::startQuick(const QString &filename) +{ + m_qmlComponent = new QQmlComponent(m_qmlEngine, QUrl(filename)); + if (m_qmlComponent->isLoading()) + connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowMultiThreaded::run); + else + run(); +} + +void WindowMultiThreaded::exposeEvent(QExposeEvent *) +{ + if (isExposed()) { + if (!m_quickInitialized) + startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); + } +} + +void WindowMultiThreaded::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) { + updateSizes(); + m_quickRenderer->requestResize(); + polishSyncAndRender(); + } +} + +void WindowMultiThreaded::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 WindowMultiThreaded::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/rendercontrol_opengl/window_multithreaded.h b/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.h new file mode 100644 index 0000000000..ded80a0064 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINDOW_MULTITHREADED_H +#define WINDOW_MULTITHREADED_H + +#include +#include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QOpenGLContext) +QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject) +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 CubeRenderer; + +class QuickRenderer : public QObject +{ + Q_OBJECT + +public: + QuickRenderer(); + + void requestInit(); + void requestRender(); + void requestResize(); + void requestStop(); + + QWaitCondition *cond() { return &m_cond; } + QMutex *mutex() { return &m_mutex; } + + void setContext(QOpenGLContext *ctx) { m_context = ctx; } + void setSurface(QOffscreenSurface *s) { m_surface = s; } + void setWindow(QWindow *w) { m_window = w; } + void setQuickWindow(QQuickWindow *w) { m_quickWindow = w; } + void setRenderControl(QQuickRenderControl *r) { m_renderControl = r; } + + void aboutToQuit(); + +private: + bool event(QEvent *e) override; + void init(); + void cleanup(); + void ensureFbo(); + void render(QMutexLocker *lock); + + QWaitCondition m_cond; + QMutex m_mutex; + QOpenGLContext *m_context; + QOffscreenSurface *m_surface; + QOpenGLFramebufferObject *m_fbo; + QWindow *m_window; + QQuickWindow *m_quickWindow; + QQuickRenderControl *m_renderControl; + CubeRenderer *m_cubeRenderer; + QMutex m_quitMutex; + bool m_quit; +}; + +class WindowMultiThreaded : public QWindow +{ + Q_OBJECT + +public: + WindowMultiThreaded(); + ~WindowMultiThreaded(); + +protected: + void exposeEvent(QExposeEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + bool event(QEvent *e) override; + +private slots: + void run(); + void requestUpdate(); + void polishSyncAndRender(); + +private: + void startQuick(const QString &filename); + void updateSizes(); + + QuickRenderer *m_quickRenderer; + QThread *m_quickRendererThread; + + QOpenGLContext *m_context; + QOffscreenSurface *m_offscreenSurface; + QQuickRenderControl *m_renderControl; + QQuickWindow *m_quickWindow; + QQmlEngine *m_qmlEngine; + QQmlComponent *m_qmlComponent; + QQuickItem *m_rootItem; + bool m_quickInitialized; + bool m_psrRequested; +}; + +#endif diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.cpp new file mode 100644 index 0000000000..ddbbfe4b52 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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_singlethreaded.h" +#include "cuberenderer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class RenderControl : public QQuickRenderControl +{ +public: + RenderControl(QWindow *w) : m_window(w) { } + QWindow *renderWindow(QPoint *offset) override; + +private: + QWindow *m_window; +}; + +QWindow *RenderControl::renderWindow(QPoint *offset) +{ + if (offset) + *offset = QPoint(0, 0); + return m_window; +} + +WindowSingleThreaded::WindowSingleThreaded() + : m_rootItem(nullptr), + m_fbo(nullptr), + m_quickInitialized(false), + m_quickReady(false), + m_dpr(0) +{ + setSurfaceType(QSurface::OpenGLSurface); + + // The rendercontrol does not necessarily need an FBO. Demonstrate this + // when requested. + m_onscreen = QCoreApplication::arguments().contains(QStringLiteral("--onscreen")); + + 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_cubeRenderer = new CubeRenderer(m_offscreenSurface); + + m_renderControl = new RenderControl(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, &WindowSingleThreaded::render); + + // 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, &WindowSingleThreaded::createFbo); + connect(m_quickWindow, &QQuickWindow::sceneGraphInvalidated, this, &WindowSingleThreaded::destroyFbo); + connect(m_renderControl, &QQuickRenderControl::renderRequested, this, &WindowSingleThreaded::requestUpdate); + connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, &WindowSingleThreaded::requestUpdate); + + // Just recreating the FBO on resize is not sufficient, when moving between screens + // with different devicePixelRatio the QWindow size may remain the same but the FBO + // dimension is to change regardless. + connect(this, &QWindow::screenChanged, this, &WindowSingleThreaded::handleScreenChange); +} + +WindowSingleThreaded::~WindowSingleThreaded() +{ + // Make sure the context is current while doing cleanup. Note that we use the + // offscreen surface here because passing 'this' at this point is not safe: the + // underlying platform window may already be destroyed. To avoid all the trouble, use + // another surface that is valid for sure. + m_context->makeCurrent(m_offscreenSurface); + + // 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; + + m_context->doneCurrent(); + + delete m_cubeRenderer; + + delete m_offscreenSurface; + delete m_context; +} + +void WindowSingleThreaded::createFbo() +{ + // The scene graph has been initialized. It is now time to create an FBO and associate + // it with the QQuickWindow. + m_dpr = devicePixelRatio(); + if (!m_onscreen) { + m_fbo = new QOpenGLFramebufferObject(size() * m_dpr, QOpenGLFramebufferObject::CombinedDepthStencil); + m_quickWindow->setRenderTarget(m_fbo); + } else { + // Special case: No FBO. Render directly to the window's default framebuffer. + m_onscreenSize = size() * m_dpr; + m_quickWindow->setRenderTarget(0, m_onscreenSize); + } +} + +void WindowSingleThreaded::destroyFbo() +{ + delete m_fbo; + m_fbo = nullptr; +} + +void WindowSingleThreaded::render() +{ + QSurface *surface = m_offscreenSurface; + if (m_onscreen) + surface = this; + if (!m_context->makeCurrent(surface)) + 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_context->functions()->glFlush(); + + m_quickReady = true; + + // Get something onto the screen. + if (!m_onscreen) + m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); + else + m_context->swapBuffers(this); +} + +void WindowSingleThreaded::requestUpdate() +{ + if (!m_updateTimer.isActive()) + m_updateTimer.start(); +} + +void WindowSingleThreaded::run() +{ + disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowSingleThreaded::run); + + if (m_qmlComponent->isError()) { + const QList errorList = m_qmlComponent->errors(); + for (const QQmlError &error : errorList) + qWarning() << error.url() << error.line() << error; + return; + } + + QObject *rootObject = m_qmlComponent->create(); + if (m_qmlComponent->isError()) { + const QList errorList = m_qmlComponent->errors(); + for (const QQmlError &error : errorList) + qWarning() << error.url() << error.line() << error; + return; + } + + m_rootItem = qobject_cast(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. + QSurface *surface = m_offscreenSurface; + if (m_onscreen) + surface = this; + m_context->makeCurrent(surface); + m_renderControl->initialize(m_context); + m_quickInitialized = true; +} + +void WindowSingleThreaded::updateSizes() +{ + // Behave like SizeRootObjectToView. + m_rootItem->setWidth(width()); + m_rootItem->setHeight(height()); + + m_quickWindow->setGeometry(0, 0, width(), height()); + + m_cubeRenderer->resize(width(), height()); +} + +void WindowSingleThreaded::startQuick(const QString &filename) +{ + m_qmlComponent = new QQmlComponent(m_qmlEngine, QUrl(filename)); + if (m_qmlComponent->isLoading()) + connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowSingleThreaded::run); + else + run(); +} + +void WindowSingleThreaded::exposeEvent(QExposeEvent *) +{ + if (isExposed()) { + if (!m_quickInitialized) { + if (!m_onscreen) + m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); + startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); + } + } +} + +void WindowSingleThreaded::resizeFbo() +{ + QSurface *surface = m_offscreenSurface; + if (m_onscreen) + surface = this; + if (m_rootItem && m_context->makeCurrent(surface)) { + delete m_fbo; + createFbo(); + m_context->doneCurrent(); + updateSizes(); + render(); + } +} + +void WindowSingleThreaded::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_onscreen) { + if (m_fbo && m_fbo->size() != size() * devicePixelRatio()) + resizeFbo(); + } else { + if (m_onscreenSize != size() * devicePixelRatio()) + resizeFbo(); + } +} + +void WindowSingleThreaded::handleScreenChange() +{ + if (m_dpr != devicePixelRatio()) + resizeFbo(); +} + +void WindowSingleThreaded::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 WindowSingleThreaded::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/rendercontrol_opengl/window_singlethreaded.h b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h new file mode 100644 index 0000000000..44b79d9f51 --- /dev/null +++ b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINDOW_SINGLETHREADED_H +#define WINDOW_SINGLETHREADED_H + +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QOpenGLContext) +QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject) +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 CubeRenderer; + +class WindowSingleThreaded : public QWindow +{ + Q_OBJECT + +public: + WindowSingleThreaded(); + ~WindowSingleThreaded(); + +protected: + void exposeEvent(QExposeEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + +private slots: + void run(); + + void createFbo(); + void destroyFbo(); + void render(); + void requestUpdate(); + void handleScreenChange(); + +private: + void startQuick(const QString &filename); + void updateSizes(); + void resizeFbo(); + + QOpenGLContext *m_context; + QOffscreenSurface *m_offscreenSurface; + QQuickRenderControl *m_renderControl; + QQuickWindow *m_quickWindow; + QQmlEngine *m_qmlEngine; + QQmlComponent *m_qmlComponent; + QQuickItem *m_rootItem; + QOpenGLFramebufferObject *m_fbo; + bool m_quickInitialized; + bool m_quickReady; + QTimer m_updateTimer; + CubeRenderer *m_cubeRenderer; + qreal m_dpr; + bool m_onscreen; + QSize m_onscreenSize; +}; + +#endif diff --git a/examples/quick/rendercontrol/window_multithreaded.cpp b/examples/quick/rendercontrol/window_multithreaded.cpp deleted file mode 100644 index 01d6b8b5b2..0000000000 --- a/examples/quick/rendercontrol/window_multithreaded.cpp +++ /dev/null @@ -1,454 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd 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_multithreaded.h" -#include "cuberenderer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - This implementation runs the Qt Quick scenegraph's sync and render phases on a - separate, dedicated thread. Rendering the cube using our custom OpenGL engine - happens on that thread as well. This is similar to the built-in threaded - render loop, but does not support all the features. There is no support for - getting Animators running on the render thread for example. - - We choose to use QObject's event mechanism to communicate with the QObject - living on the render thread. An alternative would be to subclass QThread and - reimplement run() with a custom event handling approach, like - QSGThreadedRenderLoop does. That would potentially lead to better results but - is also more complex. -*/ - -static const QEvent::Type INIT = QEvent::Type(QEvent::User + 1); -static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); -static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); -static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); - -static const QEvent::Type UPDATE = QEvent::Type(QEvent::User + 5); - -QuickRenderer::QuickRenderer() - : m_context(nullptr), - m_surface(nullptr), - m_fbo(nullptr), - m_window(nullptr), - m_quickWindow(nullptr), - m_renderControl(nullptr), - m_cubeRenderer(nullptr), - m_quit(false) -{ -} - -void QuickRenderer::requestInit() -{ - QCoreApplication::postEvent(this, new QEvent(INIT)); -} - -void QuickRenderer::requestRender() -{ - QCoreApplication::postEvent(this, new QEvent(RENDER)); -} - -void QuickRenderer::requestResize() -{ - QCoreApplication::postEvent(this, new QEvent(RESIZE)); -} - -void QuickRenderer::requestStop() -{ - QCoreApplication::postEvent(this, new QEvent(STOP)); -} - -bool QuickRenderer::event(QEvent *e) -{ - QMutexLocker lock(&m_mutex); - - switch (int(e->type())) { - case INIT: - init(); - return true; - case RENDER: - render(&lock); - return true; - case RESIZE: - if (m_cubeRenderer) - m_cubeRenderer->resize(m_window->width(), m_window->height()); - return true; - case STOP: - cleanup(); - return true; - default: - return QObject::event(e); - } -} - -void QuickRenderer::init() -{ - m_context->makeCurrent(m_surface); - - // Pass our offscreen surface to the cube renderer just so that it will - // have something is can make current during cleanup. QOffscreenSurface, - // just like QWindow, must always be created on the gui thread (as it might - // be backed by an actual QWindow). - m_cubeRenderer = new CubeRenderer(m_surface); - m_cubeRenderer->resize(m_window->width(), m_window->height()); - - m_renderControl->initialize(m_context); -} - -void QuickRenderer::cleanup() -{ - m_context->makeCurrent(m_surface); - - m_renderControl->invalidate(); - - delete m_fbo; - m_fbo = nullptr; - - delete m_cubeRenderer; - m_cubeRenderer = nullptr; - - m_context->doneCurrent(); - m_context->moveToThread(QCoreApplication::instance()->thread()); - - m_cond.wakeOne(); -} - -void QuickRenderer::ensureFbo() -{ - if (m_fbo && m_fbo->size() != m_window->size() * m_window->devicePixelRatio()) { - delete m_fbo; - m_fbo = nullptr; - } - - if (!m_fbo) { - m_fbo = new QOpenGLFramebufferObject(m_window->size() * m_window->devicePixelRatio(), - QOpenGLFramebufferObject::CombinedDepthStencil); - m_quickWindow->setRenderTarget(m_fbo); - } -} - -void QuickRenderer::render(QMutexLocker *lock) -{ - Q_ASSERT(QThread::currentThread() != m_window->thread()); - - if (!m_context->makeCurrent(m_surface)) { - qWarning("Failed to make context current on render thread"); - return; - } - - ensureFbo(); - - // Synchronization and rendering happens here on the render thread. - m_renderControl->sync(); - - // The gui thread can now continue. - m_cond.wakeOne(); - lock->unlock(); - - // Meanwhile on this thread continue with the actual rendering (into the FBO first). - m_renderControl->render(); - m_context->functions()->glFlush(); - - // The cube renderer uses its own context, no need to bother with the state here. - - // Get something onto the screen using our custom OpenGL engine. - QMutexLocker quitLock(&m_quitMutex); - if (!m_quit) - m_cubeRenderer->render(m_window, m_context, m_fbo->texture()); -} - -void QuickRenderer::aboutToQuit() -{ - QMutexLocker lock(&m_quitMutex); - m_quit = true; -} - -class RenderControl : public QQuickRenderControl -{ -public: - RenderControl(QWindow *w) : m_window(w) { } - QWindow *renderWindow(QPoint *offset) override; - -private: - QWindow *m_window; -}; - -WindowMultiThreaded::WindowMultiThreaded() - : m_qmlComponent(nullptr), - m_rootItem(nullptr), - m_quickInitialized(false), - m_psrRequested(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 RenderControl(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()); - - m_quickRenderer = new QuickRenderer; - m_quickRenderer->setContext(m_context); - - // These live on the gui thread. Just give access to them on the render thread. - m_quickRenderer->setSurface(m_offscreenSurface); - m_quickRenderer->setWindow(this); - m_quickRenderer->setQuickWindow(m_quickWindow); - m_quickRenderer->setRenderControl(m_renderControl); - - m_quickRendererThread = new QThread; - - // Notify the render control that some scenegraph internals have to live on - // m_quickRenderThread. - m_renderControl->prepareThread(m_quickRendererThread); - - // The QOpenGLContext and the QObject representing the rendering logic on - // the render thread must live on that thread. - m_context->moveToThread(m_quickRendererThread); - m_quickRenderer->moveToThread(m_quickRendererThread); - - m_quickRendererThread->start(); - - // 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_renderControl, &QQuickRenderControl::renderRequested, this, &WindowMultiThreaded::requestUpdate); - connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, &WindowMultiThreaded::requestUpdate); -} - -WindowMultiThreaded::~WindowMultiThreaded() -{ - // Release resources and move the context ownership back to this thread. - m_quickRenderer->mutex()->lock(); - m_quickRenderer->requestStop(); - m_quickRenderer->cond()->wait(m_quickRenderer->mutex()); - m_quickRenderer->mutex()->unlock(); - - m_quickRendererThread->quit(); - m_quickRendererThread->wait(); - - delete m_renderControl; - delete m_qmlComponent; - delete m_quickWindow; - delete m_qmlEngine; - - delete m_offscreenSurface; - delete m_context; -} - -void WindowMultiThreaded::requestUpdate() -{ - if (m_quickInitialized && !m_psrRequested) { - m_psrRequested = true; - QCoreApplication::postEvent(this, new QEvent(UPDATE)); - } -} - -bool WindowMultiThreaded::event(QEvent *e) -{ - if (e->type() == UPDATE) { - polishSyncAndRender(); - m_psrRequested = false; - return true; - } else if (e->type() == QEvent::Close) { - // Avoid rendering on the render thread when the window is about to - // close. Once a QWindow is closed, the underlying platform window will - // go away, even though the QWindow instance itself is still - // valid. Operations like swapBuffers() are futile and only result in - // warnings afterwards. Prevent this. - m_quickRenderer->aboutToQuit(); - } - return QWindow::event(e); -} - -void WindowMultiThreaded::polishSyncAndRender() -{ - Q_ASSERT(QThread::currentThread() == thread()); - - // Polishing happens on the gui thread. - m_renderControl->polishItems(); - // Sync happens on the render thread with the gui thread (this one) blocked. - QMutexLocker lock(m_quickRenderer->mutex()); - m_quickRenderer->requestRender(); - // Wait until sync is complete. - m_quickRenderer->cond()->wait(m_quickRenderer->mutex()); - // Rendering happens on the render thread without blocking the gui (main) - // thread. This is good because the blocking swap (waiting for vsync) - // happens on the render thread, not blocking other work. -} - -void WindowMultiThreaded::run() -{ - disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowMultiThreaded::run); - - if (m_qmlComponent->isError()) { - const QList errorList = m_qmlComponent->errors(); - for (const QQmlError &error : errorList) - qWarning() << error.url() << error.line() << error; - return; - } - - QObject *rootObject = m_qmlComponent->create(); - if (m_qmlComponent->isError()) { - const QList errorList = m_qmlComponent->errors(); - for (const QQmlError &error : errorList) - qWarning() << error.url() << error.line() << error; - return; - } - - m_rootItem = qobject_cast(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(); - - m_quickInitialized = true; - - // Initialize the render thread and perform the first polish/sync/render. - m_quickRenderer->requestInit(); - polishSyncAndRender(); -} - -void WindowMultiThreaded::updateSizes() -{ - // Behave like SizeRootObjectToView. - m_rootItem->setWidth(width()); - m_rootItem->setHeight(height()); - - m_quickWindow->setGeometry(0, 0, width(), height()); -} - -void WindowMultiThreaded::startQuick(const QString &filename) -{ - m_qmlComponent = new QQmlComponent(m_qmlEngine, QUrl(filename)); - if (m_qmlComponent->isLoading()) - connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowMultiThreaded::run); - else - run(); -} - -void WindowMultiThreaded::exposeEvent(QExposeEvent *) -{ - if (isExposed()) { - if (!m_quickInitialized) - startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); - } -} - -void WindowMultiThreaded::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) { - updateSizes(); - m_quickRenderer->requestResize(); - polishSyncAndRender(); - } -} - -void WindowMultiThreaded::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 WindowMultiThreaded::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_multithreaded.h b/examples/quick/rendercontrol/window_multithreaded.h deleted file mode 100644 index ded80a0064..0000000000 --- a/examples/quick/rendercontrol/window_multithreaded.h +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef WINDOW_MULTITHREADED_H -#define WINDOW_MULTITHREADED_H - -#include -#include -#include -#include -#include - -QT_FORWARD_DECLARE_CLASS(QOpenGLContext) -QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject) -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 CubeRenderer; - -class QuickRenderer : public QObject -{ - Q_OBJECT - -public: - QuickRenderer(); - - void requestInit(); - void requestRender(); - void requestResize(); - void requestStop(); - - QWaitCondition *cond() { return &m_cond; } - QMutex *mutex() { return &m_mutex; } - - void setContext(QOpenGLContext *ctx) { m_context = ctx; } - void setSurface(QOffscreenSurface *s) { m_surface = s; } - void setWindow(QWindow *w) { m_window = w; } - void setQuickWindow(QQuickWindow *w) { m_quickWindow = w; } - void setRenderControl(QQuickRenderControl *r) { m_renderControl = r; } - - void aboutToQuit(); - -private: - bool event(QEvent *e) override; - void init(); - void cleanup(); - void ensureFbo(); - void render(QMutexLocker *lock); - - QWaitCondition m_cond; - QMutex m_mutex; - QOpenGLContext *m_context; - QOffscreenSurface *m_surface; - QOpenGLFramebufferObject *m_fbo; - QWindow *m_window; - QQuickWindow *m_quickWindow; - QQuickRenderControl *m_renderControl; - CubeRenderer *m_cubeRenderer; - QMutex m_quitMutex; - bool m_quit; -}; - -class WindowMultiThreaded : public QWindow -{ - Q_OBJECT - -public: - WindowMultiThreaded(); - ~WindowMultiThreaded(); - -protected: - void exposeEvent(QExposeEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - bool event(QEvent *e) override; - -private slots: - void run(); - void requestUpdate(); - void polishSyncAndRender(); - -private: - void startQuick(const QString &filename); - void updateSizes(); - - QuickRenderer *m_quickRenderer; - QThread *m_quickRendererThread; - - QOpenGLContext *m_context; - QOffscreenSurface *m_offscreenSurface; - QQuickRenderControl *m_renderControl; - QQuickWindow *m_quickWindow; - QQmlEngine *m_qmlEngine; - QQmlComponent *m_qmlComponent; - QQuickItem *m_rootItem; - bool m_quickInitialized; - bool m_psrRequested; -}; - -#endif diff --git a/examples/quick/rendercontrol/window_singlethreaded.cpp b/examples/quick/rendercontrol/window_singlethreaded.cpp deleted file mode 100644 index ddbbfe4b52..0000000000 --- a/examples/quick/rendercontrol/window_singlethreaded.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd 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_singlethreaded.h" -#include "cuberenderer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class RenderControl : public QQuickRenderControl -{ -public: - RenderControl(QWindow *w) : m_window(w) { } - QWindow *renderWindow(QPoint *offset) override; - -private: - QWindow *m_window; -}; - -QWindow *RenderControl::renderWindow(QPoint *offset) -{ - if (offset) - *offset = QPoint(0, 0); - return m_window; -} - -WindowSingleThreaded::WindowSingleThreaded() - : m_rootItem(nullptr), - m_fbo(nullptr), - m_quickInitialized(false), - m_quickReady(false), - m_dpr(0) -{ - setSurfaceType(QSurface::OpenGLSurface); - - // The rendercontrol does not necessarily need an FBO. Demonstrate this - // when requested. - m_onscreen = QCoreApplication::arguments().contains(QStringLiteral("--onscreen")); - - 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_cubeRenderer = new CubeRenderer(m_offscreenSurface); - - m_renderControl = new RenderControl(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, &WindowSingleThreaded::render); - - // 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, &WindowSingleThreaded::createFbo); - connect(m_quickWindow, &QQuickWindow::sceneGraphInvalidated, this, &WindowSingleThreaded::destroyFbo); - connect(m_renderControl, &QQuickRenderControl::renderRequested, this, &WindowSingleThreaded::requestUpdate); - connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, &WindowSingleThreaded::requestUpdate); - - // Just recreating the FBO on resize is not sufficient, when moving between screens - // with different devicePixelRatio the QWindow size may remain the same but the FBO - // dimension is to change regardless. - connect(this, &QWindow::screenChanged, this, &WindowSingleThreaded::handleScreenChange); -} - -WindowSingleThreaded::~WindowSingleThreaded() -{ - // Make sure the context is current while doing cleanup. Note that we use the - // offscreen surface here because passing 'this' at this point is not safe: the - // underlying platform window may already be destroyed. To avoid all the trouble, use - // another surface that is valid for sure. - m_context->makeCurrent(m_offscreenSurface); - - // 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; - - m_context->doneCurrent(); - - delete m_cubeRenderer; - - delete m_offscreenSurface; - delete m_context; -} - -void WindowSingleThreaded::createFbo() -{ - // The scene graph has been initialized. It is now time to create an FBO and associate - // it with the QQuickWindow. - m_dpr = devicePixelRatio(); - if (!m_onscreen) { - m_fbo = new QOpenGLFramebufferObject(size() * m_dpr, QOpenGLFramebufferObject::CombinedDepthStencil); - m_quickWindow->setRenderTarget(m_fbo); - } else { - // Special case: No FBO. Render directly to the window's default framebuffer. - m_onscreenSize = size() * m_dpr; - m_quickWindow->setRenderTarget(0, m_onscreenSize); - } -} - -void WindowSingleThreaded::destroyFbo() -{ - delete m_fbo; - m_fbo = nullptr; -} - -void WindowSingleThreaded::render() -{ - QSurface *surface = m_offscreenSurface; - if (m_onscreen) - surface = this; - if (!m_context->makeCurrent(surface)) - 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_context->functions()->glFlush(); - - m_quickReady = true; - - // Get something onto the screen. - if (!m_onscreen) - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); - else - m_context->swapBuffers(this); -} - -void WindowSingleThreaded::requestUpdate() -{ - if (!m_updateTimer.isActive()) - m_updateTimer.start(); -} - -void WindowSingleThreaded::run() -{ - disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowSingleThreaded::run); - - if (m_qmlComponent->isError()) { - const QList errorList = m_qmlComponent->errors(); - for (const QQmlError &error : errorList) - qWarning() << error.url() << error.line() << error; - return; - } - - QObject *rootObject = m_qmlComponent->create(); - if (m_qmlComponent->isError()) { - const QList errorList = m_qmlComponent->errors(); - for (const QQmlError &error : errorList) - qWarning() << error.url() << error.line() << error; - return; - } - - m_rootItem = qobject_cast(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. - QSurface *surface = m_offscreenSurface; - if (m_onscreen) - surface = this; - m_context->makeCurrent(surface); - m_renderControl->initialize(m_context); - m_quickInitialized = true; -} - -void WindowSingleThreaded::updateSizes() -{ - // Behave like SizeRootObjectToView. - m_rootItem->setWidth(width()); - m_rootItem->setHeight(height()); - - m_quickWindow->setGeometry(0, 0, width(), height()); - - m_cubeRenderer->resize(width(), height()); -} - -void WindowSingleThreaded::startQuick(const QString &filename) -{ - m_qmlComponent = new QQmlComponent(m_qmlEngine, QUrl(filename)); - if (m_qmlComponent->isLoading()) - connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowSingleThreaded::run); - else - run(); -} - -void WindowSingleThreaded::exposeEvent(QExposeEvent *) -{ - if (isExposed()) { - if (!m_quickInitialized) { - if (!m_onscreen) - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); - startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); - } - } -} - -void WindowSingleThreaded::resizeFbo() -{ - QSurface *surface = m_offscreenSurface; - if (m_onscreen) - surface = this; - if (m_rootItem && m_context->makeCurrent(surface)) { - delete m_fbo; - createFbo(); - m_context->doneCurrent(); - updateSizes(); - render(); - } -} - -void WindowSingleThreaded::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_onscreen) { - if (m_fbo && m_fbo->size() != size() * devicePixelRatio()) - resizeFbo(); - } else { - if (m_onscreenSize != size() * devicePixelRatio()) - resizeFbo(); - } -} - -void WindowSingleThreaded::handleScreenChange() -{ - if (m_dpr != devicePixelRatio()) - resizeFbo(); -} - -void WindowSingleThreaded::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 WindowSingleThreaded::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_singlethreaded.h b/examples/quick/rendercontrol/window_singlethreaded.h deleted file mode 100644 index 44b79d9f51..0000000000 --- a/examples/quick/rendercontrol/window_singlethreaded.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, 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 The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef WINDOW_SINGLETHREADED_H -#define WINDOW_SINGLETHREADED_H - -#include -#include -#include - -QT_FORWARD_DECLARE_CLASS(QOpenGLContext) -QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject) -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 CubeRenderer; - -class WindowSingleThreaded : public QWindow -{ - Q_OBJECT - -public: - WindowSingleThreaded(); - ~WindowSingleThreaded(); - -protected: - void exposeEvent(QExposeEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - -private slots: - void run(); - - void createFbo(); - void destroyFbo(); - void render(); - void requestUpdate(); - void handleScreenChange(); - -private: - void startQuick(const QString &filename); - void updateSizes(); - void resizeFbo(); - - QOpenGLContext *m_context; - QOffscreenSurface *m_offscreenSurface; - QQuickRenderControl *m_renderControl; - QQuickWindow *m_quickWindow; - QQmlEngine *m_qmlEngine; - QQmlComponent *m_qmlComponent; - QQuickItem *m_rootItem; - QOpenGLFramebufferObject *m_fbo; - bool m_quickInitialized; - bool m_quickReady; - QTimer m_updateTimer; - CubeRenderer *m_cubeRenderer; - qreal m_dpr; - bool m_onscreen; - QSize m_onscreenSize; -}; - -#endif -- cgit v1.2.3