QRhiBuffer Class

顶点、索引或统一(常量)缓冲区资源。更多

头文件: #include <rhi/qrhi.h>
CMake: find_package(Qt6 REQUIRED COMPONENTS Gui)
target_link_libraries(mytarget PRIVATE Qt6::GuiPrivate)
qmake: QT += gui-private
Qt 6.6
继承: QRhiResource

公共类型

struct NativeBuffer
enum Type { Immutable, Static, Dynamic }
enum UsageFlag { VertexBuffer, IndexBuffer, UniformBuffer, StorageBuffer }
flags UsageFlags

公共函数

virtual char *beginFullDynamicBufferUpdateForCurrentFrame()
virtual bool create() = 0
virtual void endFullDynamicBufferUpdateForCurrentFrame()
virtual QRhiBuffer::NativeBuffer nativeBuffer()
void setSize(quint32 sz)
void setType(QRhiBuffer::Type t)
void setUsage(QRhiBuffer::UsageFlags u)
quint32 size() const
QRhiBuffer::Type type() const
QRhiBuffer::UsageFlags usage() const

重新实现的公共函数

virtual QRhiResource::Type resourceType() const override

详细说明

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

QRhiBuffer 可封装零个、一个或多个本地缓冲区对象(如VkBufferMTLBuffer )。对于某些图形 API 和后端,某些类型的缓冲区可能根本不使用本地缓冲区对象(例如,如果不使用统一缓冲区对象,则使用 OpenGL),但这对 QRhiBuffer API 的用户来说是透明的。同样,某些类型的缓冲区可能会在下面使用两个或三个本地缓冲区,以实现高效的每帧内容更新,而不会导致 GPU 管线停滞,但这对应用程序和库来说大多是不可见的。

QRhiBuffer 实例总是通过调用the QRhi's newBuffer() function 来创建的。这不会创建任何本地图形资源。为此,请在设置适当的选项(如类型、使用标志和大小)后调用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() ,则调用create() 会销毁任何现有的本地资源。如果飞行中的帧仍在使用这些本地资源(即它们仍有可能被 GPU 读取),则会自动推迟销毁这些资源。因此,要安全地增加已初始化缓冲区的大小,一个非常常见和方便的模式如下。在实践中,这将在下面丢弃并创建一组全新的本地资源,因此这并不一定是一个便宜的操作,但却比其他操作更方便、更快捷,因为通过不销毁buf 对象本身,在其他数据结构中(例如,在任何引用 QRhiBuffer 的QRhiShaderResourceBinding 中)对该对象的所有引用都将保持有效。

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

在使用统一缓冲区时,为了提高效率,有时需要将多个绘制调用的数据合并到一个缓冲区中。请注意对齐要求:对于某些图形应用程序接口,统一缓冲区的偏移量必须对齐到 256 字节。这既适用于QRhiShaderResourceBinding ,也适用于传给setShaderResources() 的动态偏移量。使用ubufAlignment() 和ubufAligned() 函数创建可移植代码。作为示例,下面是发出多个 (N) 绘制调用的大纲,这些调用具有相同的管道和几何图形,但在绑定点 0 暴露的统一缓冲区中的数据不同。假设缓冲区是通过uniformBufferWithDynamicOffset() 暴露的,这样就可以向setShaderResources() 传递QRhiCommandBuffer::DynamicOffset 列表。

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());
    batch->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);
}

另请参见 QRhiResourceUpdateBatch,QRhi, 和QRhiCommandBuffer

成员类型文档

enum QRhiBuffer::Type

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

常数说明
QRhiBuffer::Immutable0表示数据在初始上传后不会发生变化。在引擎盖下,此类缓冲资源通常放置在设备本地(GPU)内存中(在适用的系统上)。上传新数据是可能的,但代价可能很高。上传通常是通过复制到一个单独的、主机可见的暂存缓冲区,然后再从该缓冲区向实际的 GPU 专用缓冲区发送一个 GPU 缓冲区到缓冲区的副本。
QRhiBuffer::Static1表示预计数据变化频率很低。通常放置在设备本地(GPU)内存中(如适用)。在使用主机可见的暂存缓冲区进行上传的后端,与不可变类型不同,暂存缓冲区会为该类型保留,因此后续上传不会影响性能。应避免频繁更新,尤其是连续帧内的更新。
QRhiBuffer::Dynamic2表示数据会频繁更改。不建议用于大型缓冲区。通常由主机可见内存备份两份,以便在不中断图形流水线的情况下进行更改。双缓冲对应用程序透明管理,不会以任何形式在此 API 中公开。对于使用UniformBuffer 的缓冲区,建议使用这种类型,对于某些后端,这是唯一可能的类型。

