- Qt 6.12
- Qt Canvas Painter
- C++ Classes
- QCanvasRhiPaintDriver
- Qt Canvas Painter | Commercial or GPLv3

# QCanvasRhiPaintDriver Class

The QCanvasRhiPaintDriver class manages the lower level aspects of QCanvasPainter-based rendering for QRhi render targets and offscreen canvases. More...

Header: | `#include <QCanvasRhiPaintDriver>`
CMake: | `find_package(Qt6 REQUIRED COMPONENTS CanvasPainter)` `target_link_libraries(mytarget PRIVATE Qt6::CanvasPainter)`
Since: | Qt 6.11

- List of all members, including inherited members

## Public Types

enum class | BeginPaintFlag { DepthTest }
flags | BeginPaintFlags
enum class | EndPaintFlag { DoNotRecordRenderPass }
flags | EndPaintFlags

## Public Functions

void | beginPaint(QCanvasOffscreenCanvas &canvas, QRhiCommandBuffer *cb, QCanvasRhiPaintDriver::BeginPaintFlags flags = {})
void | beginPaint(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QMatrix4x4 &matrix, QCanvasRhiPaintDriver::BeginPaintFlags flags = {})
void | beginPaint(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &fillColor = Qt::black, QSize logicalSize = QSize(), qreal dpr = 1.0, QCanvasRhiPaintDriver::BeginPaintFlags flags = {})
void | endPaint(QCanvasRhiPaintDriver::EndPaintFlags flags = {})
void | grabCanvas(const QCanvasOffscreenCanvas &canvas, const QObject *context, Functor &&callback)
void | renderPaint()
void | resetForNewFrame()

## Detailed Description

Applications wishing to render with QCanvasPainter onto a QRhi-based render target, such as into a QRhiTexture, or to the color buffer of a QRhiSwapChain, use QCanvasPainterFactory to initialize and retrieve a QCanvasPainter and QCanvasRhiPaintDriver associated with the QRhi. The drawing API is provided by QCanvasPainter, while the low-level aspects of rendering (what is the render target, what is the command buffer, etc.) are controlled by QCanvasRhiPaintDriver.

Note: This class is relevant only when working with QCanvasPainter without a convenience class such as QCanvasPainterWidget or QCanvasPainterItem, because those provide a QCanvasPainter instance to the application and manage its rendering implicitly.

Applications do not create instances of QCanvasRhiPaintDriver themselves. Rather, retrieve it from a successfully initialized QCanvasPainterFactory by calling paintDriver().

The following is an almost complete, standalone console application that draws a circle into a QRhiTexture, reads back the result, and saves it into a PNG file:

     int main(int argc, char *argv[])
     {
         QGuiApplication app(argc, argv);

         // std::unique_ptr<QRhi> rhi(QRhi::create(...));

         std::unique_ptr<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), 1,
                                          QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
         tex->create();
         std::unique_ptr<QRhiRenderBuffer> ds(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(1280, 720)));
         ds->create();
         QRhiTextureRenderTargetDescription rtDesc;
         rtDesc.setColorAttachments({ tex.get() });
         rtDesc.setDepthStencilBuffer(ds.get());
         std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
         std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
         rt->setRenderPassDescriptor(rp.get());
         rt->create();

         std::unique_ptr<QCanvasPainterFactory> factory(new QCanvasPainterFactory);
         QCanvasPainter *painter = factory->create(rhi.get());
         QCanvasRhiPaintDriver *pd = factory->paintDriver();

         QRhiCommandBuffer *cb;
         QRhiReadbackResult readbackResult;

         rhi->beginOffscreenFrame(&cb);
         pd->resetForNewFrame();

         {
             pd->beginPaint(cb, rt.get());

             painter->beginPath();
             painter->circle(640, 360, 180);
             painter->setFillStyle(Qt::red);
             painter->fill();

             pd->endPaint();

             QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
             u->readBackTexture({ tex.get() }, &readbackResult);
             cb->resourceUpdate(u);
         }

         rhi->endOffscreenFrame();

         QImage image(reinterpret_cast<const uchar *>(readbackResult.data.constData()),
                         readbackResult.pixelSize.width(),
                         readbackResult.pixelSize.height(),
                         QImage::Format_RGBA8888);
         if (rhi->isYUpInFramebuffer())
             image.flip();

         image.save("result.png");

         return 0;
     }

