Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QCPAINTERRHIRENDERER_P_H
#define QCPAINTERRHIRENDERER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <rhi/qrhi.h>
#include "qcpainterengineutils_p.h"
#include "qcanvascustombrush_p.h"
#include "qcanvasoffscreencanvas.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
#include <functional>
#include <memory>
#include <optional>
#include <vector>
QT_BEGIN_NAMESPACE
class QCPainterEngine;
struct QCRHIContext;
class QCanvasPainter;
class QCanvasCustomBrush;
struct QCRHIPipelineStateKey;
struct QCRHISamplerDesc;
struct QCRHICall;
struct QCRHIPath;
struct QCRHICommonUniforms;
class QCanvasPath;
struct QCRHITexture
{
QRhiTexture *tex = nullptr;
int id = 0;
int width = 0;
int height = 0;
QCanvasPainter::ImageFlags flags = {};
bool own = true;
};
struct QCRhiCanvas
{
QRhiTexture *tex = nullptr;
QRhiRenderBuffer *msaaColorBuffer = nullptr;
QRhiRenderBuffer *ds = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rp = nullptr;
QCanvasOffscreenCanvas::Flags flags;
bool isNull() const { return !tex; }
void destroy();
};
bool operator==(const QCRhiCanvas &a, const QCRhiCanvas &b) noexcept;
bool operator!=(const QCRhiCanvas &a, const QCRhiCanvas &b) noexcept;
struct QCRhiCanvasGrab
{
QRhiReadbackResult readbackResult;
QtPrivate::SlotObjUniquePtr callback;
QPointer<const QObject> context;
bool hasContext = false;
};
struct QCRhiUncachedPathDrawArgs
{
const QCPaths &paths;
int pathsCount;
};
struct QCRhiCachedPathDrawArgs
{
QCanvasPath *canvasPath;
int pathGroup;
const QTransform &pathTransform;
std::optional<QCRhiUncachedPathDrawArgs> updateData;
};
class QCPainterRhiRenderer
{
public:
enum RenderFlag {
Antialiasing = 1 << 0,
StencilStrokes = 1 << 1,
TransformedClipping = 1 << 3,
SimpleClipping = 1 << 4,
DepthTest = 1 << 5,
CustomMatrix = 1 << 6,
WindingEnforce = 1 << 7,
};
Q_DECLARE_FLAGS(RenderFlags, RenderFlag)
QCPainterRhiRenderer();
~QCPainterRhiRenderer();
void create(QRhi *rhi, QCanvasPainter *painter);
void destroy();
bool isValid() const { return ctx != nullptr; }
QCPainterEngine *engine() { return m_e; }
// beginPrepare() and endPrepare() must be called outside a render pass.
//
// If devicePixelRatio is 0, rt->devicePixelRatio() is used. Specifying an
// override is relevant when the target is a texture that is independent
// from any on-screen window: rt's dpr is 1 then, but we may still want to
// take an on-screen widget or window's dpr into account. (because we know
// that the texture is eventually used in that window for something)
//
void beginPrepare(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
float devicePixelRatio = 0.0f);
void endPrepare();
// Conveniences doing both beginPrepare and engine->beginPaint
void beginPrepareAndPaint(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt);
void beginPrepareAndPaint(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
float logicalWidth, float logicalHeight, float dpr);
void setCustomMatrix(const QMatrix4x4 &matrix);
void endPrepareAndPaint();
// then when recording the render pass, call render()
void render();
void resetForNewFrame(); // to be called once for the whole frame
void resetForPass(); // called by beginPrepare()
void syncDebugCounters();
void resetDebugCounters();
bool hasDrawCalls() const;
bool testFlag(RenderFlag flag) const;
void setFlag(RenderFlags flag, bool enable);
RenderFlags flags() const;
int flagsCacheProps() const;
bool renderCreate();
QCRHITexture *renderCreateNativeTexture(QRhiTexture *texture, QCanvasPainter::ImageFlags flags = {});
QCRHITexture *renderUpdateNativeTexture(QRhiTexture *oldTexture, QRhiTexture *texture);
bool isOffscreenCanvasYUp() const;
int renderCreateTexture(QCTextureFormat type, int w, int h, QCanvasPainter::ImageFlags imageFlags, const uchar* data);
bool renderDeleteTexture(int image);
bool renderUpdateTexture(int image, int x, int y, int w, int h, const uchar* data);
void setViewport(float x, float y, float width, float height);
void renderStencil(const QCState &state, const QList<QRectF> &rects);
void renderStencil(const QCState &state, const QVectorPath &clipPath);
void clearStencil(const QCState &state);
void renderFill(const QCPaint &paint, const QCState &state,
const QRectF &bounds,
std::optional<QCRhiUncachedPathDrawArgs> uncachedPathInfo,
std::optional<QCRhiCachedPathDrawArgs> cachedPathInfo,
QCanvasPainter::FillRule fillRule);
void renderStroke(const QCPaint &paint, const QCState &state,
float strokeWidth,
std::optional<QCRhiUncachedPathDrawArgs> uncachedPathInfo,
std::optional<QCRhiCachedPathDrawArgs> cachedPathInfo);
#ifndef QCPAINTER_DISABLE_TEXT_SUPPORT
void renderTextFill(
const QCPaint &paint,
const QCState &state,
const QCRhiDistanceFieldGlyphCache::VertexList &verts,
const QCRhiDistanceFieldGlyphCache::IndexList &indices,
bool colorGlyphs = false);
void renderTextFillCustom(
const QCPaint &paint,
const QCState &state,
QCanvasCustomBrushPrivate *privBrush,
const QCRhiDistanceFieldGlyphCache::VertexList &verts,
const QCRhiDistanceFieldGlyphCache::IndexList &indices);
int populateFont(
const QFont &font,
const QRectF &rect,
const QString &text,
QCRhiDistanceFieldGlyphCache::VertexList &vertices,
QCRhiDistanceFieldGlyphCache::IndexList &indices);
int populateFontFromShapedText(
QFontEngine *fontEngine,
const quint32 *glyphIndexes,
const QFixedPoint *glyphPositions,
int glyphCount,
QCRhiDistanceFieldGlyphCache::VertexList &vertices,
QCRhiDistanceFieldGlyphCache::IndexList &indices);
int populateColorFont(
QCRhiDistanceFieldGlyphCache::VertexList &vertices,
QCRhiDistanceFieldGlyphCache::IndexList &indices);
#endif
void renderDelete();
bool isPathCachedForFill(QCanvasPath *path, int pathGroup, const QCState &state);
bool isPathCachedForStroke(QCanvasPath *path, int pathGroup, const QCState &state);
void removePathGroup(int pathGroup);
static void textureFormatInfo(QRhiTexture::Format format, QSize size,
quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel);
QCanvasOffscreenCanvas createCanvas(QSize pixelSize, int sampleCount, QCanvasOffscreenCanvas::Flags flags);
void destroyCanvas(QCanvasOffscreenCanvas &canvas);
QRhiRenderTarget *canvasRenderTarget(const QCanvasOffscreenCanvas &canvas);
void recordCanvasRenderPass(QRhiCommandBuffer *cb, const QCanvasOffscreenCanvas &canvas);
void grabCanvas(const QCanvasOffscreenCanvas &canvas, const QObject *context,
QtPrivate::SlotObjUniquePtr callback, QRhiCommandBuffer *maybeCb);
void recordRenderPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, QColor clearColor);
bool isYUpInNDC() const;
private:
QRhiGraphicsPipeline *pipeline(const QCRHIPipelineStateKey &key,
QRhiRenderPassDescriptor *rpDesc,
QRhiShaderResourceBindings *srb);
QRhiSampler *sampler(const QCRHISamplerDesc &samplerDescription);
QRhiResourceUpdateBatch *resourceUpdateBatch();
void commitResourceUpdates();
QCRHITexture *findTexture(QRhiTexture *texture) const;
QCRHITexture *findTexture(int id) const;
QRhiShaderResourceBindings *createSrb(int brushImage, int fontImage);
void updateVertUniforms(QCRHICall *call, const QTransform &transform);
QCRHICall *allocCall();
QCRHITexture *allocTexture();
int allocPaths(int count);
int allocVerts(int count);
int allocIndices(int count);
int allocCommonUniforms(int count);
QCRHICommonUniforms *uniformPtr(int i) const;
QCanvasCustomBrushPrivate::CommonUniforms *customUniformPtr(int i) const;
void preparePaint(QCRHICommonUniforms* frag, const QCPaint &paint,
const QCState &state, float width, float aa, float strokeThr,
float fontAlphaMin, float fontAlphaMax);
void prepareCustomPaint(QCanvasCustomBrushPrivate::CommonUniforms* frag, const QCPaint &paint,
QCanvasCustomBrushPrivate *privBrush, const QCState &state,
float width, float aa, float strokeThr,
float fontAlphaMin, float fontAlphaMax);
void bindPipeline(QCRHICall *call,
int pipelineIndex, int srbIndex,
const QRhiCommandBuffer::DynamicOffset &vertDynamicOffset,
const QRhiCommandBuffer::DynamicOffset &dynamicOffset,
bool indexedDraw,
bool *needsViewport);
int transferFillGeom(QCRHICall *call,
int vertexCount, const QCVertex *vertices, int indexCount,
int pathCount, const QCPath *pathInfos);
void transferStrokeGeom(QCRHICall *call,
int vertexCount, const QCVertex *vertices,
int pathCount, const QCPath *pathInfos);
void transferPathsFromCachedPathGroup(QCRHICall *call, int pathCount, const QCRHIPath *paths);
private:
friend class QCanvasPainter;
friend class QCanvasPainterPrivate;
QCContext* createRhiContext(QRhi *rhi);
QCContext *ctx = nullptr;
QCRHIContext *rhiCtx = nullptr;
QCanvasPainter *m_painter = nullptr;
QCPainterEngine *m_e = nullptr;
QVector<QCRhiCanvas> m_canvases;
std::vector<std::unique_ptr<QCRhiCanvasGrab>> m_canvasGrabs;
// Log draw calls and triangle amounts for each painting type.
int logFillDrawCallCount = 0;
int logStrokeDrawCallCount = 0;
int logTextDrawCallCount = 0;
int logFillTriCount = 0;
int logStrokeTriCount = 0;
int logTextTriCount = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QCPainterRhiRenderer::RenderFlags)
QT_END_NAMESPACE
#endif // QCPAINTERRHIRENDERER_P_H