QQuickRhiItem Class

QQuickRhiItem 类是QQuickFramebufferObject 的可移植替代品,它不与 OpenGL 绑定,而是允许通过Qt QuickQRhi APIs 集成渲染。更多

头文件: #include <QQuickRhiItem>
CMake: find_package(Qt6 REQUIRED COMPONENTS Quick)
target_link_libraries(mytarget PRIVATE Qt6::Quick)
qmake: QT += quick
Qt 6.7
继承: QQuickItem

属性

公共函数

QQuickRhiItem(QQuickItem *parent = nullptr)
virtual ~QQuickRhiItem() override
bool alphaBlending() const
QQuickRhiItem::TextureFormat colorBufferFormat() const
QSize effectiveColorBufferSize() const
int fixedColorBufferHeight() const
int fixedColorBufferWidth() const
bool isMirrorVerticallyEnabled() const
int sampleCount() const
void setAlphaBlending(bool enable)
void setColorBufferFormat(QQuickRhiItem::TextureFormat format)
void setFixedColorBufferHeight(int height)
void setFixedColorBufferWidth(int width)
void setMirrorVertically(bool enable)
void setSampleCount(int samples)

重新实现的公共函数

virtual bool isTextureProvider() const override
virtual QSGTextureProvider *textureProvider() const override

信号

受保护函数

virtual QQuickRhiItemRenderer *createRenderer() = 0
bool isAutoRenderTargetEnabled() const
void setAutoRenderTarget(bool enabled)

重新实现的受保护函数

virtual bool event(QEvent *e) override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
virtual void releaseResources() override

详细说明

QQuickRhiItem 实际上是QRhiWidgetQt Quick 世界中的对应函数。这两个函数都可以被子类化,它们都可以实现基于记录QRhi 的渲染,该渲染以屏幕外的颜色缓冲区为目标。然后,生成的 2D 图像将与Qt Quick 场景的其他部分合成。

注: 虽然 QQuickRhiItem 是 Qt 的公共 API,但 Qt GUI 模块中的QRhi 系列类(包括QShaderQShaderDescription )提供了有限的兼容性保证。这些类没有源代码或二进制兼容性保证,这意味着 API 只能保证与应用程序开发时所使用的 Qt 版本兼容。qquickrhiitem.h 并不直接包含任何与QRhi 相关的头文件。要在实现 QQuickRhiItem 子类时使用这些类,请链接到Qt::GuiPrivate (如果使用 CMake),并包含带有rhi 前缀的相应头文件,例如#include <rhi/qrhi.h>

QQuickRhiItem 是传统QQuickFramebufferObject 类的替代品。后者本质上与 OpenGL / OpenGL ES 相绑定,而 QQuickRhiItem 可与QRhi 类协同工作,允许使用 Vulkan、Metal、Direct 3D 11/12 和 OpenGL / OpenGL ES 运行相同的渲染代码。从概念和功能上看,它们非常接近,从QQuickFramebufferObject 迁移到 QQuickRhiItem 也很简单。QQuickFramebufferObject 将继续可用,以确保与直接使用 OpenGL API 的现有应用代码兼容。

注: 当使用software 适配的Qt Quick 场景图时,QQuickRhiItem 将无法运行。

在大多数平台上,场景图渲染以及 QQuickRhiItem 执行的渲染将在专用线程上进行。因此,QQuickRhiItem 类将项目实现(QQuickItem 子类)与实际渲染逻辑严格分开。所有项目逻辑,如暴露给 QML 的属性和 UI 相关辅助函数,都必须位于 QQuickRhiItem 子类中。所有与渲染有关的内容都必须位于QQuickRhiItemRenderer 类中。为避免两个线程出现竞赛条件和读写问题,渲染器和项目绝不能读写共享变量,这一点很重要。项目与呈现器之间的通信应主要通过 QQuickRhiItem::synchronize() 函数进行。该函数将在 GUI 线程阻塞时在呈现线程上调用。也可以使用队列连接或事件在项目和呈现器之间进行通信。