## Member Type Documentation

### enum class QCanvasRhiPaintDriver::BeginPaintFlag
flags QCanvasRhiPaintDriver::BeginPaintFlags

Specifies the flags for beginPaint().

Constant | Value | Description
--- | --- | ---
`QCanvasRhiPaintDriver::BeginPaintFlag::DepthTest` | `0x01` | Indicates that depth testing should be enabled when rendering. Normally QCanvasPainter does not write or test the depth buffer. If there is a need to test against values written by another renderer, set this flag. The depth comparison function used is Less.

The BeginPaintFlags type is a typedef for QFlags<BeginPaintFlag>. It stores an OR combination of BeginPaintFlag values.

### enum class QCanvasRhiPaintDriver::EndPaintFlag
flags QCanvasRhiPaintDriver::EndPaintFlags

Specifies the flags for endPaint().

Constant | Value | Description
--- | --- | ---
`QCanvasRhiPaintDriver::EndPaintFlag::DoNotRecordRenderPass` | `0x01` | Indicates that flushing the QCanvasPainter's generated commands and recording the QRhi render pass is not desired, because there will be an explicit call to renderPaint() afterwards.

The EndPaintFlags type is a typedef for QFlags<EndPaintFlag>. It stores an OR combination of EndPaintFlag values.

## Member Function Documentation

### void QCanvasRhiPaintDriver::beginPaint(QCanvasOffscreenCanvas &canvas, QRhiCommandBuffer *cb, QCanvasRhiPaintDriver::BeginPaintFlags flags = {})

Begins painting onto the specified offscreen canvas, recording rendering commands to the command buffer cb.

Note: A beginPaint() must always be followed by an endPaint(). Nesting is not currently supported.

Clearing happens with the fill color set on the canvas, unless the QCanvasOffscreenCanvas::Flag::PreserveContents flag is set, in which case there is no clear and the canvas contents is preserved (with potential implications for performance, depending on the underlying GPU architecture).

Note: The associated QRhi must be recording a frame (QRhi::beginFrame() or QRhi::beginOffscreenFrame() must have been called), but it should not be in render pass recording state when this function is called.

flags specifies the optional flags that control rendering.

This is an overloaded function.

### void QCanvasRhiPaintDriver::beginPaint(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QMatrix4x4 &matrix, QCanvasRhiPaintDriver::BeginPaintFlags flags = {})

Begins painting onto the render target rt, recording rendering commands to the command buffer cb.

This overload takes a custom matrix, which is used to transform the vertices. The matrix must be suitable to deal with vertices where the coordinates are given in pixels. A common use case is to pass in Qt Quick's modelview-projection matrix when implementing QCanvasPainter-pased rendering within a QSGRenderNode subclass.

The viewport size and device pixel ratio are taken from the render target.

This overload does not take a fill color since in practice it is expected to be followed by an endPaint() with the flag EndPaintFlag::DoNotRecordRenderPass set.

Note: Support for clipping is limited when a custom matrix is used. Rectangular, non-transformed clips are supported when the matrix specifies an orthographic projection without any added scaling or rotation. It is generally recommended to avoid drawing relying on clipping in this mode.

Note: A beginPaint() must always be followed by an endPaint(). Nesting is not currently supported.

Note: rt is expected to have both a color and depth-stencil attachment. In case there are multiple color attachments, only the color buffer for attachment 0 is written. QCanvasPainter requires the presence of a depth-stencil buffer. Currently only stencil is utilized, depth testing and writing are always disabled.

Note: The associated QRhi must be recording a frame (QRhi::beginFrame() or QRhi::beginOffscreenFrame() must have been called), but it should not be in render pass recording state when this function is called.

