Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qsgrendernode.h"
++ #include "qsgrendernode_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \class QSGRenderNode
++ \brief The QSGRenderNode class represents a set of custom rendering commands
++ targeting the graphics API that is in use by the scenegraph.
++ \inmodule QtQuick
++ \since 5.8
++
++ QSGRenderNode allows creating scene graph nodes that perform their own
++ custom rendering via QRhi (the common approach from Qt 6.6 on), directly
++ via a 3D graphics API such as OpenGL, Vulkan, or Metal, or, when the \c
++ software backend is in use, via QPainter.
++
++ 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. See \l{Qt Quick Scene Graph} for a further discussion of
++ the three approaches.
++
++ \sa {Scene Graph - Custom QSGRenderNode}
++ */
++
++ QSGRenderNode::QSGRenderNode()
++ : QSGNode(RenderNodeType),
++ d(new QSGRenderNodePrivate)
++ {
++ }
++
++ /*!
++ Destructs the render node. Derived classes are expected to perform cleanup
++ similar to releaseResources() in here.
++
++ With QRhi and resources such as QRhiBuffer, QRhiTexture,
++ QRhiGraphicsPipeline, etc., it is often good practice to use smart pointers,
++ such as std::unique_ptr, which can often avoid the need to implement a
++ destructor, and lead to more compact source code. Keep in mind, however,
++ that implementing releaseResources(), likely containing a number of reset()
++ calls on the unique_ptrs, is still important.
++
++ \sa releaseResources()
++ */
++ QSGRenderNode::~QSGRenderNode()
++ {
++ delete d;
++ }
++
++ QSGRenderNodePrivate::QSGRenderNodePrivate()
++ : m_matrix(nullptr)
++ , m_clip_list(nullptr)
++ , m_opacity(1)
++ {
++ m_projectionMatrix.resize(1);
++ }
++
++ /*!
++ This function should return a mask where each bit represents graphics states
++ changed by the \l render() function.
++
++ \note With Qt 6 and QRhi-based rendering the only relevant values are
++ ViewportState and ScissorState. Other values can be returned but are
++ ignored in practice.
++
++ \value ViewportState Viewport
++ \value ScissorState Scissor test enabled state, scissor rectangle
++ \value DepthState This value has no effect in Qt 6.
++ \value StencilState This value has no effect in Qt 6.
++ \value ColorState This value has no effect in Qt 6.
++ \value BlendState This value has no effect in Qt 6.
++ \value CullState This value has no effect in Qt 6.
++ \value RenderTargetState This value has no effect in Qt 6.
++
++ \note The \c software backend exposes its QPainter and saves and restores
++ before and after invoking render(). Therefore reporting any changed states
++ from here is not necessary.
++
++ The default implementation returns 0, meaning no relevant state was changed
++ in render().
++
++ \note This function may be called before render().
++ */
++ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
++ {
++ return {};
++ }
++
++ /*!
++ Called from the frame preparation phase. There is a call to this function
++ before each invocation of render().
++
++ Unlike render(), this function is called before the scenegraph starts
++ recording the render pass for the current frame on the underlying command
++ buffer. This is useful when doing rendering with graphics APIs, such as
++ Vulkan, where copy type of operations will need to be recorded before the
++ render pass.
++
++ The default implementation is empty.
++
++ When implementing a QSGRenderNode that uses QRhi to render, query the QRhi
++ object from the QQuickWindow via \l{QQuickWindow::rhi()}. To get a
++ QRhiCommandBuffer for submitting work to, call commandBuffer(). To query
++ information about the active render target, call renderTarget(). See the
++ \l{{Scene Graph - Custom QSGRenderNode}} example for details.
++
++ \since 6.0
++ */
++ void QSGRenderNode::prepare()
++ {
++ }
++
++ /*!
++ \fn void QSGRenderNode::render(const RenderState *state)
++
++ This function is called by the renderer and should paint this node with
++ directly invoking commands via QRhi or directly via the underlying graphics
++ API (OpenGL, Direct3D, etc.).
++
++ The effective opacity can be retrieved with \l inheritedOpacity().
++
++ The projection matrix is available through \a state, while the model-view
++ matrix can be fetched with \l matrix(). The combined matrix is then the
++ projection matrix times the model-view matrix. The correct stacking of the
++ items in the scene is ensured by the projection matrix.
++
++ When using the provided matrices, the coordinate system for vertex data
++ follows the usual QQuickItem conventions: top-left is (0, 0), bottom-right
++ is the corresponding QQuickItem's width() and height() minus one. For
++ example, assuming a two float (x-y) per vertex coordinate layout, a
++ triangle covering half of the item can be specified as (width - 1, height - 1),
++ (0, 0), (0, height - 1) using counter-clockwise direction.
++
++ \note QSGRenderNode is provided as a means to implement custom 2D or 2.5D Qt
++ Quick items. It is not intended for integrating true 3D content into the Qt
++ Quick scene. That use case is better supported by the other methods for
++ integrating custom rendering.
++
++ \note QSGRenderNode can perform significantly better than texture-based
++ approaches (such as, QQuickRhiItem), especially on systems where
++ the fragment processing power is limited. This is because it avoids
++ rendering to a texture and then drawing a textured quad. Rather,
++ QSGRenderNode allows recording draw calls in line with the scenegraph's
++ other commands, avoiding an additional render target and the potentially
++ expensive texturing and blending.
++
++ Clip information is calculated before the function is called.
++ Implementations wishing to take clipping into account can set up scissoring
++ or stencil based on the information in \a state. The stencil buffer is
++ filled with the necessary clip shapes, but it is up to the implementation
++ to enable stencil testing.
++
++ Some scenegraph backends, software in particular, use no scissor or
++ stencil. There the clip region is provided as an ordinary QRegion.
++
++ When implementing a QSGRenderNode that uses QRhi to render, query the QRhi
++ object from the QQuickWindow via \l{QQuickWindow::rhi()}. To get a
++ QRhiCommandBuffer for submitting work to, call commandBuffer(). To query
++ information about the active render target, call renderTarget(). See the
++ \l{{Scene Graph - Custom QSGRenderNode}} example for details.
++
++ With Qt 6 and its QRhi-based scene graph renderer, no assumptions should be
++ made about the active (OpenGL) state when this function is called, even
++ when OpenGL is in use. Assume nothing about the pipelines and dynamic
++ states bound on the command list/buffer when this function is called.
++
++ \note Depth writes are expected to be disabled. Enabling depth writes can
++ lead to unexpected results, depending on the scenegraph backend in use and
++ the content in the scene, so exercise caution with this.
++
++ \note In Qt 6, \l changedStates() has limited use. See the documentation
++ for changedStates() for more information.
++
++ With some graphics APIs, including when using QRhi directly, it can be
++ necessary to reimplement prepare() in addition, or alternatively connect to
++ the QQuickWindow::beforeRendering() signal. These are called/emitted before
++ recording the beginning of a renderpass on the command buffer
++ (vkCmdBeginRenderPass with Vulkan, or starting to encode via
++ MTLRenderCommandEncoder in case of Metal. Recording copy operations cannot
++ be done inside render() with such APIs. Rather, do such operations either
++ in prepare() or the slot connected to beforeRendering (with
++ DirectConnection).
++
++ \sa QSGRendererInterface, QQuickWindow::rendererInterface()
++ */
++
++ /*!
++ This function is called when all custom graphics resources allocated by
++ this node have to be freed immediately. In case the node does not directly
++ allocate graphics resources (buffers, textures, render targets, fences,
++ etc.) through the graphics API that is in use, there is nothing to do here.
++
++ Failing to release all custom resources can lead to incorrect behavior in
++ graphics device loss scenarios on some systems since subsequent
++ reinitialization of the graphics system may fail.
++
++ \note Some scenegraph backends may choose not to call this function.
++ Therefore it is expected that QSGRenderNode implementations perform cleanup
++ both in their destructor and in releaseResources().
++
++ Unlike with the destructor, it is expected that render() can reinitialize
++ all resources it needs when called after a call to releaseResources().
++
++ With OpenGL, the scenegraph's OpenGL context will be current both when
++ calling the destructor and this function.
++ */
++ void QSGRenderNode::releaseResources()
++ {
++ }
++
++ /*!
++ \enum QSGRenderNode::StateFlag
++
++ This enum contains the possible values for use in the bitmask returned from changedStates().
++
++ \value ViewportState Viewport
++ \value ScissorState Scissor test enabled state, scissor rectangle
++ \value DepthState This value has no effect in Qt 6.
++ \value StencilState This value has no effect in Qt 6.
++ \value ColorState This value has no effect in Qt 6.
++ \value BlendState This value has no effect in Qt 6.
++ \value CullState This value has no effect in Qt 6.
++ \value RenderTargetState This value has no effect in Qt 6.
++ */
++
++ /*!
++ \enum QSGRenderNode::RenderingFlag
++
++ Possible values for the bitmask returned from flags().
++
++ \value BoundedRectRendering Indicates that the implementation of render()
++ does not render outside the area reported from rect() in item
++ coordinates. Such node implementations can lead to more efficient rendering,
++ depending on the scenegraph backend. For example, the \c software backend can
++ continue to use the more optimal partial update path when all render nodes
++ in the scene have this flag set.
++
++ \value DepthAwareRendering Indicates that the implementations of render()
++ conforms to scenegraph expectations by only generating a Z value of 0 in
++ scene coordinates which is then transformed by the matrices retrieved from
++ RenderState::projectionMatrix() and matrix(), as described in the notes for
++ render(). Such node implementations can lead to more efficient rendering,
++ depending on the scenegraph backend. For example, the batching OpenGL
++ renderer can continue to use a more optimal path when all render nodes in
++ the scene have this flag set.
++
++ \value OpaqueRendering Indicates that the implementation of render() writes
++ out opaque pixels for the entire area reported from rect(). By default the
++ renderers must assume that render() can also output semi or fully
++ transparent pixels. Setting this flag can improve performance in some
++ cases.
++
++ \value NoExternalRendering Indicates that the implementations of prepare()
++ and render() exclusively use the QRhi family of APIs, instead of
++ directly calling a 3D API such as OpenGL, Vulkan, or Metal.
++
++ \sa render(), prepare(), rect(), QRhi
++ */
++
++ /*!
++ \return flags describing the behavior of this render node.
++
++ The default implementation returns 0.
++
++ \sa RenderingFlag, rect()
++ */
++ QSGRenderNode::RenderingFlags QSGRenderNode::flags() const
++ {
++ return {};
++ }
++
++ /*!
++ \return the bounding rectangle in item coordinates for the area render()
++ touches. The value is only in use when flags() includes
++ BoundedRectRendering, ignored otherwise.
++
++ Reporting the rectangle in combination with BoundedRectRendering is
++ particularly important with the \c software backend because otherwise
++ having a rendernode in the scene would trigger fullscreen updates, skipping
++ all partial update optimizations.
++
++ For rendernodes covering the entire area of a corresponding QQuickItem the
++ return value will be (0, 0, item->width(), item->height()).
++
++ \note Nodes are also free to render outside the boundaries specified by the
++ item's width and height, since the scenegraph nodes are not bounded by the
++ QQuickItem geometry, as long as this is reported correctly from this function.
++
++ \sa flags()
++ */
++ QRectF QSGRenderNode::rect() const
++ {
++ return QRectF();
++ }
++
++ /*!
++ \return pointer to the current projection matrix.
++
++ In render() this is the same matrix that is returned from
++ RenderState::projectionMatrix(). This getter exists so that prepare() also
++ has a way to query the projection matrix.
++
++ When working with a modern graphics API, or Qt's own graphics abstraction
++ layer, it is more than likely that one will want to load
++ \c{*projectionMatrix() * *matrix()} into a uniform buffer. That is however
++ something that needs to be done in prepare(), so outside the recording of a
++ render pass. That is why both matrices are queriable directly from the
++ QSGRenderNode, both in prepare() and render().
++
++ \since 6.5
++ */
++ const QMatrix4x4 *QSGRenderNode::projectionMatrix() const
++ {
++ return &d->m_projectionMatrix[0];
++ }
++
++ /*!
++ \internal
++ */
++ const QMatrix4x4 *QSGRenderNode::projectionMatrix(qsizetype index) const
++ {
++ return &d->m_projectionMatrix[index];
++ }
++
++ /*!
++ \return pointer to the current model-view matrix.
++ */
++ const QMatrix4x4 *QSGRenderNode::matrix() const
++ {
++ return d->m_matrix;
++ }
++
++ /*!
++ \return the current clip list.
++ */
++ const QSGClipNode *QSGRenderNode::clipList() const
++ {
++ return d->m_clip_list;
++ }
++
++ /*!
++ \return the current effective opacity.
++ */
++ qreal QSGRenderNode::inheritedOpacity() const
++ {
++ return d->m_opacity;
++ }
++
++ /*!
++ \return the current render target.
++
++ This is provided mainly to enable prepare() and render() implementations
++ that use QRhi accessing the QRhiRenderTarget's
++ \l{QRhiRenderPassDescriptor}{renderPassDescriptor} or
++ \l{QRhiRenderTarget::pixelSize()}{pixel size}.
++
++ To build a QRhiGraphicsPipeline, which implies having to provide a
++ QRhiRenderPassDescriptor, query the renderPassDescriptor from the render
++ target. Be aware however that the render target may change over the
++ lifetime of the custom QQuickItem and the QSGRenderNode. For example,
++ consider what happens when dynamically setting \c{layer.enabled: true} on
++ the item or an ancestor of it: this triggers rendering into a texture, not
++ directly to the window, which means the QSGRenderNode is going to work with
++ a different render target from then on. The new render target may then have
++ a different pixel format, which can make already built graphics pipelines
++ incompatible. This can be handled with logic such as the following:
++
++ \code
++ if (m_pipeline && renderTarget()->renderPassDescriptor()->serializedFormat() != m_renderPassFormat) {
++ delete m_pipeline;
++ m_pipeline = nullptr;
++ }
++ if (!m_pipeline) {
++ // Build a new QRhiGraphicsPipeline.
++ // ...
++ // Store the serialized format for fast and simple comparisons later on.
++ m_renderPassFormat = renderTarget()->renderPassDescriptor()->serializedFormat();
++ }
++ \endcode
++
++ \since 6.6
++
++ \sa commandBuffer()
++ */
++ QRhiRenderTarget *QSGRenderNode::renderTarget() const
++ {
++ return d->m_rt.rt;
++ }
++
++ /*!
++ \return the current command buffer.
++
++ \since 6.6
++
++ \sa renderTarget()
++ */
++ QRhiCommandBuffer *QSGRenderNode::commandBuffer() const
++ {
++ return d->m_rt.cb;
++ }
++
++ QSGRenderNode::RenderState::~RenderState()
++ {
++ }
++
++ /*!
++ \class QSGRenderNode::RenderState
++ \brief Provides information about the projection matrix and clipping.
++ \inmodule QtQuick
++
++ The render state contains information for the renderer when invoking
++ commands to the scenegraph backends.
++
++ \sa QSGRenderNode::render()
++ */
++
++ /*!
++ \fn const QMatrix4x4 *QSGRenderNode::RenderState::projectionMatrix() const
++
++ \return pointer to the current projection matrix.
++
++ The model-view matrix can be retrieved with QSGRenderNode::matrix().
++ Typically \c{projection * modelview} is the matrix that is then used in the
++ vertex shader to transform the vertices.
++ */
++
++ /*!
++ \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorRect() const
++
++ \return the current scissor rectangle when clipping is active. x and y are
++ the bottom left coordinates.
++ */
++
++ /*!
++ \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorEnabled() const
++
++ \return the current state of scissoring.
++
++ \note Only relevant for graphics APIs that have a dedicated on/off state of
++ scissoring.
++ */
++
++ /*!
++ \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilValue() const
++
++ \return the current stencil reference value when clipping is active.
++ */
++
++ /*!
++ \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilEnabled() const
++
++ \return the current state of stencil testing.
++
++ \note With graphics APIs where stencil testing is enabled in pipeline state
++ objects, instead of individual state-setting commands, it is up to the
++ implementation of render() to enable stencil testing with operations
++ \c KEEP, comparison function \c EQUAL, and a read and write mask of \c 0xFF.
++ */
++
++ /*!
++ \fn const QRegion *QSGRenderNode::RenderState::clipRegion() const
++
++ \return the current clip region or null for backends where clipping is
++ implemented via stencil or scissoring.
++
++ The \c software backend uses no projection, scissor or stencil, meaning most
++ of the render state is not in use. However, the clip region that can be set
++ on the QPainter still has to be communicated since reconstructing this
++ manually in render() is not reasonable. It can therefore be queried via
++ this function. The region is in world coordinates and can be passed
++ to QPainter::setClipRegion() with Qt::ReplaceClip. This must be done before
++ calling QPainter::setTransform() since the clip region is already mapped to
++ the transform provided in QSGRenderNode::matrix().
++ */
++
++ /*!
++ \return pointer to a \a state value.
++
++ Reserved for future use.
++ */
++ void *QSGRenderNode::RenderState::get(const char *state) const
++ {
++ Q_UNUSED(state);
++ return nullptr;
++ }
++
++ QT_END_NAMESPACE