3.8 KiB
3.8 KiB
D3D12Sampler
命名空间: XCEngine::RHI
类型: class
头文件: XCEngine/RHI/D3D12/D3D12Sampler.h
描述: D3D12 后端的采样器包装,但当前实现本质上只是一个 D3D12_SAMPLER_DESC 缓存对象,而不是独立的 GPU sampler 资源句柄。
概览
D3D12Sampler 在当前引擎里承担的是“描述数据载体”角色。
它做的事情很少,但很关键:
- 保存一份已经转换成 D3D12 原生格式的
D3D12_SAMPLER_DESC - 作为跨后端接口 RHISampler 的 D3D12 实现
- 让 D3D12DescriptorSet 在真正写入 descriptor heap 时,可以从对象里取到 sampler 描述
它当前不做这些事情:
- 不创建独立的 D3D12 sampler 对象
- 不持有 descriptor heap 槽位
- 不执行类似 OpenGL
Bind(unit)的状态绑定
设计定位
D3D12 的 sampler 语义和 OpenGL 很不一样。OpenGL 常把 sampler 看成一个可绑定对象,而 D3D12 更接近“把采样状态写成 descriptor,再由 descriptor table 参与绑定”。
这也是当前实现只保存 D3D12_SAMPLER_DESC 的原因:
- D3D12Device 负责把引擎通用
SamplerDesc翻译成 D3D12 描述 - D3D12DescriptorSet 在
UpdateSampler()中调用ID3D12Device::CreateSampler(...),把描述真正写进目标 heap
这种设计的好处是实现非常直接,能很好贴合 D3D12 的 descriptor 模型。代价是文档必须讲清楚:D3D12Sampler 不是资源拥有者,它只是后续写 descriptor 所需的数据源。
生命周期
- 构造后把
m_desc清零,m_id保持默认值0 - Initialize 只复制一份
D3D12_SAMPLER_DESC - 真正的 GPU 可见 sampler descriptor 通常在
D3D12DescriptorSet::UpdateSampler()期间才写入 heap - Shutdown 只把内部描述清零,不回收任何 heap 槽位
- 析构时自动调用 Shutdown
当前实现的真实行为
Initialize()完全忽略传入的ID3D12Device*Initialize()不做合法性校验,也不调用ID3D12Device::CreateSampler(...)- GetNativeHandle 返回的是
&m_desc,不是原生 D3D12 句柄 - GetID 返回
m_id,但当前实现从未给它赋过新值,因此始终是0 - Bind 和 Unbind 是空实现
- Shutdown 会清零
m_desc,但不会重置m_id
为什么这样设计
从商业级引擎实践看,D3D12 后端通常会把 sampler 设计成“不可变描述 + descriptor heap 写入”两段式流程,而不是像旧式 API 那样在对象上直接做 bind。
这种分层有几个明显好处:
- 采样状态可以和纹理资源解耦,便于 descriptor set 统一管理
- 后端不需要维护额外的 sampler 对象生命周期
- 测试和资源绑定代码都可以围绕 descriptor heap 展开,而不是围绕状态机展开
当前 XCEngine 的实现选择了这条路线中的最轻量版本:先保证绑定链路可用,再逐步补缓存、复用和校验能力。
当前限制
- 不是 GPU 资源拥有者,只是描述缓存
- 不做参数校验,错误的
D3D12_SAMPLER_DESC也会被接受 - 没有 sampler 去重、缓存或共享机制
- 没有真正可用于调试的唯一 ID
Bind/Unbind对 D3D12 调用方没有实际效果