QQuickRhiItem 类

QQuickRhiItem 类是 QQuickFramebufferObject 的便携式替代品,它不依赖于 OpenGL,而是允许与 QRhi API 集成渲染。更多...

头文件 #include <QQuickRhiItem>
CMakefind_package(Qt6 REQUIRED COMPONENTS Quick)
target_link_libraries(mytarget PRIVATE Qt6::Quick)
qmakeQT += quick
Qt 6.7
继承自 QQuickItem
状态初步

此类仍在开发中,可能会发生变化。

属性

公共函数

QQuickRhiItem(QQuickItem *parent = nullptr)
virtual~QQuickRhiItem() override
boolalphaBlending() const
QQuickRhiItem::TextureFormatcolorBufferFormat() const
QSizeeffectiveColorBufferSize() const
intfixedColorBufferHeight() const
intfixedColorBufferWidth() const
boolisMirrorVerticallyEnabled() const
intsampleCount() const
voidsetAlphaBlending(bool enable)
voidsetColorBufferFormat(QQuickRhiItem::TextureFormat format)
voidsetFixedColorBufferHeight(int height)
voidsetFixedColorBufferWidth(int width)
voidsetMirrorVertically(bool enable)
voidsetSampleCount(int samples)

重实现的公共函数

virtual boolisTextureProvider() const override
virtual QSGTextureProvider *textureProvider() const override

信号

受保护的函数

virtual QQuickRhiItemRenderer *createRenderer() = 0
boolisAutoRenderTargetEnabled() const
voidsetAutoRenderTarget(bool enabled)

重实现的受保护函数

virtual boolevent(QEvent *e) override
virtual voidgeometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
virtual voidreleaseResources() override

详细说明

注意:QQuickRhiItem在Qt 6.7中处于技术预览阶段。API正在开发中,可能会更改。

QQuickRhiItem在Qt Quick的世界中实际上是QRhiWidget的对应物。这两个都旨在被继承,它们都使能够记录以离屏颜色缓冲区为目标基于QRhi的渲染。生成的2D图像随后与Qt Quick场景的其他部分进行合成。

注意:虽然QQuickRhiItem是公开的Qt API,但Qt Gui模块中的QRhi类家族,包括QShaderQShaderDescription,提供有限的兼容性保障。这些类没有源代码或二进制兼容性保障,这意味着只能保证API与开发应用程序所使用的Qt版本一起工作。不可混合源代码的变化目标是保持最小,并且仅在新版本(6.7,6.8等)中实施。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的现有应用程序代码的兼容性。

注意:当使用Qt Quick场景图的software适配器时,QQuickRhiItem将无法正常工作。

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

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

QRhiWidget类似,QQuickRhiItem自动管理颜色缓冲区,这通常是一个二维纹理(QRhiTexture),或者在使用多重采样时是一个QRhiRenderBuffer。(一些3D API将纹理和渲染缓冲区分开来,而另一些则将底层原生资源视为相同;渲染缓冲区主要用于允许与OpenGL ES 3.0的多重采样)

默认情况下,纹理的大小将适应项目的尺寸(考虑到设备像素比)。如果项目大小改变,纹理将使用正确的尺寸重新创建。如果更喜欢固定大小,请将fixedColorBufferWidthfixedColorBufferHeight设置为非零值。

QQuickRhiItem是一个纹理提供者,可以直接用于ShaderEffects和其他消耗纹理提供者的类。

虽然不是主要的使用场景,但QQuickRhiItem还允许结合使用直接使用3D图形API(如Vulkan、Metal、Direct 3D或OpenGL)的渲染代码。有关在QRhi渲染过程中记录本地命令的详细信息,请参阅QRhiCommandBuffer::beginExternal(),以及有关将现有原生纹理包装并随后在QRhi的后续渲染过程中使用的QRhiTexture::createFrom()。有关配置原生3D API环境(例如设备扩展)的信息,请参阅QQuickGraphicsConfiguration,请注意,QQuickWindow可以通过调用QWindow::setVulkanInstance()在早期与自定义QVulkanInstance相关联。

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

一个简单的示例

