diff options
Diffstat (limited to 'examples')
7 files changed, 68 insertions, 710 deletions
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt b/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt index fb1c6ecf12..310f721f2e 100644 --- a/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt +++ b/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt @@ -15,16 +15,17 @@ find_package(Qt6 COMPONENTS Core) find_package(Qt6 COMPONENTS Gui) find_package(Qt6 COMPONENTS Quick) find_package(Qt6 COMPONENTS Qml) +find_package(Qt6 COMPONENTS OpenGL) 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::OpenGL Qt::Qml Qt::Quick ) diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp index 71903045cc..7a24d70ec5 100644 --- a/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp +++ b/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp @@ -49,38 +49,19 @@ ****************************************************************************/ #include <QGuiApplication> -#include <QCommandLineParser> -#include <QCommandLineOption> +#include <QQuickWindow> #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); + // only functional when Qt Quick is also using OpenGL + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::OpenGLRhi); - parser.process(app); - - QScopedPointer<QWindow> 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(); + WindowSingleThreaded window; + window.resize(1024, 768); + window.show(); return app.exec(); } diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro b/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro index 900f5d14dd..3f32a0dff4 100644 --- a/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro +++ b/examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pro @@ -1,14 +1,12 @@ TEMPLATE = app -QT += quick qml +QT += quick qml opengl 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/rendercontrol_opengl/window_multithreaded.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.cpp deleted file mode 100644 index 01d6b8b5b2..0000000000 --- a/examples/quick/rendercontrol/rendercontrol_opengl/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 <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_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<QQmlError> 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<QQmlError> errorList = m_qmlComponent->errors(); - for (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/rendercontrol_opengl/window_multithreaded.h b/examples/quick/rendercontrol/rendercontrol_opengl/window_multithreaded.h deleted file mode 100644 index ded80a0064..0000000000 --- a/examples/quick/rendercontrol/rendercontrol_opengl/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 <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) 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 index ddbbfe4b52..fb3c0b4f4f 100644 --- a/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.cpp +++ b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.cpp @@ -56,7 +56,6 @@ #include <QOpenGLShaderProgram> #include <QOpenGLVertexArrayObject> #include <QOpenGLBuffer> -#include <QOpenGLVertexArrayObject> #include <QOffscreenSurface> #include <QScreen> #include <QQmlEngine> @@ -65,6 +64,8 @@ #include <QQuickWindow> #include <QQuickRenderControl> #include <QCoreApplication> +#include <QQuickRenderTarget> +#include <QQuickGraphicsDevice> class RenderControl : public QQuickRenderControl { @@ -85,17 +86,13 @@ QWindow *RenderControl::renderWindow(QPoint *offset) WindowSingleThreaded::WindowSingleThreaded() : m_rootItem(nullptr), - m_fbo(nullptr), + m_textureId(0), 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); @@ -137,13 +134,13 @@ WindowSingleThreaded::WindowSingleThreaded() // 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_quickWindow, &QQuickWindow::sceneGraphInitialized, this, &WindowSingleThreaded::createTexture); + connect(m_quickWindow, &QQuickWindow::sceneGraphInvalidated, this, &WindowSingleThreaded::destroyTexture); 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 + // Just recreating the texture on resize is not sufficient, when moving between screens + // with different devicePixelRatio the QWindow size may remain the same but the texture // dimension is to change regardless. connect(this, &QWindow::screenChanged, this, &WindowSingleThreaded::handleScreenChange); } @@ -156,14 +153,13 @@ WindowSingleThreaded::~WindowSingleThreaded() // 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; + delete m_quickWindow; + delete m_renderControl; + + if (m_textureId) + m_context->functions()->glDeleteTextures(1, &m_textureId); m_context->doneCurrent(); @@ -173,42 +169,42 @@ WindowSingleThreaded::~WindowSingleThreaded() delete m_context; } -void WindowSingleThreaded::createFbo() +void WindowSingleThreaded::createTexture() { - // The scene graph has been initialized. It is now time to create an FBO and associate + // The scene graph has been initialized. It is now time to create an texture 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); - } + m_textureSize = size() * m_dpr; + QOpenGLFunctions *f = m_context->functions(); + f->glGenTextures(1, &m_textureId); + f->glBindTexture(GL_TEXTURE_2D, m_textureId); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_textureSize.width(), m_textureSize.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + m_quickWindow->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ &m_textureId, 0 }, m_textureSize)); } -void WindowSingleThreaded::destroyFbo() +void WindowSingleThreaded::destroyTexture() { - delete m_fbo; - m_fbo = nullptr; + m_context->functions()->glDeleteTextures(1, &m_textureId); + m_textureId = 0; } void WindowSingleThreaded::render() { - QSurface *surface = m_offscreenSurface; - if (m_onscreen) - surface = this; - if (!m_context->makeCurrent(surface)) + if (!m_context->makeCurrent(m_offscreenSurface)) return; - // Polish, synchronize and render the next frame (into our fbo). In this example + // Polish, synchronize and render the next frame (into our texture). 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->beginFrame(); m_renderControl->polishItems(); m_renderControl->sync(); m_renderControl->render(); + m_renderControl->endFrame(); m_quickWindow->resetOpenGLState(); QOpenGLFramebufferObject::bindDefault(); @@ -218,10 +214,7 @@ void WindowSingleThreaded::render() 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); + m_cubeRenderer->render(this, m_context, m_quickReady ? m_textureId : 0); } void WindowSingleThreaded::requestUpdate() @@ -263,11 +256,9 @@ void WindowSingleThreaded::run() 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_context->makeCurrent(m_offscreenSurface); + m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(m_context)); + m_renderControl->initialize(); m_quickInitialized = true; } @@ -295,21 +286,18 @@ void WindowSingleThreaded::exposeEvent(QExposeEvent *) { if (isExposed()) { if (!m_quickInitialized) { - if (!m_onscreen) - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); + m_cubeRenderer->render(this, m_context, m_quickReady ? m_textureId : 0); startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); } } } -void WindowSingleThreaded::resizeFbo() +void WindowSingleThreaded::resizeTexture() { - QSurface *surface = m_offscreenSurface; - if (m_onscreen) - surface = this; - if (m_rootItem && m_context->makeCurrent(surface)) { - delete m_fbo; - createFbo(); + if (m_rootItem && m_context->makeCurrent(m_offscreenSurface)) { + m_context->functions()->glDeleteTextures(1, &m_textureId); + m_textureId = 0; + createTexture(); m_context->doneCurrent(); updateSizes(); render(); @@ -318,21 +306,16 @@ void WindowSingleThreaded::resizeFbo() void WindowSingleThreaded::resizeEvent(QResizeEvent *) { - // If this is a resize after the scene is up and running, recreate the fbo and the + // If this is a resize after the scene is up and running, recreate the texture 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(); - } + if (m_textureId && m_textureSize != size() * devicePixelRatio()) + resizeTexture(); } void WindowSingleThreaded::handleScreenChange() { if (m_dpr != devicePixelRatio()) - resizeFbo(); + resizeTexture(); } void WindowSingleThreaded::mousePressEvent(QMouseEvent *e) diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h index 44b79d9f51..ad53d5c8a5 100644 --- a/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h +++ b/examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.h @@ -55,14 +55,16 @@ #include <QMatrix4x4> #include <QTimer> -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) +QT_BEGIN_NAMESPACE +class QOpenGLContext; +class QOpenGLTexture; +class QOffscreenSurface; +class QQuickRenderControl; +class QQuickWindow; +class QQmlEngine; +class QQmlComponent; +class QQuickItem; +QT_END_NAMESPACE class CubeRenderer; @@ -83,8 +85,8 @@ protected: private slots: void run(); - void createFbo(); - void destroyFbo(); + void createTexture(); + void destroyTexture(); void render(); void requestUpdate(); void handleScreenChange(); @@ -92,7 +94,7 @@ private slots: private: void startQuick(const QString &filename); void updateSizes(); - void resizeFbo(); + void resizeTexture(); QOpenGLContext *m_context; QOffscreenSurface *m_offscreenSurface; @@ -101,14 +103,13 @@ private: QQmlEngine *m_qmlEngine; QQmlComponent *m_qmlComponent; QQuickItem *m_rootItem; - QOpenGLFramebufferObject *m_fbo; + uint m_textureId; + QSize m_textureSize; bool m_quickInitialized; bool m_quickReady; QTimer m_updateTimer; CubeRenderer *m_cubeRenderer; qreal m_dpr; - bool m_onscreen; - QSize m_onscreenSize; }; #endif |