QQuickRhiItem Class | Qt Quick 6.7.0
Description: The QQuickRhiItem class is a portable alternative to QQuickFramebufferObject that is not tied to OpenGL, but rather allows integrating rendering with the QRhi APIs with Qt Quick.

- Qt 6.7
- Qt Quick
- C++ Classes
- QQuickRhiItem
- Qt 6.7.0 Reference Documentation

# QQuickRhiItem Class

The QQuickRhiItem class is a portable alternative to QQuickFramebufferObject that is not tied to OpenGL, but rather allows integrating rendering with the QRhi APIs with Qt Quick. More...

Header: | #include <QQuickRhiItem>
CMake: | find_package(Qt6 REQUIRED COMPONENTS Quick) target_link_libraries(mytarget PRIVATE Qt6::Quick)
qmake: | QT += quick
Since: | Qt 6.7
Inherits: | QQuickItem

This class is under development and is subject to change.

- List of all members, including inherited members

## Properties

alphaBlending : bool autoRenderTarget : bool effectiveTextureSize : const QSize explicitTextureHeight : int | explicitTextureWidth : int mirrorVertically : bool sampleCount : int textureFormat : TextureFormat

## Public Functions

| QQuickRhiItem(QQuickItem *parent = nullptr)
bool | alphaBlending() const
virtual QQuickRhiItemRenderer * | createRenderer() = 0
QSize | effectiveTextureSize() const
int | explicitTextureHeight() const
int | explicitTextureWidth() const
bool | isAutoRenderTargetEnabled() const
bool | isMirrorVerticallyEnabled() const
int | sampleCount() const
void | setAlphaBlending(bool enable)
void | setAutoRenderTarget(bool enabled)
void | setExplicitTextureHeight(int height)
void | setExplicitTextureWidth(int width)
void | setMirrorVertically(bool enable)
void | setSampleCount(int samples)
void | setTextureFormat(QQuickRhiItem::TextureFormat format)
QQuickRhiItem::TextureFormat | textureFormat() const

## Signals

void | alphaBlendingChanged()
void | autoRenderTargetChanged()
void | effectiveTextureSizeChanged()
void | explicitTextureHeightChanged()
void | explicitTextureWidthChanged()
void | mirrorVerticallyChanged()
void | sampleCountChanged()
void | textureFormatChanged()

## Reimplemented Protected Functions

virtual void | geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
virtual bool | isTextureProvider() const override
virtual void | releaseResources() override
virtual QSGTextureProvider * | textureProvider() const override

## Detailed Description

Note: QQuickRhiItem is in tech preview in Qt 6.7. The API is under development and subject to change.

QQuickRhiItem is effectively the counterpart of QRhiWidget in the world of Qt Quick. Both of these are meant to be subclassed, and they both enable recording QRhi-based rendering that targets an offscreen color buffer. The resulting 2D image is then composited with the rest of the Qt Quick scene.

Note: While QQuickRhiItem is a public Qt API, the QRhi family of classes in the Qt Gui module, including QShader and QShaderDescription, offer limited compatibility guarantees. There are no source or binary compatibility guarantees for these classes, meaning the API is only guaranteed to work with the Qt version the application was developed against. Source incompatible changes are however aimed to be kept at a minimum and will only be made in minor releases (6.7, 6.8, and so on). `qquickrhiitem.h` does not directly include any QRhi-related headers. To use those classes when implementing a QQuickRhiItem subclass, link to `Qt::GuiPrivate` (if using CMake), and include the appropriate headers with the `rhi` prefix, for example `#include <rhi/qrhi.h>`.

QQuickRhiItem is a replacement for the legacy QQuickFramebufferObject class. The latter is inherently tied to OpenGL / OpenGL ES, whereas QQuickRhiItem works with the QRhi classes, allowing to run the same rendering code with Vulkan, Metal, Direct 3D 11/12, and OpenGL / OpenGL ES. Conceptually and functionally they are very close, and migrating from QQuickFramebufferObject to QQuickRhiItem is straightforward. QQuickFramebufferObject continues to be available to ensure compatibility for existing application code that works directly with the OpenGL API.

Note: QQuickRhiItem will not be functional when using the `software` adaptation of the Qt Quick scene graph.

On most platforms, the scene graph rendering, and thus the rendering performed by the QQuickRhiItem will occur on a dedicated thread. For this reason, the QQuickRhiItem class enforces a strict separation between the item implementation (the QQuickItem subclass) and the actual rendering logic. All item logic, such as properties and UI-related helper functions exposed to QML must be located in the QQuickRhiItem subclass. Everything that relates to rendering must be located in the QQuickRhiItemRenderer class. To avoid race conditions and read/write issues from two threads it is important that the renderer and the item never read or write shared variables. Communication between the item and the renderer should primarily happen via the QQuickRhiItem::synchronize() function. This function will be called on the render thread while the GUI thread is blocked. Using queued connections or events for communication between item and renderer is also possible.