以下是一个QQuickRhiItem的子类的例子。这里以完整的形式展示。它渲染一个具有透视投影的单个三角形,三角形的旋转基于自定义项目中的angle属性。这意味着它可以通过QML中的动画(例如NumberAnimation)来驱动。

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》引言中展示的代码相同。顶点和片段着色器与其中展示的一致。

一旦向 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场景图 - RHI 纹理项目QRhi场景图和渲染

属性文档

alphaBlending : bool

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

默认值为 false。这是出于性能考虑:如果没有半透明效果,因为 QQuickRhiItemRenderer 会清空为不透明颜色并且永远不会渲染 alpha 值小于 1 的片段,则启用混合没有意义。

如果 QQuickRhiItemRenderer 子类在涉及半透明效果的情况下进行渲染,请将此属性设置为 true。

注意:在某些条件下,即使此属性的值为某个值,仍然会发生混合。例如,如果项目的 透明度(更准确地说,从父链继承的合并透明度)小于 1,则即使将此属性设置为 false,也会自动启用混合。

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

访问函数

boolalphaBlending() const
voidsetAlphaBlending(bool enable)

通知器信号

voidalphaBlendingChanged()

colorBufferFormat : TextureFormat

此属性控制用作颜色缓冲区的纹理的纹理格式。默认值为 TextureFormat::RGBA8。 QQuickRhiItem 支持渲染到 QRhiTexture 支持格式的子集。仅应指定从 QRhi::isTextureFormatSupported() 中报告为支持的格式,否则渲染将不可用。

注意:当项目和其渲染器已经初始化并进行了渲染时设置新格式意味着创建的所有 QRhiGraphicsPipeline 对象都可能变得不可用,如果相关的 QRhiRenderPassDescriptor 现在因为纹理格式不同而不再兼容。类似地,在动态更改 sampleCount 时,这意味着 initialize() 或 render() 实现必须注意释放现有的管道并创建新的管道。

访问函数

QQuickRhiItem::TextureFormatcolorBufferFormat() const
voidsetColorBufferFormat(QQuickRhiItem::TextureFormat format)

通知器信号

voidcolorBufferFormatChanged()

[只读] effectiveColorBufferSize : const QSize

此属性公开了底层颜色缓冲区(QRhiTextureQRhiRenderBuffer)的尺寸(像素为单位)。它用于GUI(主要)线程、QML绑定或JavaScript。

注意:在场景图渲染线程上运行的 QQuickRhiItemRenderer 实现,不应使用此属性。这些实现应而不是从 渲染目标 中查询大小。

注意:从主线程的角度来看,值异步变为可用,因为在渲染线程上发生渲染时该值会变化。这意味着此属性主要在QML绑定中很有用。应用代码不应假设当 QQuickRhiItem 对象构建时,值已经是最新的。

这是一个只读属性。

访问函数

QSizeeffectiveColorBufferSize() const

通知器信号

voideffectiveColorBufferSizeChanged()

fixedColorBufferHeight : int

项关联纹理的固定高度(像素)。当需要不依赖于项大小的固定纹理大小时相关。此大小对项的几何形状(大小和场景中的位置)没有影响,这意味着纹理的内容将伸展(放大)或缩放到项的区域。

例如,设置大小与项大小(像素)恰好两倍,实际上执行了2x超采样(以两倍分辨率渲染,然后在绘制场景中对应项的四边形纹理时隐式缩放)。

默认值为 0。值为0表示纹理的大小随项的大小而变化。(纹理大小 = 项大小 * 设备像素比)。

访问函数

intfixedColorBufferHeight() const
voidsetFixedColorBufferHeight(int height)

通知器信号

voidfixedColorBufferHeightChanged()

fixedColorBufferWidth : int

项关联的纹理或渲染缓冲区的固定宽度(像素)。当需要不依赖于项大小的固定颜色缓冲区大小时相关。此大小对项的几何形状(大小和场景中的位置)没有影响,这意味着纹理的内容将伸展(放大)或缩放到项的区域。

