aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@theqtcompany.com>2014-11-25 11:38:46 +0100
committerLaszlo Agocs <laszlo.agocs@theqtcompany.com>2014-12-12 12:19:14 +0100
commit6179550a0ca761bfabd4f6c67103f5397a306df0 (patch)
treeeaf4a12431769a77ff3fed7945d1d0bf37144fcd /examples/quick
parent2a6f6eee104ef66e4e236fa82fe71bb77f151ee8 (diff)
Support threading with QQuickRenderControl
Reorganize the rendercontrol example to demonstrate both the single and multi threaded approaches. A small helper function is introduced to the QQuickRenderControl API: The QSGRenderContext has to live on the render thread. Previously there was no way for applications to move it to the desired thread. This is now possible. Pass --threaded to the rendercontrol example to use a separate render thread. [ChangeLog][QtQuick] QQuickRenderControl can now be used to render the Qt Quick scene on a dedicated render thread, similarly to how the built-in threaded render loop operates. Task-number: QTBUG-42813 Change-Id: I01c3b2ffca8a174d9d2c267a51f2e484ed7b34b3 Reviewed-by: Gunnar Sletta <gunnar@sletta.org> Reviewed-by: Jørgen Lind <jorgen.lind@theqtcompany.com>
Diffstat (limited to 'examples/quick')
-rw-r--r--examples/quick/rendercontrol/cuberenderer.cpp216
-rw-r--r--examples/quick/rendercontrol/cuberenderer.h73
-rw-r--r--examples/quick/rendercontrol/main.cpp19
-rw-r--r--examples/quick/rendercontrol/rendercontrol.pro10
-rw-r--r--examples/quick/rendercontrol/window_multithreaded.cpp423
-rw-r--r--examples/quick/rendercontrol/window_multithreaded.h142
-rw-r--r--examples/quick/rendercontrol/window_singlethreaded.cpp (renamed from examples/quick/rendercontrol/window.cpp)229
-rw-r--r--examples/quick/rendercontrol/window_singlethreaded.h (renamed from examples/quick/rendercontrol/window.h)27
8 files changed, 938 insertions, 201 deletions
diff --git a/examples/quick/rendercontrol/cuberenderer.cpp b/examples/quick/rendercontrol/cuberenderer.cpp
new file mode 100644
index 0000000000..c385d46533
--- /dev/null
+++ b/examples/quick/rendercontrol/cuberenderer.cpp
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "cuberenderer.h"
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QOpenGLVertexArrayObject>
+#include <QOffscreenSurface>
+#include <QWindow>
+
+CubeRenderer::CubeRenderer()
+ : m_context(0),
+ m_program(0),
+ m_vbo(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.
+ QScopedPointer<QOffscreenSurface> offscreenSurface(new QOffscreenSurface);
+ offscreenSurface->setFormat(m_context->format());
+ offscreenSurface->create();
+ m_context->makeCurrent(offscreenSurface.data());
+
+ 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->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
+ m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
+ m_program->bindAttributeLocation("vertex", 0);
+ m_program->bindAttributeLocation("coord", 1);
+ m_program->link();
+ m_matrixLoc = m_program->uniformLocation("matrix");
+
+ m_vao = new QOpenGLVertexArrayObject;
+ m_vao->create();
+ 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, 0);
+ 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
new file mode 100644
index 0000000000..8f98376fdf
--- /dev/null
+++ b/examples/quick/rendercontrol/cuberenderer.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUBERENDERER_H
+#define CUBERENDERER_H
+
+#include <QMatrix4x4>
+
+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)
+
+class CubeRenderer
+{
+public:
+ CubeRenderer();
+ ~CubeRenderer();
+
+ void resize(int w, int h);
+ void render(QWindow *w, QOpenGLContext *share, uint texture);
+
+private:
+ void init(QWindow *w, QOpenGLContext *share);
+ void setupVertexAttribs();
+
+ 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/main.cpp b/examples/quick/rendercontrol/main.cpp
index d362278ddf..e61eb110aa 100644
--- a/examples/quick/rendercontrol/main.cpp
+++ b/examples/quick/rendercontrol/main.cpp
@@ -39,13 +39,24 @@
****************************************************************************/
#include <QGuiApplication>
-#include "window.h"
+#include "window_singlethreaded.h"
+#include "window_multithreaded.h"
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
- Window window;
- window.resize(1024, 768);
- window.show();
+
+ QScopedPointer<QWindow> window;
+ if (QCoreApplication::arguments().contains(QLatin1String("--threaded"))) {
+ 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 ed25a11c1b..3301a773a4 100644
--- a/examples/quick/rendercontrol/rendercontrol.pro
+++ b/examples/quick/rendercontrol/rendercontrol.pro
@@ -2,8 +2,14 @@ TEMPLATE = app
QT += quick qml
-SOURCES += main.cpp window.cpp
-HEADERS += window.h
+SOURCES += main.cpp \
+ window_singlethreaded.cpp \
+ window_multithreaded.cpp \
+ cuberenderer.cpp
+
+HEADERS += window_singlethreaded.h \
+ window_multithreaded.h \
+ cuberenderer.h
RESOURCES += rendercontrol.qrc
diff --git a/examples/quick/rendercontrol/window_multithreaded.cpp b/examples/quick/rendercontrol/window_multithreaded.cpp
new file mode 100644
index 0000000000..b59b0f59da
--- /dev/null
+++ b/examples/quick/rendercontrol/window_multithreaded.cpp
@@ -0,0 +1,423 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "window_multithreaded.h"
+#include "cuberenderer.h"
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QOpenGLFramebufferObject>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QOpenGLVertexArrayObject>
+#include <QOffscreenSurface>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QQuickItem>
+#include <QQuickWindow>
+#include <QQuickRenderControl>
+#include <QCoreApplication>
+
+/*
+ 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_fbo(0),
+ m_cubeRenderer(0),
+ 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);
+
+ m_cubeRenderer = new CubeRenderer;
+ 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 = 0;
+
+ delete m_cubeRenderer;
+ m_cubeRenderer = 0;
+
+ 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 = 0;
+ }
+
+ 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();
+
+ // 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;
+}
+
+WindowMultiThreaded::WindowMultiThreaded()
+ : m_rootItem(0),
+ 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 QQuickRenderControl(this);
+
+ // Create a QQuickWindow that is associated with out render control. Note that this
+ // window never gets created or shown, meaning that it will never get an underlying
+ // native (platform) window.
+ m_quickWindow = new QQuickWindow(m_renderControl);
+
+ // Create a QML engine.
+ m_qmlEngine = new QQmlEngine;
+ if (!m_qmlEngine->incubationController())
+ m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
+
+ 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, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(run()));
+
+ if (m_qmlComponent->isError()) {
+ QList<QQmlError> errorList = m_qmlComponent->errors();
+ foreach (const QQmlError &error, errorList)
+ qWarning() << error.url() << error.line() << error;
+ return;
+ }
+
+ QObject *rootObject = m_qmlComponent->create();
+ if (m_qmlComponent->isError()) {
+ QList<QQmlError> errorList = m_qmlComponent->errors();
+ foreach (const QQmlError &error, errorList)
+ qWarning() << error.url() << error.line() << error;
+ return;
+ }
+
+ m_rootItem = qobject_cast<QQuickItem *>(rootObject);
+ if (!m_rootItem) {
+ qWarning("run: Not a QQuickItem");
+ delete rootObject;
+ return;
+ }
+
+ // The root item is ready. Associate it with the window.
+ m_rootItem->setParentItem(m_quickWindow->contentItem());
+
+ // Update item and rendering related geometries.
+ updateSizes();
+
+ 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
new file mode 100644
index 0000000000..74eab1a963
--- /dev/null
+++ b/examples/quick/rendercontrol/window_multithreaded.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WINDOW_MULTITHREADED_H
+#define WINDOW_MULTITHREADED_H
+
+#include <QWindow>
+#include <QMatrix4x4>
+#include <QThread>
+#include <QWaitCondition>
+#include <QMutex>
+
+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) Q_DECL_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) Q_DECL_OVERRIDE;
+ void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE;
+ void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
+ void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
+ bool event(QEvent *e) Q_DECL_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.cpp b/examples/quick/rendercontrol/window_singlethreaded.cpp
index 4813936538..993c508c0a 100644
--- a/examples/quick/rendercontrol/window.cpp
+++ b/examples/quick/rendercontrol/window_singlethreaded.cpp
@@ -38,7 +38,8 @@
**
****************************************************************************/
-#include "window.h"
+#include "window_singlethreaded.h"
+#include "cuberenderer.h"
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFramebufferObject>
@@ -54,11 +55,9 @@
#include <QQuickRenderControl>
#include <QCoreApplication>
-Window::Window()
+WindowSingleThreaded::WindowSingleThreaded()
: m_rootItem(0),
m_fbo(0),
- m_program(0),
- m_vbo(0),
m_quickInitialized(false),
m_quickReady(false)
{
@@ -82,6 +81,8 @@ Window::Window()
m_offscreenSurface->setFormat(m_context->format());
m_offscreenSurface->create();
+ m_cubeRenderer = new CubeRenderer;
+
m_renderControl = new QQuickRenderControl(this);
// Create a QQuickWindow that is associated with out render control. Note that this
@@ -98,18 +99,18 @@ Window::Window()
// a timer with a small interval is used to get better performance.
m_updateTimer.setSingleShot(true);
m_updateTimer.setInterval(5);
- connect(&m_updateTimer, &QTimer::timeout, this, &Window::updateQuick);
+ 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, &Window::createFbo);
- connect(m_quickWindow, &QQuickWindow::sceneGraphInvalidated, this, &Window::destroyFbo);
- connect(m_renderControl, &QQuickRenderControl::renderRequested, this, &Window::requestUpdate);
- connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, &Window::requestUpdate);
+ 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);
}
-Window::~Window()
+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
@@ -125,17 +126,16 @@ Window::~Window()
delete m_quickWindow;
delete m_qmlEngine;
delete m_fbo;
- delete m_program;
- delete m_vbo;
- delete m_vao;
m_context->doneCurrent();
delete m_offscreenSurface;
delete m_context;
+
+ delete m_cubeRenderer;
}
-void Window::createFbo()
+void WindowSingleThreaded::createFbo()
{
// The scene graph has been initialized. It is now time to create an FBO and associate
// it with the QQuickWindow.
@@ -143,19 +143,41 @@ void Window::createFbo()
m_quickWindow->setRenderTarget(m_fbo);
}
-void Window::destroyFbo()
+void WindowSingleThreaded::destroyFbo()
{
delete m_fbo;
m_fbo = 0;
}
-void Window::requestUpdate()
+void WindowSingleThreaded::render()
+{
+ if (!m_context->makeCurrent(m_offscreenSurface))
+ return;
+
+ // Polish, synchronize and render the next frame (into our fbo). In this example
+ // everything happens on the same thread and therefore all three steps are performed
+ // in succession from here. In a threaded setup the render() call would happen on a
+ // separate thread.
+ m_renderControl->polishItems();
+ m_renderControl->sync();
+ m_renderControl->render();
+
+ m_quickWindow->resetOpenGLState();
+ QOpenGLFramebufferObject::bindDefault();
+
+ m_quickReady = true;
+
+ // Get something onto the screen.
+ m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0);
+}
+
+void WindowSingleThreaded::requestUpdate()
{
if (!m_updateTimer.isActive())
m_updateTimer.start();
}
-void Window::run()
+void WindowSingleThreaded::run()
{
disconnect(m_qmlComponent, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(run()));
@@ -190,89 +212,10 @@ void Window::run()
// Initialize the render control and our OpenGL resources.
m_context->makeCurrent(m_offscreenSurface);
m_renderControl->initialize(m_context);
-
- static const char *vertexShaderSource =
- "attribute highp vec4 vertex;\n"
- "attribute lowp vec2 coord;\n"
- "varying lowp vec2 v_coord;\n"
- "uniform highp mat4 matrix;\n"
- "void main() {\n"
- " v_coord = coord;\n"
- " gl_Position = matrix * vertex;\n"
- "}\n";
- static const char *fragmentShaderSource =
- "varying lowp vec2 v_coord;\n"
- "uniform sampler2D sampler;\n"
- "void main() {\n"
- " gl_FragColor = vec4(texture2D(sampler, v_coord).rgb, 1.0);\n"
- "}\n";
- m_program = new QOpenGLShaderProgram;
- m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
- m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
- m_program->bindAttributeLocation("vertex", 0);
- m_program->bindAttributeLocation("coord", 1);
- m_program->link();
- m_matrixLoc = m_program->uniformLocation("matrix");
-
- m_vao = new QOpenGLVertexArrayObject;
- m_vao->create();
- m_vao->bind();
-
- m_vbo = new QOpenGLBuffer;
- m_vbo->create();
- m_vbo->bind();
-
- GLfloat v[] = {
- -0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5,
- 0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5,
- -0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5,
- 0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5,
-
- 0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5,
- 0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5,
- -0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5,
- -0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5,
-
- 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
- -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5,
- -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5,
- 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5
- };
- GLfloat texCoords[] = {
- 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
- 1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
- 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
- 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
-
- 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
- 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
- 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
- 1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
-
- 0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f,
- 1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f,
- 1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f,
- 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f
- };
-
- const int vertexCount = 36;
- m_vbo->allocate(sizeof(GLfloat) * vertexCount * 5);
- m_vbo->write(0, v, sizeof(GLfloat) * vertexCount * 3);
- m_vbo->write(sizeof(GLfloat) * vertexCount * 3, texCoords, sizeof(GLfloat) * vertexCount * 2);
- m_vbo->release();
-
- if (m_vao->isCreated())
- setupVertexAttribs();
-
- // Must unbind before changing the current context. Hence the absence of
- // QOpenGLVertexArrayObject::Binder here.
- m_vao->release();
-
- m_context->doneCurrent();
m_quickInitialized = true;
}
-void Window::updateSizes()
+void WindowSingleThreaded::updateSizes()
{
// Behave like SizeRootObjectToView.
m_rootItem->setWidth(width());
@@ -280,40 +223,28 @@ void Window::updateSizes()
m_quickWindow->setGeometry(0, 0, width(), height());
- m_proj.setToIdentity();
- m_proj.perspective(45, width() / float(height()), 0.01f, 100.0f);
-}
-
-void Window::setupVertexAttribs()
-{
- m_vbo->bind();
- m_program->enableAttributeArray(0);
- m_program->enableAttributeArray(1);
- m_context->functions()->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
- m_context->functions()->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0,
- (const void *)(36 * 3 * sizeof(GLfloat)));
- m_vbo->release();
+ m_cubeRenderer->resize(width(), height());
}
-void Window::startQuick(const QString &filename)
+void WindowSingleThreaded::startQuick(const QString &filename)
{
m_qmlComponent = new QQmlComponent(m_qmlEngine, QUrl(filename));
if (m_qmlComponent->isLoading())
- connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &Window::run);
+ connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &WindowSingleThreaded::run);
else
run();
}
-void Window::exposeEvent(QExposeEvent *)
+void WindowSingleThreaded::exposeEvent(QExposeEvent *)
{
if (isExposed()) {
- render();
+ m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0);
if (!m_quickInitialized)
startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml"));
}
}
-void Window::resizeEvent(QResizeEvent *)
+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.
@@ -325,71 +256,7 @@ void Window::resizeEvent(QResizeEvent *)
}
}
-void Window::updateQuick()
-{
- if (!m_context->makeCurrent(m_offscreenSurface))
- return;
-
- // Polish, synchronize and render the next frame (into our fbo). In this example
- // everything happens on the same thread and therefore all three steps are performed
- // in succession from here. In a threaded setup the render() call would happen on a
- // separate thread.
- m_renderControl->polishItems();
- m_renderControl->sync();
- m_renderControl->render();
-
- m_quickWindow->resetOpenGLState();
- QOpenGLFramebufferObject::bindDefault();
-
- m_quickReady = true;
-
- // Get something onto the screen.
- render();
-}
-
-void Window::render()
-{
- if (!m_context->makeCurrent(this))
- return;
-
- QOpenGLFunctions *f = m_context->functions();
- f->glClearColor(0.0f, 0.1f, 0.25f, 1.0f);
- f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- if (m_quickReady) {
- f->glFrontFace(GL_CW); // because our cube's vertex data is such
- f->glEnable(GL_CULL_FACE);
- f->glEnable(GL_DEPTH_TEST);
-
- f->glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
-
- m_program->bind();
- QOpenGLVertexArrayObject::Binder vaoBinder(m_vao);
- // If VAOs are not supported, set the vertex attributes every time.
- if (!m_vao->isCreated())
- setupVertexAttribs();
-
- static GLfloat angle = 0;
- QMatrix4x4 m;
- m.translate(0, 0, -2);
- m.rotate(90, 0, 0, 1);
- m.rotate(angle, 0.5, 1, 0);
- angle += 0.5f;
-
- m_program->setUniformValue(m_matrixLoc, m_proj * m);
-
- // Draw the cube.
- f->glDrawArrays(GL_TRIANGLES, 0, 36);
-
- m_program->release();
- f->glDisable(GL_DEPTH_TEST);
- f->glDisable(GL_CULL_FACE);
- }
-
- m_context->swapBuffers(this);
-}
-
-void Window::mousePressEvent(QMouseEvent *e)
+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
@@ -399,7 +266,7 @@ void Window::mousePressEvent(QMouseEvent *e)
QCoreApplication::sendEvent(m_quickWindow, &mappedEvent);
}
-void Window::mouseReleaseEvent(QMouseEvent *e)
+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.h b/examples/quick/rendercontrol/window_singlethreaded.h
index 2723aeb011..3ff98423ac 100644
--- a/examples/quick/rendercontrol/window.h
+++ b/examples/quick/rendercontrol/window_singlethreaded.h
@@ -38,15 +38,15 @@
**
****************************************************************************/
+#ifndef WINDOW_SINGLETHREADED_H
+#define WINDOW_SINGLETHREADED_H
+
#include <QWindow>
#include <QMatrix4x4>
#include <QTimer>
QT_FORWARD_DECLARE_CLASS(QOpenGLContext)
QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject)
-QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
-QT_FORWARD_DECLARE_CLASS(QOpenGLBuffer)
-QT_FORWARD_DECLARE_CLASS(QOpenGLVertexArrayObject)
QT_FORWARD_DECLARE_CLASS(QOffscreenSurface)
QT_FORWARD_DECLARE_CLASS(QQuickRenderControl)
QT_FORWARD_DECLARE_CLASS(QQuickWindow)
@@ -54,13 +54,15 @@ QT_FORWARD_DECLARE_CLASS(QQmlEngine)
QT_FORWARD_DECLARE_CLASS(QQmlComponent)
QT_FORWARD_DECLARE_CLASS(QQuickItem)
-class Window : public QWindow
+class CubeRenderer;
+
+class WindowSingleThreaded : public QWindow
{
Q_OBJECT
public:
- Window();
- ~Window();
+ WindowSingleThreaded();
+ ~WindowSingleThreaded();
protected:
void exposeEvent(QExposeEvent *e) Q_DECL_OVERRIDE;
@@ -69,16 +71,15 @@ protected:
void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
private slots:
- void render();
- void updateQuick();
void run();
+
void createFbo();
void destroyFbo();
+ void render();
void requestUpdate();
private:
void startQuick(const QString &filename);
- void setupVertexAttribs();
void updateSizes();
QOpenGLContext *m_context;
@@ -89,12 +90,10 @@ private:
QQmlComponent *m_qmlComponent;
QQuickItem *m_rootItem;
QOpenGLFramebufferObject *m_fbo;
- QOpenGLShaderProgram *m_program;
- QOpenGLBuffer *m_vbo;
- QOpenGLVertexArrayObject *m_vao;
bool m_quickInitialized;
bool m_quickReady;
- int m_matrixLoc;
- QMatrix4x4 m_proj;
QTimer m_updateTimer;
+ CubeRenderer *m_cubeRenderer;
};
+
+#endif