diff options
Diffstat (limited to 'examples/quick/scenegraph/openglunderqml')
3 files changed, 68 insertions, 35 deletions
diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc index 3d4f4443e9..9676815c44 100644 --- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc +++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc @@ -50,6 +50,12 @@ in the QML file and this value is used by the OpenGL shader program that draws the squircles. + The example is equivalent in most ways to the \l{Scene Graph - Direct3D 11 + Under QML}{Direct3D 11 Under QML}, \l{Scene Graph - Metal Under QML}{Metal + Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} + examples, they all render the same custom content, just via different + native APIs. + \snippet scenegraph/openglunderqml/squircle.h 2 First of all, we need an object we can expose to QML. This is a @@ -94,17 +100,18 @@ \snippet scenegraph/openglunderqml/squircle.cpp 3 - The default behavior of the scene graph is to clear the - framebuffer before rendering. Since we render before the scene - graph, we need to turn this clearing off. This means that we need - to clear ourselves in the \c paint() function. + The default behavior of the scene graph is to clear the framebuffer before + rendering. This is fine since we will insert our own rendering code after + this clear is enqueued. Make sure however that we clear to the desired + color (black). \snippet scenegraph/openglunderqml/squircle.cpp 9 - We use the \c sync() function to initialize the renderer and to - copy the state in our item into the renderer. When the renderer is - created, we also connect the \l QQuickWindow::beforeRendering() to - the renderer's \c paint() slot. + We use the \c sync() function to initialize the renderer and to copy the + state in our item into the renderer. When the renderer is created, we also + connect the \l QQuickWindow::beforeRendering() and \l + QQuickWindow::beforeRenderPassRecording() to the renderer's \c init() and + \c paint() slots. \note The \l QQuickWindow::beforeSynchronizing() signal is emitted on the rendering thread while the GUI thread is blocked, so it is @@ -112,8 +119,11 @@ \snippet scenegraph/openglunderqml/squircle.cpp 6 - In the \c cleanup() function we delete the renderer which in turn - cleans up its own resources. + In the \c cleanup() function we delete the renderer which in turn cleans up + its own resources. This is complemented by reimplementing \l + QQuickWindow::releaseResources() since just connecting to the + sceneGraphInvalidated() signal is not sufficient on its own to handle all + cases. \snippet scenegraph/openglunderqml/squircle.cpp 8 @@ -124,22 +134,13 @@ \snippet scenegraph/openglunderqml/squircle.cpp 4 - In the SquircleRenderer's \c paint() function we start by - initializing the shader program. By initializing the shader - program here, we make sure that the OpenGL context is bound and - that we are on the correct thread. + In the SquircleRenderer's \c init() function we start by initializing the + shader program if not yet done. The OpenGL context is current on the thread + when the slot is invoked. \snippet scenegraph/openglunderqml/squircle.cpp 5 - We use the shader program to draw the squircle. At the end of the - \c paint function we release the program and disable the - attributes we used so that the OpenGL context is in a "clean" - state for the scene graph to pick it up. - - \note If tracking the changes in the OpenGL context's state is not - feasible, one can use the function \l - QQuickWindow::resetOpenGLState() which will reset all state that - the scene graph relies on. + We use the shader program to draw the squircle in \c paint(). \snippet scenegraph/openglunderqml/main.cpp 1 diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp index d6f6b327f2..828857fe24 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.cpp +++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp @@ -53,6 +53,7 @@ #include <QtQuick/qquickwindow.h> #include <QtGui/QOpenGLShaderProgram> #include <QtGui/QOpenGLContext> +#include <QtCore/QRunnable> //! [7] Squircle::Squircle() @@ -82,10 +83,9 @@ void Squircle::handleWindowChanged(QQuickWindow *win) connect(win, &QQuickWindow::beforeSynchronizing, this, &Squircle::sync, Qt::DirectConnection); connect(win, &QQuickWindow::sceneGraphInvalidated, this, &Squircle::cleanup, Qt::DirectConnection); //! [1] - // If we allow QML to do the clearing, they would clear what we paint - // and nothing would show. //! [3] - win->setClearBeforeRendering(false); + // Ensure we start with cleared to black. The squircle's blend mode relies on this. + win->setColor(Qt::black); } } //! [3] @@ -93,10 +93,23 @@ void Squircle::handleWindowChanged(QQuickWindow *win) //! [6] void Squircle::cleanup() { - if (m_renderer) { - delete m_renderer; - m_renderer = nullptr; - } + delete m_renderer; + m_renderer = nullptr; +} + +class CleanupJob : public QRunnable +{ +public: + CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } + void run() override { delete m_renderer; } +private: + SquircleRenderer *m_renderer; +}; + +void Squircle::releaseResources() +{ + window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage); + m_renderer = nullptr; } SquircleRenderer::~SquircleRenderer() @@ -110,7 +123,8 @@ void Squircle::sync() { if (!m_renderer) { m_renderer = new SquircleRenderer(); - connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection); + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::init, Qt::DirectConnection); + connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection); } m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); m_renderer->setT(m_t); @@ -119,9 +133,12 @@ void Squircle::sync() //! [9] //! [4] -void SquircleRenderer::paint() +void SquircleRenderer::init() { if (!m_program) { + QSGRendererInterface *rif = m_window->rendererInterface(); + Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::OpenGL || rif->graphicsApi() == QSGRendererInterface::OpenGLRhi); + initializeOpenGLFunctions(); m_program = new QOpenGLShaderProgram(); @@ -146,7 +163,15 @@ void SquircleRenderer::paint() m_program->link(); } +} + //! [4] //! [5] +void SquircleRenderer::paint() +{ + // Play nice with the RHI. Not strictly needed when the scenegraph uses + // OpenGL directly. + m_window->beginExternalCommands(); + m_program->bind(); m_program->enableAttributeArray(0); @@ -157,6 +182,11 @@ void SquircleRenderer::paint() -1, 1, 1, 1 }; + + // This example relies on (deprecated) client-side pointers for the vertex + // input. Therefore, we have to make sure no vertex buffer is bound. + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_program->setAttributeArray(0, GL_FLOAT, values, 2); m_program->setUniformValue("t", (float) m_t); @@ -164,9 +194,6 @@ void SquircleRenderer::paint() glDisable(GL_DEPTH_TEST); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); @@ -178,5 +205,7 @@ void SquircleRenderer::paint() // Not strictly needed for this example, but generally useful for when // mixing with raw OpenGL. m_window->resetOpenGLState(); + + m_window->endExternalCommands(); } //! [5] diff --git a/examples/quick/scenegraph/openglunderqml/squircle.h b/examples/quick/scenegraph/openglunderqml/squircle.h index 652e679f81..1b9995bc1e 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.h +++ b/examples/quick/scenegraph/openglunderqml/squircle.h @@ -70,6 +70,7 @@ public: void setWindow(QQuickWindow *window) { m_window = window; } public slots: + void init(); void paint(); private: @@ -103,6 +104,8 @@ private slots: void handleWindowChanged(QQuickWindow *win); private: + void releaseResources() override; + qreal m_t; SquircleRenderer *m_renderer; }; |