88 lines
4.5 KiB
Markdown
88 lines
4.5 KiB
Markdown
# OpenGLBuffer
|
||
|
||
**命名空间**: `XCEngine::RHI`
|
||
|
||
**类型**: `class`
|
||
|
||
**头文件**: `XCEngine/RHI/OpenGL/OpenGLBuffer.h`
|
||
|
||
**描述**: OpenGL 后端的通用缓冲区封装,负责创建原生 buffer 对象,并在引擎侧缓存一部分跨后端元数据。
|
||
|
||
## 概览
|
||
|
||
`OpenGLBuffer` 是 OpenGL RHI 里最基础的资源包装之一。它做的事情其实很直接:
|
||
|
||
- 按指定 `OpenGLBufferType` 创建一个 OpenGL buffer
|
||
- 记录大小、动态标记和目标类型
|
||
- 提供绑定、映射、更新数据等常见入口
|
||
- 额外缓存 `BufferType`、`stride`、`name`、`state` 这类跨后端元数据
|
||
|
||
从商业引擎的设计视角看,这种“原生对象 + 少量 RHI 元数据”的组合非常常见。原因是底层驱动对象本身只知道“这是一块 buffer”,但上层渲染框架还需要知道它被当作顶点缓冲、索引缓冲、常量缓冲还是别的什么角色来使用。
|
||
|
||
## 设计背景
|
||
|
||
现代显式 API 往往把“资源本体”和“如何使用它”拆得更细。例如 D3D12/Vulkan 里 buffer 的用途、状态和 view 往往由更多配套对象共同表达。OpenGL 则更多依赖“把同一个 buffer 绑定到不同 target”来体现用途。
|
||
|
||
`OpenGLBuffer` 的策略是:
|
||
|
||
- 用 `OpenGLBufferType` 决定当前 buffer 的主要 OpenGL 绑定目标。
|
||
- 用 `BufferType`、`stride`、`state` 等字段补充跨后端语义。
|
||
|
||
这样做的好处是接口统一,上层代码比较容易跨后端复用;代价是调用方必须清楚地区分“OpenGL 原生目标”和“RHI 语义元数据”不是同一个概念。
|
||
|
||
## `OpenGLBufferType` 与绑定目标
|
||
|
||
当前枚举和 `ToOpenGL(OpenGLBufferType)` 的映射关系如下:
|
||
|
||
- `Vertex` -> `GL_ARRAY_BUFFER`
|
||
- `Index` -> `GL_ELEMENT_ARRAY_BUFFER`
|
||
- `Uniform` -> `GL_UNIFORM_BUFFER`
|
||
- `CopyRead` -> `GL_COPY_READ_BUFFER`
|
||
- `CopyWrite` -> `GL_COPY_WRITE_BUFFER`
|
||
- `AtomicCounter` -> `GL_ATOMIC_COUNTER_BUFFER`
|
||
- `DispatchIndirect` -> `GL_DISPATCH_INDIRECT_BUFFER`
|
||
- `DrawIndirect` -> `GL_DRAW_INDIRECT_BUFFER`
|
||
- `ShaderBindingTable` -> `GL_SHADER_STORAGE_BUFFER`
|
||
|
||
最后这一项尤其值得注意: `ShaderBindingTable` 在当前 OpenGL 后端并不对应真正的硬件光追 SBT,而是退化映射到了 `GL_SHADER_STORAGE_BUFFER`。这正是商业级跨后端抽象里常见的做法: 保留统一语义名,但允许某些后端落到“最接近、但不完全等价”的实现。
|
||
|
||
## 生命周期
|
||
|
||
- [OpenGLBuffer()](Constructor.md) 构造空对象。
|
||
- [Initialize](Initialize.md) / [InitializeVertexBuffer](InitializeVertexBuffer.md) / [InitializeIndexBuffer](InitializeIndexBuffer.md) 生成原生 buffer 并上传初始数据。
|
||
- [Bind](Bind.md)、[BindBase](BindBase.md)、[Map](Map.md)、[SetData](SetData.md) 在使用阶段操作该 buffer。
|
||
- [Shutdown](Shutdown.md) 删除原生 buffer。
|
||
- [~OpenGLBuffer()](Destructor.md) 析构时自动调用 `Shutdown()`。
|
||
|
||
## 当前实现的真实行为
|
||
|
||
- [Initialize](Initialize.md) 当前始终返回 `true`,没有显式错误处理或状态回滚。
|
||
- `m_isIndexBuffer` 会在初始化时根据 `OpenGLBufferType::Index` 设置,但当前没有公开 getter,也没有在其他逻辑里继续参与行为。
|
||
- `m_bufferType`、`m_stride`、`m_name`、`m_state` 都不会在初始化时自动推导,需要调用方自行设置。
|
||
- [Map](Map.md) 固定使用 `GL_WRITE_ONLY`,不支持读映射、持久映射或显式 map flag。
|
||
- [Unmap](Unmap.md) 会调用 `glUnmapBuffer()`,但忽略它的返回值。
|
||
- [SetData](SetData.md) 只有在“整块覆盖且大小等于原始 `m_size`”时才走 `glBufferData()`,否则走 `glBufferSubData()`,不会做越界检查,也不会更新 `m_size`。
|
||
- 单测 `tests/RHI/OpenGL/unit/test_buffer.cpp` 主要覆盖初始化、绑定和写映射路径,没有覆盖复杂错误场景。
|
||
|
||
## 使用建议
|
||
|
||
- 如果你的调用逻辑依赖 RHI 侧的 `BufferType`、`stride` 或 `state`,请显式设置,不要假设 `Initialize()` 会自动填好。
|
||
- 如果需要把同一个 buffer 绑定到 indexed target,使用 [BindBase](BindBase.md) 时要明确传入正确的 OpenGL target,函数不会帮你从 `m_type` 推断。
|
||
- 当前不要把 `ShaderBindingTable` 视为真正的 OpenGL ray tracing SBT 支持。
|
||
|
||
## 关键方法
|
||
|
||
- [Initialize](Initialize.md)
|
||
- [Bind](Bind.md)
|
||
- [BindBase](BindBase.md)
|
||
- [Map](Map.md)
|
||
- [SetData](SetData.md)
|
||
- [Shutdown](Shutdown.md)
|
||
|
||
## 相关文档
|
||
|
||
- [OpenGL](../OpenGL.md)
|
||
- [OpenGLEnums](../OpenGLEnums/OpenGLEnums.md)
|
||
- [OpenGLCommandList](../OpenGLCommandList/OpenGLCommandList.md)
|
||
- [OpenGLVertexArray](../OpenGLVertexArray/OpenGLVertexArray.md)
|