flags specifies the optional flags that control rendering.

This is an overloaded function.

### void QCanvasRhiPaintDriver::beginPaint(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &fillColor = Qt::black, QSize logicalSize = QSize(), qreal dpr = 1.0, QCanvasRhiPaintDriver::BeginPaintFlags flags = {})

Begins painting onto the render target rt, recording rendering commands to the command buffer cb.

fillColor specifies the color used for clearing the color buffer. This value is ignored when the flags for endPaint() contain EndPaintFlag::DoNotRecordRenderPass.

Note: A beginPaint() must always be followed by an endPaint(). Nesting is not currently supported.

Note: rt is expected to have both a color and depth-stencil attachment. In case there are multiple color attachments, only the color buffer for attachment 0 is written. QCanvasPainter requires the presence of a depth-stencil buffer. Currently only stencil is utilized, depth testing and writing are always disabled.

logicalSize is optional. When non-empty, it specifies the viewport size in logical units. dpr must then specify the scale factor (device pixel ratio) so that logicalSize can be converted to pixels internally. In practice this will rarely be needed, since the render target's size is used automatically by default.

Note: The associated QRhi must be recording a frame (QRhi::beginFrame() or QRhi::beginOffscreenFrame() must have been called), but it should not be in render pass recording state when this function is called.

flags specifies the optional flags that control rendering.

This is an overloaded function.

### void QCanvasRhiPaintDriver::endPaint(QCanvasRhiPaintDriver::EndPaintFlags flags = {})

Flushes and records all QRhi rendering generated by the QCanvasPainter drawing commands.

By default, this function records a full render pass, meaning it will internally call QRhiCommandBuffer::beginPass() and QRhiCommandBuffer::endPass(). The clear color is specified by the fillColor argument of beginPaint(), or the offscreen canvas's fill color.

flags can be used to control the recording of a render pass, since in some cases it will not be desirable to let endPaint() issue an entire beginPass() - endPass() sequence.

The following two snippets are identical when it comes to the results, but the sceond offers more flexibility, in case the application wants to do more within the same render pass:

     pd->beginPaint(cb, rt, Qt::black);
     // painter->...
     pd->endPaint();

     pd->beginPaint(cb, rt);
     // painter->...
     pd->endPaint(QCanvasRhiPaintDriver::EndPaintFlag::DoNotRecordRenderPass);

     cb->beginPass(rt, Qt::black, { 1.0f, 0 });
     pd->renderPaint();
     cb->endPass();

See also beginPaint() and renderPaint().

### template <typename Functor> void QCanvasRhiPaintDriver::grabCanvas(const QCanvasOffscreenCanvas &canvas, const QObject *context, Functor &&callback)

Issues a texture readback request for canvas, associating it with context.

callback is invoked either before the function returns, or later, depending on the underlying QRhi and 3D API implementation. Reading back texture contents may involve a GPU->CPU copy, depending on the GPU architecture. It takes a single `const QImage &` argument, and can be any functor, including move-only ones.

If context is destroyed before the readback completes, the grab is cancelled and callback is not invoked. This makes it safe for the callback to reference objects that may not outlive the pending readback.

This function can be called both within a beginPaint() - endPaint() block, and outside. When called outside, it will internally invoke QRhi::beginOffscreenFrame() etc., allowing grabs to be performed at any time.

For example, the following would save the contents of an offscreen canvas to a PNG file:

     grabCanvas(offscreenCanvas, qGuiApp, [](const QImage &image) {
         image.save("result.png");
     });

### void QCanvasRhiPaintDriver::renderPaint()

Records all QRhi rendering generated by the QCanvasPainter drawing commands. Call this function only when endPaint() was invoked with DoNotRecordRenderPass. It must not be called otherwise.

The associated QRhi must be recording a render pass when this function is invoked.

See also endPaint().

### void QCanvasRhiPaintDriver::resetForNewFrame()

Resets the painter engine state. This function is expected to be called once when starting a whole new frame, typically after QRhi::beginFrame() or QRhi::beginOffscreenFrame().