diff options
author | Laszlo Agocs <[email protected]> | 2023-06-01 14:39:48 +0200 |
---|---|---|
committer | Laszlo Agocs <[email protected]> | 2023-06-01 19:22:29 +0200 |
commit | 040189e8e2a5b777b9d1f069a10efdadb723f485 (patch) | |
tree | de41d44ededcbcebe0eab54e7657f42cf4f710a3 | |
parent | 9982f16eadac1a252353a31972b03e3d3449f815 (diff) |
rhi examples: expand docs
Change-Id: I88991ffe3612b76aaa687f86acd59e24c5543a76
Reviewed-by: Andy Nichols <[email protected]>
6 files changed, 157 insertions, 4 deletions
diff --git a/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc b/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc index 4240e30558..47bf3489c5 100644 --- a/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc +++ b/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc @@ -29,4 +29,15 @@ APIs, the application links to \c{Qt::GuiPrivate} and includes \c{<rhi/qrhi.h>}. + QSGRenderNode is the enabler for one of the three ways to integrate custom + 2D/3D rendering into a Qt Quick scene. The other two options are to perform + the rendering \c before or \c after the Qt Quick scene's own rendering, + or to generate a whole separate render pass targeting a dedicated render + target (a texture) and then have an item in the scene display the texture. + The QSGRenderNode-based approach is similar to the former, in the sense + that no additional render passes or render targets are involved, and allows + injecting custom rendering commands "inline" with the Qt Quick scene's + own rendering. + + \sa {Scene Graph - RHI Under QML}, {Scene Graph - RHI Texture Item} */ diff --git a/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc b/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc index 97f0588314..596984dfdf 100644 --- a/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc +++ b/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc @@ -35,6 +35,12 @@ comes at the expense of being more expensive in terms of resources and performance since it involves rendering to a texture first. + \note This example demonstrates advanced, low-level functionality performing + portable, cross-platform 3D rendering, while relying on APIs with limited + compatibility guarantee from the Qt Gui module. To be able to use the QRhi + APIs, the application links to \c{Qt::GuiPrivate} and includes + \c{<rhi/qrhi.h>}. + \section1 Walkthrough \c ExampleRhiItem is the QQuickItem subclass that is exposed to QML @@ -49,13 +55,119 @@ \c ExampleRhiItem drives from \c RhiItem, which contains the generic implementation of a \l QQuickItem that maintains and displays a \l - QRhiTexture. + QRhiTexture. The design is somewhat similar to the legacy \l + QQuickFramebufferObject and its inner Renderer class. In essence what is + implemented here offers the core functionality of \l + QQuickFramebufferObject, but without being tied to OpenGL. To support the + threaded rendering model of the Qt Quick scene graph, the a separate \c + RhiItemRenderer object is instantiated that then lives on the Qt Quick + render thread, if there is one. \c RhiItem, being a \l QQuickItem, lives + and operates on the main (gui) thread. \snippet scenegraph/rhitextureitem/rhitextureitem.h itembase - The corresponding scene graph node is implemented using \l - QSGSimpleTextureNode. + \c RhiItemRenderer has three pure virtual functions expected to be + reimplemented in subclasses. \c initialize() is called to let the + application-provided renderer to know the \l QRhi and \l QRhiTexture + instances to use. The example also handles the resizing of the item, which + leads to having to use a new texture with a size different than before. + This means \c initialize() may be called multiple times during the lifetime + of a \c RhiItemRenderer. + + \c synchronize() is called from the scene graph's synchronizing phase, i.e. + from the \c RhiItem's \l{QQuickItem::updatePaintNode()}{updatePaintNode()}. + That implies that, if the threaded rendering model is used, that it is safe + to copy data between the main and the render thread since the main thread is + blocked. + + \c render() is the function that is called every time the \c RhiItem's + texture's content needs updating. This function is expected to record a + render pass onto the provided \l QRhiCommandBuffer, targeting a \l + QRhiTextureRenderTarget associated with the \l QRhiTexture passed to \c + initialize(). + + \snippet scenegraph/rhitextureitem/rhitextureitem.h rendererbase + + The scene graph node that is instantied by \c RhiItem is implemented using + \l QSGSimpleTextureNode. \snippet scenegraph/rhitextureitem/rhitextureitem.h itemnode + \c RhiItemNode connects to the window's + \l{QQuickWindow::beforeRendering()}{beforeRendering()} signal. This signal + is emitted on the render thread, if there is one, every time the Qt Quick + scene graph has started to prepare a new frame. + + \snippet scenegraph/rhitextureitem/rhitextureitem.cpp nodector + + The slot connected to this signal retrieves the \l QRhiCommandBuffer used by + the \l QQuickWindow, while also providing an example of what to do if there + is no on-screen window, and so no \l QRhiSwapChain associated with the \l + QQuickWindow. Then the \c RhiItemRenderer's \c render() function is invoked. + + \snippet scenegraph/rhitextureitem/rhitextureitem.cpp noderender + + The application-provided \c initialize and \c synchronize steps are invoked + from the \c RhiItemNode's sync() function which in turn is called from \c + RhiItem's \c updatePaintNode(). + + Once the \l QRhi is retrieved from the \l QQuickWindow, the need for a new + \l QRhiTexture is examined. If there is no texture yet, or it looks like the + item's size (in pixels, note the multiplication with the device pixel ratio) + has changed, a new texture is created. The \l QRhiTexture is then wrapped in + a \l QSGTexture, which also involves passing ownership. The wrapping \l + QSGTexture is created using a \l QQuickWindow helper function, + \l{QQuickWindow::createTextureFromRhiTexture()}{createTextureFromRhiTexture()}. In + spirit this is similar to the + \l{QQuickWindow::createTextureFromImage()}{createTextureFromImage()}, but + while the traditional QImage-based function creates a new \l QRhiTexture + under the hood, this variant takes an existing \l QRhiTexture. + + Finally, the application-provided \c synchronize() function is invoked. + + \snippet scenegraph/rhitextureitem/rhitextureitem.cpp nodesync + + The example's implementation makes a copy of the angle value, meaning the + renderer's copy of the value is updated based on the current value of the + property in \c ExampleRhiItem. + + \snippet scenegraph/rhitextureitem/rhitextureitem.cpp examplesync + + The example's implementation of the initialization step stores the \l QRhi + for future use. This example does not handle the case of the \l QRhi + changing over the lifetime of the item. If moving (reparenting) the item + between \l QQuickWindow instances is involved, then that would need to be + handled as well. What is handled however, is the case of the \c + outputTexture changing. With the implementation of \c RhiItem and \c + RhiItemNode, the \l QRhiTexture is different whenever the window, and so the + item in the scene, is resized. + + If not yet done, a \l QRhiTextureRenderTarget is created. The example also + demonstrates rendering with a depth buffer present. Care must be taken to + correctly resize this buffer whenever its size no longer matches the \c + outputTexture's size in the previous invocation of the function. + + Finally, if not yet done, the resources needed for rendering the scene are + prepared: vertex buffer, uniform buffer, graphics pipeline. + + The traditional Qt logo renderer, that has been ported from the OpenGL-based + examples of Qt 4 and 5, provides vertex positions and normals in two + separate chunks, hence using a non-interleaved layout for the vertex buffer. + + The vertex and fragment shaders are loaded from \c{.qsb} files generated at + build time (if using CMake). + + \snippet scenegraph/rhitextureitem/rhitextureitem.cpp exampleinit + + In the \c render step, the uniform buffer is updated. Note how the + \l{QRhi::clipSpaceCorrMatrix()}{QRhi-provided correction matrix} is + multiplied in. This allows ignoring the 3D API specific differences when it + comes to coordinate systems, and continuing to work with OpenGL-style + vertices and normals. + + A single render pass is recorded, containing a single draw call. + + \snippet scenegraph/rhitextureitem/rhitextureitem.cpp examplerender + + \sa {Scene Graph - RHI Under QML}, {Scene Graph - Custom QSGRenderNode} */ diff --git a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp index 92615a4d5b..99e2f79dc3 100644 --- a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp +++ b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp @@ -4,12 +4,14 @@ #include "rhitextureitem.h" #include <QFile> +//! [nodector] RhiItemNode::RhiItemNode(RhiItem *item) : m_item(item) { m_window = m_item->window(); connect(m_window, &QQuickWindow::beforeRendering, this, &RhiItemNode::render, Qt::DirectConnection); +//! [nodector] connect(m_window, &QQuickWindow::screenChanged, this, [this]() { if (m_window->effectiveDevicePixelRatio() != m_dpr) m_item->update(); @@ -21,6 +23,7 @@ QSGTexture *RhiItemNode::texture() const return m_sgTexture.get(); } +//! [nodesync] void RhiItemNode::sync() { if (!m_rhi) { @@ -56,7 +59,9 @@ void RhiItemNode::sync() m_renderer->synchronize(m_item); } +//! [nodesync] +//! [noderender] void RhiItemNode::render() { // called before Qt Quick starts recording its main render pass @@ -86,6 +91,7 @@ void RhiItemNode::render() markDirty(QSGNode::DirtyMaterial); emit textureChanged(); } +//! [noderender] void RhiItemNode::scheduleUpdate() { @@ -213,6 +219,7 @@ static QShader getShader(const QString &name) return QShader(); } +//! [exampleinit] void ExampleRhiItemRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture) { m_rhi = rhi; @@ -247,6 +254,7 @@ void ExampleRhiItemRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture) const quint32 vbufSize = vsize + nsize; scene.vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, vbufSize)); scene.vbuf->create(); +//! [exampleinit] scene.resourceUpdates = m_rhi->nextResourceUpdateBatch(); scene.resourceUpdates->uploadStaticBuffer(scene.vbuf.get(), 0, vsize, m_vertices.constData()); @@ -284,6 +292,7 @@ void ExampleRhiItemRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture) } } +//! [examplesync] void ExampleRhiItemRenderer::synchronize(RhiItem *rhiItem) { // called on the render thread (if there is one), while the main (gui) thread is blocked @@ -292,7 +301,9 @@ void ExampleRhiItemRenderer::synchronize(RhiItem *rhiItem) if (item->angle() != scene.logoAngle) scene.logoAngle = item->angle(); } +//! [examplesync] +//! [examplerender] void ExampleRhiItemRenderer::render(QRhiCommandBuffer *cb) { QRhiResourceUpdateBatch *rub = scene.resourceUpdates; @@ -320,6 +331,7 @@ void ExampleRhiItemRenderer::render(QRhiCommandBuffer *cb) cb->endPass(); } +//! [examplerender] void ExampleRhiItemRenderer::createGeometry() { diff --git a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h index d32ef34f11..8a36b2b4f1 100644 --- a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h +++ b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h @@ -15,6 +15,7 @@ class RhiItemNode; QT_FORWARD_DECLARE_CLASS(QSGPlainTexture) +//! [rendererbase] class RhiItemRenderer { public: @@ -22,7 +23,7 @@ public: virtual void initialize(QRhi *rhi, QRhiTexture *outputTexture) = 0; virtual void synchronize(RhiItem *item) = 0; virtual void render(QRhiCommandBuffer *cb) = 0; - +//! [rendererbase] void update(); private: diff --git a/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc b/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc index 358b89123c..fed4d1d54a 100644 --- a/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc +++ b/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc @@ -34,6 +34,20 @@ with all the 3D APIs supported by QRhi (such as, OpenGL, Vulkan, Metal, Direct 3D 11 and 12). + \note This example demonstrates advanced, low-level functionality performing + portable, cross-platform 3D rendering, while relying on APIs with limited + compatibility guarantee from the Qt Gui module. To be able to use the QRhi + APIs, the application links to \c{Qt::GuiPrivate} and includes + \c{<rhi/qrhi.h>}. + + Adding custom rendering as an underlay/overlay is one of the three ways to integrate + custom 2D/3D rendering into a Qt Quick scene. The other two options are to perform + the rendering "inline" with the Qt Quick scene's own rendering using QSGRenderNode, + or to generate a whole separate render pass targeting a dedicated render target + (a texture) and then have an item in the scene display the texture. + Refer to the \l{Scene Graph - RHI Texture Item} and the + \l{Scene Graph - Custom QSGRenderNode} examples regarding those approaches. + \section1 Core Concepts The beforeRendering() signal is emitted at the start of every frame, before @@ -216,4 +230,6 @@ normally be generated at build time, and lists them in the qrc file. This approach is however not recommended for new applications that use CMake as the build system. + + \sa {Scene Graph - RHI Texture Item}, {Scene Graph - Custom QSGRenderNode} */ diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc index 2f0cff62bc..6a513071a8 100644 --- a/src/quick/doc/src/examples.qdoc +++ b/src/quick/doc/src/examples.qdoc @@ -151,6 +151,7 @@ Creator. \li \l{Scene Graph - Custom Material}{Custom Material} \li \l{Scene Graph - RHI Under QML}{Portable QRhi-based 3D rendering as a scene underlay} \li \l{Scene Graph - RHI Texture Item}{Displaying a QRhi-rendered image in a QQuickItem} + \li \l{Scene Graph - Custom QSGRenderNode}{Implementing a QRhi-based QSGRenderNode} \li \l{Scene Graph - Two Texture Providers}{Texture Providers and Materials} \li \l{Scene Graph - Custom Geometry}{Custom Geometry} \li \l{Scene Graph - Graph}{Graph} |