枚举 QRhiBuffer::UsageFlag
flags QRhiBuffer::UsageFlags

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

常量说明
QRhiBuffer::VertexBuffer1 << 0顶点缓冲区。该值允许在setVertexInput() 中使用QRhiBuffer
QRhiBuffer::IndexBuffer1 << 1索引缓冲区。它允许在setVertexInput() 中使用QRhiBuffer
QRhiBuffer::UniformBuffer1 << 2统一缓冲区(也称为常量缓冲区)。这允许QRhiBufferUniformBuffer 结合使用。当NonDynamicUniformBuffers 被报告为不支持时,这种用法只能与动态类型结合使用。
QRhiBuffer::StorageBuffer1 << 3存储缓冲区。这样,QRhiBuffer 就可以与BufferLoadBufferStoreBufferLoadStore 结合使用。这种用法只能与不可变或静态类型结合使用,而且只有当Compute feature 被报告为受支持时才可用。

UsageFlags 类型是QFlags<UsageFlag> 的类型定义。它存储 UsageFlag 值的 OR 组合。

成员函数文档

[virtual] char *QRhiBuffer::beginFullDynamicBufferUpdateForCurrentFrame()

返回包含主机可见缓冲区数据的内存块指针。

这是中型到大型动态统一缓冲区的快捷方式,这些缓冲区的全部内容(或至少当前帧中着色器读取的所有区域)在每一帧中都会发生变化,而基于QRhiResourceUpdateBatch 的更新机制因涉及大量数据复制而显得过于繁重。

在调用此函数后,必须调用 endFullDynamicUniformBufferUpdateForCurrentFrame(),然后再记录任何依赖于此缓冲区的渲染或计算过程。

警告 通过此方法更新数据与基于QRhiResourceUpdateBatch 的更新和回读不兼容。当尝试将两种更新模式结合到同一缓冲区时,可能会出现意外行为。同样,通过这种直接方式更新的数据可能对readBackBuffer operations 不可见,具体取决于后端。

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

警告:此方法无法进行部分更新: 使用此方法无法进行部分更新,因为某些后端可能会选择在调用此函数时丢失缓冲区先前内容的策略。数据必须写入当前正在准备的帧中着色器读取的所有区域。

警告 此函数只能在录制帧时调用,即在QRhi::beginFrame() 和QRhi::endFrame() 之间调用。

警告:此函数只能在录制帧时调用: 此函数只能在动态缓冲区中调用。

[pure virtual] bool QRhiBuffer::create()

创建相应的本地图形资源。如果由于之前的 create() 没有相应的destroy() 而导致资源已经存在,则会先隐式调用destroy()。

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

[virtual] void QRhiBuffer::endFullDynamicBufferUpdateForCurrentFrame()

beginFullDynamicBufferUpdateForCurrentFrame() 返回的内存块中的缓冲区数据内容全部更新后调用。

[virtual] QRhiBuffer::NativeBuffer QRhiBuffer::nativeBuffer()

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

一个QRhiBuffer 可能由多个本地缓冲区对象支持,具体取决于type() 和正在使用的QRhi 后端。在这种情况下,所有对象都会在返回结构体的对象数组中返回,其中 slotCount 指定了本地缓冲区对象的数量。虽然recording a frame,QRhi::currentFrameSlot() 可用于确定QRhi 在记录帧内从该QRhiBuffer 读取或写入的操作中使用哪个本地缓冲区。

在某些情况下,QRhiBuffer 完全没有本地缓冲区对象的支持。在这种情况下,slotCount 将被设置为 0,并且不会返回有效的本地对象。这并不是错误,而且在特定后端不使用本地缓冲区的 QRhiBuffers 具有特定类型或用途时,这也是完全正确的。

注意: 请注意QRhi 后端可能会采用各种缓冲区更新策略。与纹理不同,上传图像数据总是意味着在命令缓冲区上记录缓冲区到图像(或类似)的复制命令,而缓冲区,尤其是动态缓冲区和UniformBuffer 缓冲区,可以以多种不同的方式运行。例如,如果给定的后端和图形 API 不使用或不支持统一缓冲区,那么使用类型为UniformBufferQRhiBuffer 甚至可能根本没有本地缓冲区对象支持。数据写入缓冲区的方式和所使用的后备内存类型也存在差异。对于由主机可见内存支持的缓冲区,调用此函数可保证所有返回的本地缓冲区都会执行待处理的主机写入操作。

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

[override virtual] QRhiResource::Type QRhiBuffer::resourceType() const

重实现:QRhiResource::resourceType() 常量。

返回资源类型。

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()。

© 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.