例如,设置大小与项大小(像素)恰好两倍,实际上执行了2x超采样(以两倍分辨率渲染,然后在绘制场景中对应项的四边形纹理时隐式缩放)。

默认值为 0。值为0表示纹理的大小随项的大小而变化。(纹理大小 = 项大小 * 设备像素比)。

访问函数

intfixedColorBufferWidth() const
voidsetFixedColorBufferWidth(int width)

通知器信号

voidfixedColorBufferWidthChanged()

mirrorVertically : bool

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

默认值为 false

访问函数

boolisMirrorVerticallyEnabled() const
voidsetMirrorVertically(bool enable)

通知器信号

voidmirrorVerticallyChanged()

sampleCount : int

此属性控制多重采样抗锯齿的样本计数。默认值为 1,这意味着禁用了MSAA。

有效值是1、4、8,有时是16和32。可以使用 QRhi::supportedSampleCounts() 在运行时查询支持样本计数,但通常应用应该请求1(无MSAA)、4x(普通MSAA)或8x(高MSAA)。

注意: 设置新值意味着由渲染器创建的所有 QRhiGraphicsPipeline 对象必须从那时起使用相同的样本数。使用不同样本数创建的现有 QRhiGraphicsPipeline 对象不能再使用。当值改变时,所有颜色和深度模板缓冲区将被自动销毁并重新创建,并且再次调用 initialize()。然而,当 isAutoRenderTargetEnabled() 为 false 时,应用程序将负责管理深度模板缓冲区或额外的颜色缓冲区。

将样本数从默认的 1 改变为更高的值意味着 colorTexture() 变为 nullptr,并且 msaaColorBuffer() 开始返回有效的对象。切换回 1(或 0)意味着相反的情况:在接下来调用 initialize() 时,msaaColorBuffer() 将返回 nullptr,而 colorTexture() 将再次有效。此外,当样本数大于 1 时(即使用 msaa),resolveTexture() 返回有效的(非多重样本)QRhiTexture

访问函数

intsampleCount() const
voidsetSampleCount(int samples)

通知器信号

voidsampleCountChanged()

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

成员函数文档

[显式] QQuickRhiItem::QQuickRhiItem(QQuickItem *parent = nullptr)

使用指定的 parent 构造一个新的 QQuickRhiItem。

[重写虚函数 noexcept] QQuickRhiItem::~QQuickRhiItem()

析构函数。

[纯虚保护] QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()

重新实现此函数以创建和返回一个 QQuickRhiItemRenderer 子类的实例。

此函数将在渲染线程上调用,而 GUI 线程被阻塞。

[重写虚保护] bool QQuickRhiItem::event(QEvent *e)

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

[重写虚保护] void QQuickRhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)

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

[保护] bool QQuickRhiItem::isAutoRenderTargetEnabled() const

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

默认情况下,此值是 true

另请参阅 setAutoRenderTarget

[重写虚函数] bool QQuickRhiItem::isTextureProvider() const

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

[覆盖虚保护方法] void QQuickRhiItem::releaseResources()

重写: QQuickItem::releaseResources().

[保护方法] void QQuickRhiItem::setAutoRenderTarget(bool enabled)

控制项目是否自动创建并维护一个深度-模板缓冲区(QRhiRenderBuffer)和一个纹理渲染目标(QRhiTextureRenderTarget)。默认值是 true。尽早调用此函数,例如从派生类的构造函数中,将 enabled 设置为 false 以禁用此功能。

在自动模式下,深度-模板缓冲区的大小和样本数遵循颜色缓冲纹理的设置。在非自动模式下,总是从 renderTarget() 和 depthStencilBuffer() 返回 nullptr,此时由应用程序的 initialize() 实现负责设置和管理这些对象。

[覆盖虚方法] QSGTextureProvider *QQuickRhiItem::textureProvider() const

重写: QQuickItem::textureProvider() const.

© 2024 Qt公司。此处包含的文档贡献版权属于其各自的拥有者。提供的文档按照自由软件基金会发布的 GNU自由文档许可版1.3 的条款进行许可。Qt及其相关标志是芬兰的Qt公司及其它国家/地区的商标。所有其他商标均为其各自所有者的财产。