rhi examples: expand docs
Change-Id: I88991ffe3612b76aaa687f86acd59e24c5543a76 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
parent
9982f16ead
commit
040189e8e2
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in New Issue