Applications must subclass both QQuickRhiItem and QQuickRhiItemRenderer. The pure virtual createRenderer() function must be reimplemented to return a new instance of the QQuickRhiItemRenderer subclass.

As with QRhiWidget, QQuickRhiItem automatically managed the color buffer, which is a 2D texture (QRhiTexture) normally, or a QRhiRenderBuffer when multisampling is in use. (some 3D APIs differentiate between textures and renderbuffers, while with some others the underlying native resource is the same; renderbuffers are used mainly to allow multisampling with OpenGL ES 3.0)

The size of the texture will by default adapt to the size of the item (with the device pixel ratio taken into account). If the item size changes, the texture is recreated with the correct size. If a fixed size is preferred, set explicitTextureWidth and explicitTextureHeight to non-zero values.

QQuickRhiItem is a texture provider and can be used directly in ShaderEffects and other classes that consume texture providers.

While not a primary use case, QQuickRhiItem also allows incorporating rendering code that directly uses a 3D graphics API such as Vulkan, Metal, Direct 3D, or OpenGL. See QRhiCommandBuffer::beginExternal() for details on recording native commands within a QRhi render pass, as well as QRhiTexture::createFrom() for a way to wrap an existing native texture and then use it with QRhi in a subsequent render pass. See also QQuickGraphicsConfiguration regarding configuring the native 3D API environment (e.g. device extensions) and note that the QQuickWindow can be associated with a custom QVulkanInstance by calling QWindow::setVulkanInstance() early enough.

Note: QQuickRhiItem always uses the same QRhi instance the QQuickWindow uses (and by extension, the same OpenGL context, Vulkan device, etc.). To choose which underlying 3D graphics API is used, call setGraphicsApi() on the QQuickWindow early enough. Changing it is not possible once the scene graph has initialized, and all QQuickRhiItem instances in the scene will render using the same 3D API.

#### A simple example

Take the following subclass of QQuickRhiItem. It is shown here in complete form. It renders a single triangle with a perspective projection, where the triangle is rotated based on the `angle` property of the custom item. (meaning it can be driven for example with animations such as NumberAnimation from QML)

     class ExampleRhiItemRenderer : public QQuickRhiItemRenderer
     {
     public:
         void initialize(QRhiCommandBuffer *cb) override;
         void synchronize(QQuickRhiItem *item) override;
         void render(QRhiCommandBuffer *cb) override;

     private:
         QRhi *m_rhi = nullptr;
         std::unique_ptr<QRhiBuffer> m_vbuf;
         std::unique_ptr<QRhiBuffer> m_ubuf;
         std::unique_ptr<QRhiShaderResourceBindings> m_srb;
         std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
         QMatrix4x4 m_viewProjection;
         float m_angle = 0.0f;
     };

     class ExampleRhiItem : public QQuickRhiItem
     {
         Q_OBJECT
         QML_NAMED_ELEMENT(ExampleRhiItem)
         Q_PROPERTY(float angle READ angle WRITE setAngle NOTIFY angleChanged)

     public:
         QQuickRhiItemRenderer *createRenderer() override;

         float angle() const { return m_angle; }
         void setAngle(float a);

     signals:
         void angleChanged();

     private:
         float m_angle = 0.0f;
     };

     QQuickRhiItemRenderer *ExampleRhiItem::createRenderer()
     {
         return new ExampleRhiItemRenderer;
     }

     void ExampleRhiItem::setAngle(float a)
     {
         if (m_angle == a)
             return;

         m_angle = a;
         emit angleChanged();
         update();
     }

     void ExampleRhiItemRenderer::synchronize(QQuickRhiItem *rhiItem)
     {
         ExampleRhiItem *item = static_cast<ExampleRhiItem *>(rhiItem);
         if (item->angle() != m_angle)
             m_angle = item->angle();
     }

     static QShader getShader(const QString &name)
     {
         QFile f(name);
         return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
     }

     static float vertexData[] = {
         0.0f,   0.5f,   1.0f, 0.0f, 0.0f,
         -0.5f,  -0.5f,   0.0f, 1.0f, 0.0f,
         0.5f,  -0.5f,   0.0f, 0.0f, 1.0f,
     };

     void ExampleRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
     {
         if (m_rhi != rhi()) {
             m_pipeline.reset();
             m_rhi = rhi();
         }

         if (!m_pipeline) {
             m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
             m_vbuf->create();

             m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64));
             m_ubuf->create();

             m_srb.reset(m_rhi->newShaderResourceBindings());
             m_srb->setBindings({
                 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, m_ubuf.get()),
             });
             m_srb->create();

             m_pipeline.reset(m_rhi->newGraphicsPipeline());
             m_pipeline->setShaderStages({
                 { QRhiShaderStage::Vertex, getShader(QLatin1String(":/shaders/color.vert.qsb")) },
                 { QRhiShaderStage::Fragment, getShader(QLatin1String(":/shaders/color.frag.qsb")) }
             });
             QRhiVertexInputLayout inputLayout;
             inputLayout.setBindings({
                 { 5 * sizeof(float) }
             });
             inputLayout.setAttributes({
                 { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
                 { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
             });
             m_pipeline->setVertexInputLayout(inputLayout);
             m_pipeline->setShaderResourceBindings(m_srb.get());
             m_pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
             m_pipeline->create();

             QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
             resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData);
             cb->resourceUpdate(resourceUpdates);
         }

         const QSize outputSize = renderTarget()->pixelSize();
         m_viewProjection = m_rhi->clipSpaceCorrMatrix();
         m_viewProjection.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
         m_viewProjection.translate(0, 0, -4);
     }

     void ExampleRhiItemRenderer::render(QRhiCommandBuffer *cb)
     {
         QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
         QMatrix4x4 modelViewProjection = m_viewProjection;
         modelViewProjection.rotate(m_angle, 0, 1, 0);
         resourceUpdates->updateDynamicBuffer(m_ubuf.get(), 0, 64, modelViewProjection.constData());

         const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
         cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates);

         cb->setGraphicsPipeline(m_pipeline.get());
         const QSize outputSize = renderTarget()->pixelSize();
         cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
         cb->setShaderResources();
         const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
         cb->setVertexInput(0, 1, &vbufBinding);
         cb->draw(3);

         cb->endPass();
     }

