- Created EditorConsoleSink (implements ILogSink interface) - EditorConsoleSink stores logs in memory buffer (max 1000 entries) - Added to Debug::Logger in Application::Initialize() - ConsolePanel now reads from EditorConsoleSink via static GetInstance() - Removed separate LogSystem singleton - Removed editor/src/Core/LogEntry.h (no longer needed) Now Editor and Engine share the same Debug::Logger, with ConsolePanel displaying logs via EditorConsoleSink.
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 模块**最严重的架构级缺陷**,应该优先修复。 |