174 lines
5.0 KiB
Markdown
174 lines
5.0 KiB
Markdown
|
|
# RHI 模块严重问题:PipelineState 与 Shader 关系设计混乱
|
|||
|
|
|
|||
|
|
## 问题严重程度
|
|||
|
|
|
|||
|
|
**严重级别**: 🔴 架构级缺陷(Critical)
|
|||
|
|
|
|||
|
|
## 问题定位
|
|||
|
|
|
|||
|
|
根据核心设计理念:
|
|||
|
|
> - **求同存异**:优先提取 API 共性作为核心抽象
|
|||
|
|
> - **核心约束原则**:面向 D3D12/Vulkan 设计,参考 Unity RHI
|
|||
|
|
|
|||
|
|
当前设计存在一个**根本性的架构缺陷**。
|
|||
|
|
|
|||
|
|
## 问题详述
|
|||
|
|
|
|||
|
|
### `RHIDevice::CreatePipelineState()` 接口设计错误
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// engine/include/XCEngine/RHI/RHIDevice.h:41
|
|||
|
|
virtual RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `GraphicsPipelineDesc` 缺少 Shader 字段
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// engine/include/XCEngine/RHI/RHITypes.h:309-320
|
|||
|
|
struct GraphicsPipelineDesc {
|
|||
|
|
InputLayoutDesc inputLayout;
|
|||
|
|
RasterizerDesc rasterizerState;
|
|||
|
|
BlendDesc blendState;
|
|||
|
|
DepthStencilStateDesc depthStencilState;
|
|||
|
|
|
|||
|
|
uint32_t topologyType = 0;
|
|||
|
|
uint32_t renderTargetCount = 1;
|
|||
|
|
uint32_t renderTargetFormats[8] = { 0 };
|
|||
|
|
uint32_t depthStencilFormat = 0;
|
|||
|
|
uint32_t sampleCount = 1;
|
|||
|
|
// ❌ 缺少 Shader 字段!
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 导致的严重后果
|
|||
|
|
|
|||
|
|
### 1. D3D12 PSO 创建后立即无效
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// engine/src/RHI/D3D12/D3D12Device.cpp:434-444
|
|||
|
|
RHIPipelineState* D3D12Device::CreatePipelineState(const GraphicsPipelineDesc& desc) {
|
|||
|
|
auto* pso = new D3D12PipelineState(m_device.Get());
|
|||
|
|
pso->SetInputLayout(desc.inputLayout);
|
|||
|
|
// ... 设置各种状态
|
|||
|
|
return pso; // ❌ 此时 m_vsBytecode 和 m_psBytecode 都是空的!
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
当调用 `EnsureValid()` 时:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// engine/src/RHI/D3D12/D3D12PipelineState.cpp:143-146
|
|||
|
|
bool D3D12PipelineState::CreateD3D12PSO() {
|
|||
|
|
if (!m_vsBytecode.pShaderBytecode || !m_psBytecode.pShaderBytecode) {
|
|||
|
|
return false; // ❌ 直接失败!
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 后端行为不一致,违反"求同存异"原则
|
|||
|
|
|
|||
|
|
| 后端 | `CreatePipelineState()` 返回后 `IsValid()` |
|
|||
|
|
|------|------------------------------------------|
|
|||
|
|
| D3D12 | `false` (需要额外设置 shader) |
|
|||
|
|
| OpenGL | `true` (OpenGL 不需要预编译 PSO) |
|
|||
|
|
|
|||
|
|
这完全违反了核心设计原则。
|
|||
|
|
|
|||
|
|
### 3. 运行时状态污染
|
|||
|
|
|
|||
|
|
Shader 通过 `SetShaderBytecodes()` 在运行时注入:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 需要外部代码手动调用
|
|||
|
|
pso->SetShaderBytecodes(vsBytecode, psBytecode); // ❌ 语义混乱
|
|||
|
|
pso->EnsureValid(); // ❌ 现在才能创建真正的 D3D12 PSO
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 设计根源分析
|
|||
|
|
|
|||
|
|
### 参考 Unity RHI 的正确做法
|
|||
|
|
|
|||
|
|
Unity 的 `PipelineState` 是 **Shader + RenderState** 的不可变组合:
|
|||
|
|
- 创建时必须提供完整的 shader 和 state
|
|||
|
|
- 创建后不可修改
|
|||
|
|
- `IsValid()` 的语义是确定的
|
|||
|
|
|
|||
|
|
### 当前设计的错误假设
|
|||
|
|
|
|||
|
|
把 Shader 和 PipelineState 分离,试图让 PSO 成为"可配置的 render state 容器",但这与 D3D12/Vulkan 的 PSO 概念完全冲突——在这些 API 中,**PSO 是 shader + state 的编译后不可变对象**。
|
|||
|
|
|
|||
|
|
## 影响范围
|
|||
|
|
|
|||
|
|
1. **无法正确实现 SRP**:上层渲染管线无法可靠地预编译 PSO
|
|||
|
|
2. **资源管理混乱**:PSO 何时真正创建?何时可以释放?语义不明确
|
|||
|
|
3. **测试困难**:后端行为不一致,单元测试无法统一编写
|
|||
|
|
4. **扩展性差**:未来添加 Vulkan 后端会遇到同样的问题
|
|||
|
|
|
|||
|
|
## 建议修复方案
|
|||
|
|
|
|||
|
|
### 方案 A:在 `GraphicsPipelineDesc` 中添加 Shader 字段
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
struct GraphicsPipelineDesc {
|
|||
|
|
// ... 现有字段
|
|||
|
|
|
|||
|
|
RHIShader* vertexShader = nullptr;
|
|||
|
|
RHIShader* pixelShader = nullptr;
|
|||
|
|
RHIShader* geometryShader = nullptr;
|
|||
|
|
RHIShader* hullShader = nullptr;
|
|||
|
|
RHIShader* domainShader = nullptr;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方案 B:创建专用的 ShaderProgram 类型
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
struct ShaderProgramDesc {
|
|||
|
|
RHIShader* vertexShader = nullptr;
|
|||
|
|
RHIShader* pixelShader = nullptr;
|
|||
|
|
// ...
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct GraphicsPipelineDesc {
|
|||
|
|
ShaderProgramDesc shaders; // 包含所有 shader
|
|||
|
|
InputLayoutDesc inputLayout;
|
|||
|
|
RasterizerDesc rasterizerState;
|
|||
|
|
BlendDesc blendState;
|
|||
|
|
DepthStencilStateDesc depthStencilState;
|
|||
|
|
// ...
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方案 C:分离 PSO 创建接口
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 独立的 Compute PSO 创建
|
|||
|
|
RHIPipelineState* CreateComputePipelineState(RHIShader* computeShader);
|
|||
|
|
|
|||
|
|
// Graphics PSO 创建时必须提供 shader
|
|||
|
|
RHIPipelineState* CreateGraphicsPipelineState(
|
|||
|
|
const GraphicsPipelineDesc& desc,
|
|||
|
|
RHIShader* vertexShader,
|
|||
|
|
RHIShader* pixelShader
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 相关文件
|
|||
|
|
|
|||
|
|
- `engine/include/XCEngine/RHI/RHIDevice.h`
|
|||
|
|
- `engine/include/XCEngine/RHI/RHITypes.h`
|
|||
|
|
- `engine/include/XCEngine/RHI/RHIPipelineState.h`
|
|||
|
|
- `engine/src/RHI/D3D12/D3D12Device.cpp`
|
|||
|
|
- `engine/src/RHI/D3D12/D3D12PipelineState.cpp`
|
|||
|
|
- `engine/src/RHI/OpenGL/OpenGLDevice.cpp`
|
|||
|
|
|
|||
|
|
## 总结
|
|||
|
|
|
|||
|
|
**`GraphicsPipelineDesc` 缺少 Shader 字段** 导致:
|
|||
|
|
|
|||
|
|
1. 抽象层无法正确表达 D3D12/Vulkan 的 PSO 概念
|
|||
|
|
2. 后端行为不一致,违反"求同存异"
|
|||
|
|
3. PSO 生命周期管理语义混乱
|
|||
|
|
|
|||
|
|
这是整个 RHI 模块**最严重的架构级缺陷**,应该优先修复。
|