It is notable that this simple class is almost exactly the same as the code shown in the QRhiWidget introduction. The vertex and fragment shaders are the same as shown there.

Once exposed to QML (note the `QML_NAMED_ELEMENT`), our custom item can be instantiated in any scene. (after importing the appropriate `URI` specified for qt_add_qml_module in the CMake project)

     ExampleRhiItem {
         anchors.fill: parent
         anchors.margins: 10
         NumberAnimation on angle { from: 0; to: 360: duration: 5000; loops: Animation.Infinite }
     }

See Scene Graph - RHI Texture Item for a more complex example.

See also QQuickRhiItemRenderer, Scene Graph - RHI Texture Item, QRhi, and Scene Graph and Rendering.

## Property Documentation

### alphaBlending : bool

Controls if blending is always enabled when drawing the quad textured with the content generated by the QQuickRhiItem and its renderer.

The default value is `false`. This is for performance reasons: if semi-transparency is not involved, because the QQuickRhiItemRenderer clears to an opaque color and never renders fragments with alpha smaller than 1, then there is no point in enabling blending.

If the QQuickRhiItemRenderer subclass renders with semi-transparency involved, set this property to true.

Note: Under certain conditions blending is still going to happen regardless of the value of this property. For example, if the item's opacity (more precisely, the combined opacity inherited from the parent chain) is smaller than 1, blending will be automatically enabled even when this property is set to false.

Note: The Qt Quick scene graph relies on and expect pre-multiplied alpha. For example, if the intention is to clear the background in the renderer to an alpha value of 0.5, then make sure to multiply the red, green, and blue clear color values with 0.5 as well. Otherwise the blending results will be incorrect.

Access functions:

bool | alphaBlending() const
void | setAlphaBlending(bool enable)

Notifier signal:

void | alphaBlendingChanged()

### autoRenderTarget : bool

This property controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget is created and maintained automatically by the item. The default value is `true`.

In automatic mode, the size and sample count of the depth-stencil buffer follows the color buffer texture's settings. In non-automatic mode, renderTarget() and depthStencilBuffer() always return `nullptr` and it is then up to the application's implementation of initialize() to take care of setting up and managing these objects.

Access functions:

bool | isAutoRenderTargetEnabled() const
void | setAutoRenderTarget(bool enabled)

Notifier signal:

void | autoRenderTargetChanged()

### `[read-only]` effectiveTextureSize : const QSize

This property exposes the size, in pixels, of the underlying color buffer (the QRhiTexture or QRhiRenderBuffer). It is provided for use on the GUI (main) thread, in QML bindings or JavaScript.

Note: QQuickRhiItemRenderer implementations, operating on the scene graph render thread, should not use this property. Those should rather query the size from the render target.

This is a read-only property.

Access functions:

QSize | effectiveTextureSize() const

Notifier signal:

void | effectiveTextureSizeChanged()

### explicitTextureHeight : int

The fixed height, in pixels, of the item's associated texture. Relevant when a fixed texture size is desired that does not depend on the item's size. This size has no effect on the geometry of the item (its size and placement within the scene), which means the texture's content will appear stretched (scaled up) or scaled down onto the item's area.