应用程序必须同时子类化 QQuickRhiItem 和QQuickRhiItemRenderer 。必须重新实现纯虚拟createRenderer() 函数,以返回QQuickRhiItemRenderer 子类的新实例。

QRhiWidget 一样,QQuickRhiItem 会自动管理颜色缓冲区,缓冲区通常是二维纹理 (QRhiTexture),使用多重采样时则是QRhiRenderBuffer 。(有些三维应用程序接口会区分纹理和呈现缓冲区,而其他一些应用程序接口的底层本地资源是相同的;呈现缓冲区主要用于 OpenGL ES 3.0 的多采样功能)

默认情况下,纹理的大小将与项目的大小相适应(考虑到device pixel ratio )。如果物品尺寸发生变化,纹理将以正确的尺寸重新创建。如果希望使用固定尺寸,请将fixedColorBufferWidthfixedColorBufferHeight 设置为非零值。

QQuickRhiItem 是texture provider ,可直接用于ShaderEffects 和其他使用纹理提供程序的类。

虽然不是主要用例,但 QQuickRhiItem 也允许将直接使用 3D 图形 API(如 Vulkan、Metal、Direct 3D 或 OpenGL)的渲染代码纳入其中。关于在QRhi 渲染传递中记录本地命令的详情,请参见QRhiCommandBuffer::beginExternal() ;关于封装现有本地纹理并在随后的渲染传递中使用QRhi 的方法,请参见QRhiTexture::createFrom() 。另请参阅QQuickGraphicsConfiguration 有关配置本地 3D API 环境(如设备扩展)的内容,并注意通过尽早调用QWindow::setVulkanInstance() ,可将QQuickWindow 与自定义QVulkanInstance 关联。

注意: QQuickRhiItem 始终使用与QQuickWindow 相同的QRhi 实例(以及相同的 OpenGL 上下文、Vulkan 设备等)。要选择使用哪种底层 3D 图形 API,请尽早在QQuickWindow 上调用setGraphicsApi() 。场景图初始化后就无法更改,场景中的所有 QQuickRhiItem 实例都将使用相同的 3D API 进行渲染。

一个简单的例子

下面是 QQuickRhiItem 的子类。这里显示的是它的完整形式。它以透视投影的方式渲染单个三角形,三角形的旋转基于自定义项目的angle 属性。(这意味着它可以用动画来驱动,如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();
}

值得注意的是,这个简单的类与QRhiWidget 介绍中的代码几乎完全相同。顶点和片段着色器也是一样的。它们以 Vulkan 风格 GLSL 源代码的形式提供,必须首先由 Qt 着色器基础架构处理。这可以通过手动运行qsb 命令行工具或使用 CMake 中的qt_add_shaders()函数来实现。QQuickRhiItem 会加载这些经过预处理的.qsb 文件,这些文件会随应用程序一起提供。有关Qt着色器翻译基础架构的更多信息,请参见 Qt Shader Tools

color.vert

#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 v_color;
layout(std140, binding = 0) uniform buf {
    mat4 mvp;
};

void main()
{
    v_color = color;
    gl_Position = mvp * position;
}

color.frag

#version 440
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 fragColor;

void main()
{
    fragColor = vec4(v_color, 1.0);
}

一旦暴露于 QML(注意QML_NAMED_ELEMENT ),我们的自定义项目就可以在任何场景中实例化。(在 CMake 项目中导入为qt_add_qml_module指定的相应URI 后)。

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

更复杂的示例请参见场景图 - RHI 纹理项

另请参阅 QQuickRhiItemRenderer,Scene Graph - RHI Texture Item,QRhi, 以及Scene Graph and Rendering

属性文档

alphaBlending : bool

控制在使用QQuickRhiItem 及其渲染器生成的内容绘制四边形纹理时是否始终启用混合。

