QRhiBuffer 类

顶点、索引或均匀(常量)缓冲区资源。 更多信息...

头文件 #include <QRhiBuffer>
CMakefind_package(Qt6 REQUIRED COMPONENTS Gui)
target_link_libraries(mytarget PRIVATE Qt6::Gui)
qmakeQT += gui
Qt 6.6
继承自 QRhiResource

公共类型

结构NativeBuffer
枚举Type { Immutable, Static, Dynamic }
枚举UsageFlag { VertexBuffer, IndexBuffer, UniformBuffer, StorageBuffer }
标志UsageFlags

公共函数

virtual char *beginFullDynamicBufferUpdateForCurrentFrame()
virtual boolcreate() = 0
virtual voidendFullDynamicBufferUpdateForCurrentFrame()
virtual QRhiBuffer::NativeBuffernativeBuffer()
voidsetSize(quint32 sz)
voidsetType(QRhiBuffer::Type t)
voidsetUsage(QRhiBuffer::UsageFlags u)
quint32size() const
QRhiBuffer::Typetype() const
QRhiBuffer::UsageFlagsusage() const

重实现的公共函数

virtual QRhiResource::TyperesourceType() const override

详细描述

注意:这是一个具有有限兼容性保证的 RHI API,请参阅 QRhi 了解详情。

QRhiBuffer 封装了零、一个或多个本地缓冲区对象(如 VkBufferMTLBuffer)。一些图形 API 和后端可能不会使用本地缓冲区对象(例如,如果不使用均匀缓冲区对象,则 OpenGL),但对于 QRhiBuffer API 的用户来说是透明的。同样,某些类型的缓冲区可能需要两个或三个本地缓冲区,以允许在不会使 GPU 管线停滞的情况下高效地更新每帧内容,但对于应用程序和库来说是几乎不可见的。

QRhiBuffer 实例始终是通过调用 QRhi 的 newBuffer() 函数 来创建的。这不会创建任何本地图形资源。为此,在设置适当的选项(如类型、使用标志、大小)后,请调用 create(),尽管在大多数情况下这些选项已经根据传递给 newBuffer() 的参数设置。

示例用法

为着色器创建一个均匀缓冲区,其中GLSL均匀块包含单个 mat4 成员,并更新其内容

QRhiBuffer *ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
if (!ubuf->create()) { error(); }
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
QMatrix4x4 mvp;
// ... set up the modelview-projection matrix
batch->updateDynamicBuffer(ubuf, 0, 64, mvp.constData());
// ...
commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call

创建具有顶点数据的缓冲区示例

const float vertices[] = { -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f };
QRhiBuffer *vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices));
if (!vbuf->create()) { error(); }
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
batch->uploadStaticBuffer(vbuf, vertices);
// ...
commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call

索引缓冲区

static const quint16 indices[] = { 0, 1, 2 };
QRhiBuffer *ibuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indices));
if (!ibuf->create()) { error(); }
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
batch->uploadStaticBuffer(ibuf, indices);
// ...
commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call

常见模式

调用 create() 如果之前已成功调用,将销毁任何现有原生资源。如果这些原生资源仍然被运行中的帧使用(即,有可能仍然由GPU读取),则自动延迟销毁这些资源。因此,安全增加已初始化缓冲区大小的一个非常常见且方便的模板如下。实际上,这会丢弃和创建一组全新的原生资源,所以这并不一定是一项低成本的运算,但它更方便,而且比替代方案更快,因为不销毁buf对象本身,所有对其的引用都保持有效(例如,在从QRhiShaderResourceBinding引用的任何数据结构中)。

if (buf->size() < newSize) {
    buf->setSize(newSize);
    if (!buf->create()) { error(); }
}
// continue using buf, fill it with new data

在处理均匀缓冲区时,有时为了效率原因需要将多个绘制调用的数据合并到单个缓冲区中。请注意对齐要求:对于某些图形API,均匀缓冲区的偏移量必须对齐到256字节。这既适用于QRhiShaderResourceBinding,也适用于传递给setShaderResources()的动态偏移量。使用ubufAlignment()和ubufAligned()函数来创建可移植的代码。以下是一个发布多个(N)绘制调用的方案,这些调用具有相同的管线和几何形状,但绑定点0的均匀缓冲区中暴露的数据不同。这假设缓冲区是通过uniformBufferWithDynamicOffset()暴露的,它允许将QRhiCommandBuffer::DynamicOffset列表传递给setShaderResources()。

