4.2 KiB
RHIBuffer
命名空间: XCEngine::RHI
类型: class (abstract)
头文件: XCEngine/RHI/RHIBuffer.h
描述: 抽象 buffer 资源接口,负责 CPU 写入、尺寸与 stride 查询、资源状态记录以及调试命名。
角色概述
RHIBuffer 是 RHIResource 的具体资源类型之一,用来承载:
- vertex buffer
- index buffer
- constant buffer
- readback buffer
- 间接参数或其他 buffer 形态
当前 buffer 的“用途种类”主要由 BufferType 表达,而不是靠不同类名再拆成一堆具体 buffer 类型。
创建方式
RHIBuffer 本身不是直接构造的,通常由 RHIDevice 通过 CreateBuffer 创建,输入参数来自 RHITypes 中的 BufferDesc:
sizestridebufferTypeflags
当前接口分组
CPU 访问
这是当前 buffer 抽象里最值得注意的一组接口。它说明当前 RHI 不是把 buffer 完全当成“只能由 copy/transfer 写入的纯 GPU 资源”,而是允许上层直接进行 CPU 写入。
元数据
资源基类能力
Map() / SetData() 应该怎么理解
从当前 D3D12 / OpenGL / Vulkan 实现来看,Map() 都是直接返回后端可写入的 CPU 指针:
- D3D12 映射底层
ID3D12Resource - OpenGL 使用
glMapBuffer - Vulkan 映射底层
VkDeviceMemory
这意味着 Map() 在当前代码库里是真实可用能力,而不是占位接口。
但仍然要注意两点:
1. 抽象接口没有显式暴露内存访问策略
RHIBuffer 自己没有告诉你它位于 upload heap、device local 还是别的内存类型。具体策略由后端在 CreateBuffer() 时根据 BufferType 决定。
例如当前源码里:
- D3D12 对
Constant/Vertex/Index倾向于使用UPLOADheap - Vulkan 当前
CreateBuffer()走的是 host-visible / host-coherent 内存
所以文档不能把它说成“所有 buffer 都适合任意频率 CPU 映射”,只能说当前常见路径支持映射和写入。
2. Map() 返回的指针应视为临时访问窗口
最安全的做法仍然是:
Map()- 写入数据
Unmap()
而不是长期持有裸指针跨帧使用。
测试体现出的真实语义
tests/RHI/unit/test_buffer.cpp 已经覆盖了这些行为:
Map()/Unmap()可用SetData()可写入整块数据GetSize()、GetStride()返回创建时的元数据GetBufferType()/SetBufferType()可读写类型标记GetState()/SetState()支持资源状态跟踪SetName()/GetName()支持调试命名GetNativeHandle()创建成功后通常非空
同时,command list 与集成测试也在真实使用 buffer:
- 创建 vertex / index buffer
- 调用
SetData() - 再通过 RHIResourceView 绑定到 draw path
当前设计理解
把 buffer 统一放进同一个接口,有几个工程上的好处:
- 渲染系统可以用一套创建入口处理大多数线性 GPU 数据
- 后端可以自行决定底层内存策略
- 测试更容易覆盖共同行为
代价是抽象层不会自动告诉你“这个 buffer 最适合做什么”,调用方仍然需要结合 BufferType 和使用场景自己判断。
生命周期
RHIBuffer 由 RHIDevice 创建,并以裸指针返回。当前推荐流程是:
- 创建 buffer
- 可选地
Map()/SetData() - 通过 view 或其他路径参与渲染
- 调用 Shutdown
delete