默认值为false 。这是出于性能方面的考虑:如果不涉及半透明,因为QQuickRhiItemRenderer 会清除为不透明颜色,并且不会渲染 alpha 小于 1 的片段,那么就没有必要启用混合。

如果QQuickRhiItemRenderer 子类在渲染时涉及半透明度,则应将此属性设为 true。

注意: 在某些情况下,无论此属性的值如何,混合仍会发生。例如,如果项目的opacity (更准确地说,是从父链继承的综合不透明度)小于 1,即使将此属性设置为 false,也会自动启用混合。

注: Qt Quick 场景图依赖于并期望预乘 alpha。例如,如果要在渲染器中将背景清除为 0.5 的 Alpha 值,那么请确保将红色、绿色和蓝色的清除颜色值也乘以 0.5。否则,混合结果将是不正确的。

访问功能:

bool alphaBlending() const
void setAlphaBlending(bool enable)

通知信号:

void alphaBlendingChanged()

colorBufferFormat : TextureFormat

该属性控制用作颜色缓冲区的纹理的纹理格式。默认值为 TextureFormat::RGBA8。QQuickRhiItem 支持QRhiTexture 所支持格式的子集进行渲染。应只指定QRhi::isTextureFormatSupported() 报告支持的格式,否则将无法进行渲染。

注意: 在项目及其渲染器已初始化并已渲染的情况下设置新格式,意味着渲染器创建的所有QRhiGraphicsPipeline 对象都可能无法使用,如果相关的QRhiRenderPassDescriptor 现在因纹理格式不同而不兼容的话。与动态更改sampleCount 类似,这意味着初始化() 或呈现() 实现必须释放现有管道并创建新管道。

访问函数:

QQuickRhiItem::TextureFormat colorBufferFormat() const
void setColorBufferFormat(QQuickRhiItem::TextureFormat format)

通知信号

void colorBufferFormatChanged()

[read-only] effectiveColorBufferSize : const QSize

该属性显示底层颜色缓冲区(QRhiTextureQRhiRenderBuffer )的大小(以像素为单位)。该属性供 GUI(主)线程、QML 绑定或 JavaScript 使用。

注意: 在场景图呈现线程上运行的QQuickRhiItemRenderer 实现不应使用此属性。它们应从render target 中查询大小。

注: 从主线程的角度看,该值是异步可用的,即在呈现线程上进行呈现时,该值会发生变化。这意味着该属性主要在 QML 绑定中有用。应用程序代码不能假定QQuickRhiItem 对象构建时的值已经是最新的。

这是一个只读属性。

访问功能:

QSize effectiveColorBufferSize() const

Notifier 信号:

void effectiveColorBufferSizeChanged()

fixedColorBufferHeight : int

项目相关纹理的固定高度(像素)。当需要一个不依赖于物品大小的固定纹理尺寸时,就需要用到它。该尺寸对物品的几何形状(其尺寸和在场景中的位置)没有影响,这意味着纹理的内容会被拉伸(放大)或缩小到物品的区域。

例如,如果设置的大小正好是物品(像素)大小的两倍,就会有效执行 2 倍的超级采样(以两倍的分辨率进行渲染,然后在对场景中物品对应的四边形进行纹理制作时隐含地缩小比例)。

默认值为0 。如果值为 0,则表示纹理的大小与物品的大小一致。(texture size =item size *device pixel ratio)。

访问功能:

int fixedColorBufferHeight() const
void setFixedColorBufferHeight(int height)

通知信号:

void fixedColorBufferHeightChanged()

fixedColorBufferWidth : int

项目的相关纹理或呈现缓冲区的固定宽度(像素)。当需要一个不依赖于物品大小的固定颜色缓冲区大小时,就需要用到它。该尺寸对项目的几何形状(其尺寸和在场景中的位置)没有影响,这意味着纹理的内容会被拉伸(放大)或缩小到项目区域。

例如,如果设置的大小正好是物品(像素)大小的两倍,就会有效执行 2 倍的超级采样(以两倍的分辨率进行渲染,然后在对场景中物品对应的四边形进行纹理处理时隐含地缩小比例)。