const int N = 2;
const int UB_SIZE = 64 + 4; // assuming a uniform block with { mat4 matrix; float opacity; }
const int ONE_UBUF_SIZE = rhi->ubufAligned(UB_SIZE);
const int TOTAL_UBUF_SIZE = N * ONE_UBUF_SIZE;
QRhiBuffer *ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, TOTAL_UBUF_SIZE);
if (!ubuf->create()) { error(); }
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
for (int i = 0; i < N; ++i) {
    batch->updateDynamicBuffer(ubuf, i * ONE_UBUF_SIZE, 64, matrix.constData());
    updates->updateDynamicBuffer(ubuf, i * ONE_UBUF_SIZE + 64, 4, &opacity);
}
// ...
// beginPass(), set pipeline, etc., and then:
for (int i = 0; i < N; ++i) {
    QRhiCommandBuffer::DynamicOffset dynOfs[] = { { 0, i * ONE_UBUF_SIZE } };
    cb->setShaderResources(srb, 1, dynOfs);
    cb->draw(36);
}

另请参阅QRhiResourceUpdateBatchQRhi,和QRhiCommandBuffer

成员类型文档

枚举 QRhiBuffer::Type

指定缓冲区资源的存储类型。

常量描述
QRhiBuffer::Immutable0表示数据在初始上传后永远不会改变。幕后,此类缓冲区资源通常放置在设备本地(GPU)内存中(在有适用的系统上)。上传新数据是可能的,但可能很昂贵。上载通常是通过复制到单独的主机可见暂存缓冲区来完成的,从该缓冲区执行GPU缓冲区到缓冲区的复制到实际仅GPU缓冲区。
QRhiBuffer::Static1表示数据期望很少改变。在有适用的系统上,通常放置在设备本地(GPU)内存中。在用于上传的主机可见暂存缓冲区后端上,对于此类型保留暂存缓冲区,与Immutable不同,因此后续上传不需要承受性能下降。应避免频繁更新,尤其是在连续帧中的更新。
QRhiBuffer::Dynamic2表示数据期望频繁改变。不推荐用于大型缓冲区。通常由主机可见内存支持两份副本,以便在不阻碍图形管线的情况下改变数据。双缓冲区由应用程序透明管理,且在此API中以任何形式披露。这是推荐的类型,在某些后端中,这是唯一的可能类型,用于具有UniformBuffer使用。

枚举 QRhiBuffer::UsageFlag
标志 QRhiBuffer::UsageFlags

标志值,用于指定如何使用缓冲区。

常量描述
QRhiBuffer::VertexBuffer1 << 0顶点缓冲区。这允许 QRhiBuffersetVertexInput() 中使用。
QRhiBuffer::IndexBuffer1 << 1索引缓冲区。这允许 QRhiBuffersetVertexInput() 中使用。
QRhiBuffer::UniformBuffer1 << 2统一缓冲区(也称为常量缓冲区)。这允许 QRhiBufferUniformBuffer 结合使用。当 NonDynamicUniformBuffers 报告为不支持时,这种使用方式只能与动态类型结合。
QRhiBuffer::StorageBuffer1 << 3存储缓冲区。这允许 QRhiBufferBufferLoadBufferStoreBufferLoadStore 结合使用。这种使用方式只能与不可变或静态类型结合,并且仅在报告支持 Compute feature 时可用。

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

成员函数文档

[virtual] char *QRhiBuffer::beginFullDynamicBufferUpdateForCurrentFrame()

返回一个指向具有主机可见缓冲区数据的内存块的指针。

这是一个对 medium-to-large 动态统一缓冲区的快捷方式,这些缓冲区的全部内容(或至少当前帧中被着色器读取的所有区域)在每个帧中都会改变,并且由于涉及的大量数据复制,基于 QRhiResourceUpdateBatch 的更新机制被认为是太重了。

在记录任何依赖于此缓冲区的渲染或计算阶段之前,必须最终调用函数 endFullDynamicUniformBufferUpdateForCurrentFrame()。

注意:通过此方法更新数据与基于 QRhiResourceUpdateBatch 的更新和回读不兼容。尝试将两种更新模型合并到同一缓冲区时可能会导致不确定的行为。

注意:通过此方法更新缓冲区数据时,必须在每个帧中执行更新,否则执行双倍或三倍缓冲的资源的后端可能会出现意外行为。

