diff options
author | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2014-11-25 11:38:46 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2014-12-12 12:19:14 +0100 |
commit | 6179550a0ca761bfabd4f6c67103f5397a306df0 (patch) | |
tree | eaf4a12431769a77ff3fed7945d1d0bf37144fcd /examples/quick/rendercontrol | |
parent | 2a6f6eee104ef66e4e236fa82fe71bb77f151ee8 (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/rendercontrol')
-rw-r--r-- | examples/quick/rendercontrol/cuberenderer.cpp | 216 | ||||
-rw-r--r-- | examples/quick/rendercontrol/cuberenderer.h | 73 | ||||
-rw-r--r-- | examples/quick/rendercontrol/main.cpp | 19 | ||||
-rw-r--r-- | examples/quick/rendercontrol/rendercontrol.pro | 10 | ||||
-rw-r--r-- | examples/quick/rendercontrol/window_multithreaded.cpp | 423 | ||||
-rw-r--r-- | examples/quick/rendercontrol/window_multithreaded.h | 142 | ||||
-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 |