默认值为0 。如果值为 0,则表示纹理的大小与物品的大小一致。(texture size =item size *device pixel ratio)。

访问功能:

int fixedColorBufferWidth() const
void setFixedColorBufferWidth(int width)

通知信号:

void fixedColorBufferWidthChanged()

mirrorVertically : bool

此属性控制绘制纹理四边形时是否翻转纹理 UV。它对离屏颜色缓冲区的内容和QQuickRhiItemRenderer 实现的渲染没有影响。

默认值为false

访问功能:

bool isMirrorVerticallyEnabled() const
void setMirrorVertically(bool enable)

通知信号:

void mirrorVerticallyChanged()

sampleCount : int

该属性控制多采样抗锯齿的采样计数。默认值为1 ,表示禁用 MSAA。

有效值为 1、4、8,有时为 16 和 32。QRhi::supportedSampleCounts() 可用于在运行时查询支持的采样计数,但通常情况下,应用程序应请求 1(无 MSAA)、4x(正常 MSAA)或 8x(高 MSAA)。

注意: 设置新值意味着渲染器创建的所有QRhiGraphicsPipeline 对象从此都必须使用相同的采样计数。使用不同样本数创建的现有QRhiGraphicsPipeline 对象不得再使用。当值发生变化时,所有颜色和深度样板缓冲区都会自动销毁和重新创建,并再次调用initialize() 。但是,当isAutoRenderTargetEnabled() 被false 时,将由应用程序来管理深度钢网缓冲区或其他颜色缓冲区。

将采样计数从默认的 1 改为更高值意味着colorTexture() 变为nullptrmsaaColorBuffer() 开始返回有效对象。返回 1(或 0)则意味着相反的结果:在下一次调用 initialize() msaaColorBuffer() 时,将返回nullptr ,而 colorTexture() 则再次成为有效对象。此外,只要采样计数大于 1(即使用 MSAA),resolveTexture() 就会返回一个有效的(非多重采样)QRhiTexture

访问功能:

int sampleCount() const
void setSampleCount(int samples)

通知信号:

void sampleCountChanged()

另请参阅 QQuickRhiItemRenderer::msaaColorBuffer() 和QQuickRhiItemRenderer::resolveTexture()。

成员函数文档

[explicit] QQuickRhiItem::QQuickRhiItem(QQuickItem *parent = nullptr)

用给定的parent 构建一个新的 QQuickRhiItem。

[override virtual noexcept] QQuickRhiItem::~QQuickRhiItem()

毁灭者

[pure virtual protected] QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()

重新实现此函数可创建并返回QQuickRhiItemRenderer 子类的新实例。

该函数将在 GUI 线程阻塞时在渲染线程上调用。

[override virtual protected] bool QQuickRhiItem::event(QEvent *e)

重实现:QQuickItem::event(QEvent *ev)。

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

重实现:QQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)。

[protected] bool QQuickRhiItem::isAutoRenderTargetEnabled() const

返回当前自动深度模版缓冲区和渲染目标管理设置。

默认值为true

另请参见 setAutoRenderTarget().

[override virtual] bool QQuickRhiItem::isTextureProvider() const

重实现:QQuickItem::isTextureProvider() const.

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

重新实现:QQuickItem::releaseResources().

[protected] void QQuickRhiItem::setAutoRenderTarget(bool enabled)

控制项目是否自动创建和维护深度钢网QRhiRenderBufferQRhiTextureRenderTarget 。默认值为true 。请在早期调用此函数,例如从派生类的构造函数中调用,并将enabled 设置为false 以禁用此功能。

在自动模式下,深度模版缓冲区的大小和采样次数将根据颜色缓冲区纹理的设置而定。在非自动模式下,renderTarget() 和 depthStencilBuffer() 总是返回nullptr ,然后由应用程序的初始化()实现来设置和管理这些对象。

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

重实现:QQuickItem::textureProvider() const.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.