For example, setting a size that is exactly twice the item's (pixel) size effectively performs 2x supersampling (rendering at twice the resolution and then implicitly scaling down when texturing the quad corresponding to the item in the scene).

By default the value is `0`. A value of 0 means that texture's size follows the item's size. (`texture size` = `item size` * `device pixel ratio`).

Access functions:

int | explicitTextureHeight() const
void | setExplicitTextureHeight(int height)

Notifier signal:

void | explicitTextureHeightChanged()

### explicitTextureWidth : int

The fixed width, in pixels, of the item's associated texture. Relevant when a fixed texture size is desired that does not depend on the item's size. This size has no effect on the geometry of the item (its size and placement within the scene), which means the texture's content will appear stretched (scaled up) or scaled down onto the item's area.

For example, setting a size that is exactly twice the item's (pixel) size effectively performs 2x supersampling (rendering at twice the resolution and then implicitly scaling down when texturing the quad corresponding to the item in the scene).

By default the value is `0`. A value of 0 means that texture's size follows the item's size. (`texture size` = `item size` * `device pixel ratio`).

Access functions:

int | explicitTextureWidth() const
void | setExplicitTextureWidth(int width)

Notifier signal:

void | explicitTextureWidthChanged()

### mirrorVertically : bool

This property controls if texture UVs are flipped when drawing the textured quad. It has no effect on the contents of the offscreen color buffer and the rendering implemented by the QQuickRhiItemRenderer.

The default value is `false`.

Access functions:

bool | isMirrorVerticallyEnabled() const
void | setMirrorVertically(bool enable)

Notifier signal:

void | mirrorVerticallyChanged()

### sampleCount : int

This property controls for sample count for multisample antialiasing. By default the value is `1` which means MSAA is disabled.

Valid values are 1, 4, 8, and sometimes 16 and 32. QRhi::supportedSampleCounts() can be used to query the supported sample counts at run time, but typically applications should request 1 (no MSAA), 4x (normal MSAA) or 8x (high MSAA).

Note: Setting a new value implies that all QRhiGraphicsPipeline objects created by the renderer must use the same sample count from then on. Existing QRhiGraphicsPipeline objects created with a different sample count must not be used anymore. When the value changes, all color and depth-stencil buffers are destroyed and recreated automatically, and initialize() is invoked again. However, when autoRenderTarget is `false`, it will be up to the application to manage this with regards to the depth-stencil buffer or additional color buffers.

Changing the sample count from the default 1 to a higher value implies that colorTexture() becomes `nullptr` and msaaColorBuffer() starts returning a valid object. Switching back to 1 (or 0), implies the opposite: in the next call to initialize() msaaColorBuffer() is going to return `nullptr`, whereas colorTexture() becomes once again valid. In addition, resolveTexture() returns a valid (non-multisample) QRhiTexture whenever the sample count is greater than 1 (i.e., MSAA is in use).

Access functions:

int | sampleCount() const
void | setSampleCount(int samples)

Notifier signal:

void | sampleCountChanged()

See also QQuickRhiItemRenderer::msaaColorBuffer() and QQuickRhiItemRenderer::resolveTexture().

### textureFormat : TextureFormat

This property controls the texture format for the texture used as the color buffer. The default value is TextureFormat::RGBA8. QQuickRhiItem supports rendering to a subset of the formats supported by QRhiTexture. Only formats that are reported as supported from QRhi::isTextureFormatSupported() should be specified, rendering will not be functional otherwise.

Note: Setting a new format when the item and its renderer are already initialized and have rendered implies that all QRhiGraphicsPipeline objects created by the renderer may become unusable, if the associated QRhiRenderPassDescriptor is now incompatible due to the different texture format. Similarly to changing sampleCount dynamically, this means that initialize() or render() implementations must then take care of releasing the existing pipelines and creating new ones.

Access functions:

QQuickRhiItem::TextureFormat | textureFormat() const
void | setTextureFormat(QQuickRhiItem::TextureFormat format)

Notifier signal:

void | textureFormatChanged()

## Member Function Documentation

### QQuickRhiItem::QQuickRhiItem(QQuickItem *parent = nullptr)

Constructs a new QQuickRhiItem with the given parent.

### `[pure virtual]` QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()

Reimplement this function to create and return a new instance of a QQuickRhiItemRenderer subclass.

This function will be called on the rendering thread while the GUI thread is blocked.

### `[override virtual protected]` void QQuickRhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)

Reimplements: QQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry).

### `[override virtual protected]` bool QQuickRhiItem::isTextureProvider() const

Reimplements: QQuickItem::isTextureProvider() const.

### `[override virtual protected]` void QQuickRhiItem::releaseResources()

Reimplements: QQuickItem::releaseResources().

### `[override virtual protected]` QSGTextureProvider *QQuickRhiItem::textureProvider() const

Reimplements: QQuickItem::textureProvider() const.