注意:此方法不可能实现部分更新,因为某些后端可能在调用此函数时选择一种策略,即失去缓冲区的先前内容。必须将数据写入帧中由着色器读取的所有区域。

注意:此函数只能在记录帧时调用,因此在 QRhi::beginFrame() 和 QRhi::endFrame() 之间。

注意:此函数只能在动态缓冲区上调用。

[pure virtual] bool QRhiBuffer::create()

创建相应的本地图形资源。如果有因之前未调用 destroy() 的 create() 而存在的资源,则隐式调用 destroy()。

成功时返回 true,图形操作失败时返回 false。无论返回值如何,调用 destroy() 总是安全。

[虚拟] void QRhiBuffer::endFullDynamicBufferUpdateForCurrentFrame()

当从 beginFullDynamicBufferUpdateForCurrentFrame() 返回的内存块更新缓冲区数据时调用。

[虚拟] QRhiBuffer::NativeBuffer QRhiBuffer::nativeBuffer()

返回此缓冲区的基本本地资源。如果后端不支持公开基本本地资源,则返回的值将为空。

根据所使用的 type()QRhi 后端,一个 QRhiBuffer 可能由多个本地缓冲区对象支持。在这种情况下,所有这些对象都将返回到返回的结构的对象数组中,其中 slotCount 指定本地缓冲区对象的数量。当正在记录帧时,可以使用 recording a frameQRhi::currentFrameSlot() 来确定 QRhi 在记录的帧内用于从此 QRhiBuffer 读取或写入操作的是哪个本地缓冲区。

在某些情况下,一个 QRhiBuffer 可能根本没有本地缓冲区对象。在这种情况下,slotCount 将设置为 0,并且不返回任何有效的本地对象。这不是错误,当某个后端不使用本地缓冲区为具有某些类型或用法的 QRhiBuffers 提供支持时,这是完全有效的。

注意:注意,QRhi 后端可能会采用各种缓冲区更新策略。与纹理不同,上传图像数据始终意味着在命令缓冲区上记录缓冲区到图像(或类似)复制命令,而缓冲区(尤其是动态和 UniformBuffer)可以以多种不同的方式操作。例如,具有 UniformBuffer 使用类型的 QRhiBuffer 可能完全不支持本地缓冲区对象,如果给定的后端不支持使用或支持均匀缓冲区和图形 API。写入缓冲区的方式和使用的后备内存类型也存在差异。对于由主机可见内存支持的缓冲区,调用此函数将保证对所有返回的本地缓冲区执行悬而未决的主机写入。

另请参阅:QRhi::currentFrameSlot() 和 QRhi::FramesInFlight

[重写虚拟] QRhiResource::Type QRhiBuffer::resourceType() const

重实现 QRhiResource::resourceType() const

返回资源类型。

void QRhiBuffer::setSize(quint32 sz)

设置缓冲区的大小(以字节为单位)。通常在 QRhi::newBuffer() 中指定大小,因此此函数仅在必须更改大小的情况下使用。与其他设置器一样,大小仅在调用 create() 时生效,对于已创建的缓冲区,这涉及到释放先前的本地资源,并在幕后创建新的资源。

后端可以选择分配大于 sz 的缓冲区大小以满足对齐要求。此操作对应用程序隐藏,且 size() 总是报告 sz 中请求的大小。

另请参阅 size()。

void QRhiBuffer::setType(QRhiBuffer::Type t)

设置缓冲区的类型为 t

另请参阅 type()。

void QRhiBuffer::setUsage(QRhiBuffer::UsageFlags u)

设置缓冲区的使用标志为 u

另请参阅 usage()。

quint32 QRhiBuffer::size() const

返回缓冲区的大小(以字节为单位)。

这始终是传递给 setSize() 或 QRhi::newBuffer() 的值。内部,如果底层图形API需要,原生缓冲区可能更大。

另请参阅 setSize()。

QRhiBuffer::Type QRhiBuffer::type() const

返回缓冲区类型。

另请参阅 setType()。

QRhiBuffer::UsageFlags QRhiBuffer::usage() const

返回缓冲区的使用标志。

另请参阅 setUsage()。

© 2024 The Qt Company Ltd. 本文件中包含的文档贡献均为各自所有者的版权。本文件中提供的文档根据自由软件基金会发布的 GNU自由文档许可证版本1.3 的条款进行许可。Qt及其相关标识是The Qt Company Ltd在芬兰以及世界其他国家的商标。所有其他商标均为各自所有者的财产。