467 lines
11 KiB
Markdown
467 lines
11 KiB
Markdown
|
|
# RHI 模块审查报告
|
|||
|
|
|
|||
|
|
## 审查范围
|
|||
|
|
|
|||
|
|
基于设计文档 `RHI模块总览.md` 中定义的设计理念,对当前 RHI 模块的设计和实现进行全面审查。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、设计问题
|
|||
|
|
|
|||
|
|
### 1. 接口一致性问题
|
|||
|
|
|
|||
|
|
#### 1.1 RHIResource 基类接口过于简单
|
|||
|
|
|
|||
|
|
**当前实现** ([RHIResource.h](file:///d:/Xuanchi/Main/XCEngine/engine/include/XCEngine/RHI/RHIResource.h)):
|
|||
|
|
```cpp
|
|||
|
|
class RHIResource {
|
|||
|
|
public:
|
|||
|
|
virtual ~RHIResource() = default;
|
|||
|
|
virtual void* GetNativeHandle() = 0;
|
|||
|
|
virtual ResourceStates GetState() const = 0;
|
|||
|
|
virtual void SetState(ResourceStates state) = 0;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- 缺少资源命名接口 (`SetName`/`GetName`)
|
|||
|
|
- 缺少资源类型查询接口
|
|||
|
|
- 缺少资源大小查询接口
|
|||
|
|
|
|||
|
|
**影响**: RHITexture 自行添加了 `SetName`/`GetName`/`GetWidth` 等方法,导致接口不统一。
|
|||
|
|
|
|||
|
|
**建议**: 扩展 RHIResource 基类接口:
|
|||
|
|
```cpp
|
|||
|
|
class RHIResource {
|
|||
|
|
public:
|
|||
|
|
virtual ~RHIResource() = default;
|
|||
|
|
|
|||
|
|
// 现有接口
|
|||
|
|
virtual void* GetNativeHandle() = 0;
|
|||
|
|
virtual ResourceStates GetState() const = 0;
|
|||
|
|
virtual void SetState(ResourceStates state) = 0;
|
|||
|
|
|
|||
|
|
// 建议新增
|
|||
|
|
virtual void SetName(const char* name) = 0;
|
|||
|
|
virtual const char* GetName() const = 0;
|
|||
|
|
virtual ResourceType GetResourceType() const = 0;
|
|||
|
|
virtual uint64_t GetGpuVirtualAddress() const = 0; // 对于 Buffer
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 1.2 资源生命周期管理不一致
|
|||
|
|
|
|||
|
|
**当前问题**:
|
|||
|
|
- 所有资源使用 `Shutdown()` + `delete` 的手动管理方式
|
|||
|
|
- 容易导致资源泄漏或重复释放
|
|||
|
|
- 与引擎 Core 模块的智能指针体系 (`Ref`, `UniqueRef`) 不统一
|
|||
|
|
|
|||
|
|
**建议**:
|
|||
|
|
1. 考虑使用引用计数管理 RHI 资源
|
|||
|
|
2. 或者提供 RAII 包装器类
|
|||
|
|
3. 至少应该提供统一的资源销毁接口(如 `RHIDevice::DestroyResource()`)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. 描述符系统设计问题
|
|||
|
|
|
|||
|
|
#### 2.1 描述符接口偏向 D3D12
|
|||
|
|
|
|||
|
|
**当前实现** ([RHIDescriptorPool.h](file:///d:/Xuanchi/Main/XCEngine/engine/include/XCEngine/RHI/RHIDescriptorPool.h)):
|
|||
|
|
```cpp
|
|||
|
|
struct DescriptorPoolDesc {
|
|||
|
|
uint32_t maxDescriptorCount; // D3D12 风格
|
|||
|
|
// ...
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- OpenGL 没有原生的描述符池概念
|
|||
|
|
- OpenGL 后端需要完全模拟,可能效率低下
|
|||
|
|
- 缺少 DescriptorSetLayout 的概念
|
|||
|
|
|
|||
|
|
**建议**: 参考 Vulkan 的描述符系统设计:
|
|||
|
|
```cpp
|
|||
|
|
// 1. 先创建 DescriptorSetLayout
|
|||
|
|
struct DescriptorSetLayoutDesc {
|
|||
|
|
std::vector<DescriptorBinding> bindings;
|
|||
|
|
};
|
|||
|
|
class RHIDescriptorSetLayout { ... };
|
|||
|
|
|
|||
|
|
// 2. 从 Layout 创建 Pool
|
|||
|
|
struct DescriptorPoolDesc {
|
|||
|
|
std::vector<DescriptorPoolSize> poolSizes; // 按类型指定数量
|
|||
|
|
uint32_t maxSets;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 3. 从 Pool 分配 Set
|
|||
|
|
class RHIDescriptorSet {
|
|||
|
|
virtual void Update(uint32_t binding, RHIResource* resource) = 0;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 2.2 缺少描述符集绑定机制
|
|||
|
|
|
|||
|
|
**当前问题**:
|
|||
|
|
- RHICommandList 直接绑定资源 (`SetVertexBuffer`, `SetTexture`)
|
|||
|
|
- 缺少批量绑定描述符集的能力
|
|||
|
|
|
|||
|
|
**建议**: 添加描述符集绑定接口:
|
|||
|
|
```cpp
|
|||
|
|
virtual void BindDescriptorSet(
|
|||
|
|
uint32_t setIndex,
|
|||
|
|
RHIDescriptorSet* descriptorSet,
|
|||
|
|
RHIPipelineLayout* layout
|
|||
|
|
) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. 命令列表设计问题
|
|||
|
|
|
|||
|
|
#### 3.1 命令列表生命周期管理
|
|||
|
|
|
|||
|
|
**当前问题**:
|
|||
|
|
- 缺少命令列表池化管理
|
|||
|
|
- 每帧可能需要重新创建命令列表
|
|||
|
|
|
|||
|
|
**建议**: 添加命令分配器概念:
|
|||
|
|
```cpp
|
|||
|
|
class RHICommandAllocator {
|
|||
|
|
public:
|
|||
|
|
virtual void Reset() = 0; // 重用命令列表内存
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Device 接口
|
|||
|
|
virtual RHICommandAllocator* CreateCommandAllocator(
|
|||
|
|
CommandListType type
|
|||
|
|
) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 3.2 屏障接口不够清晰
|
|||
|
|
|
|||
|
|
**当前实现**:
|
|||
|
|
```cpp
|
|||
|
|
virtual void ResourceBarrier(
|
|||
|
|
uint32_t numBarriers,
|
|||
|
|
ResourceBarrierDesc* barriers
|
|||
|
|
) = 0;
|
|||
|
|
|
|||
|
|
virtual void ExecuteBarriers() = 0; // 这个方法名容易误解
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**问题**: `ExecuteBarriers` 方法名暗示有延迟执行,但实际行为不明确。
|
|||
|
|
|
|||
|
|
**建议**:
|
|||
|
|
- 明确屏障是立即执行还是批量执行
|
|||
|
|
- 考虑添加 `FlushBarriers()` 方法名替代 `ExecuteBarriers()`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. 着色器系统问题
|
|||
|
|
|
|||
|
|
#### 4.1 编译接口职责混乱
|
|||
|
|
|
|||
|
|
**当前实现** ([RHIShader.h](file:///d:/Xuanchi/Main/XCEngine/engine/include/XCEngine/RHI/RHIShader.h)):
|
|||
|
|
```cpp
|
|||
|
|
class RHIShader {
|
|||
|
|
public:
|
|||
|
|
// RHIShader 自己有编译方法
|
|||
|
|
virtual bool CompileFromFile(...) = 0;
|
|||
|
|
virtual bool Compile(...) = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// RHIDevice 也有编译方法
|
|||
|
|
virtual RHIShader* CompileShader(const ShaderCompileDesc& desc) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**问题**: 编译职责分散,用户不知道该用哪个接口。
|
|||
|
|
|
|||
|
|
**建议**: 统一着色器创建流程:
|
|||
|
|
```cpp
|
|||
|
|
// 方案1: 只在 Device 中创建
|
|||
|
|
class RHIDevice {
|
|||
|
|
virtual RHIShader* CreateShader(const ShaderDesc& desc) = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 方案2: 使用 ShaderCompiler 工具类
|
|||
|
|
class RHIShaderCompiler {
|
|||
|
|
virtual RHIShader* CompileFromFile(...) = 0;
|
|||
|
|
virtual RHIShader* CompileFromSource(...) = 0;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 4.2 缺少着色器反射接口
|
|||
|
|
|
|||
|
|
**当前问题**:
|
|||
|
|
- 只有 `GetUniformInfos()` 方法
|
|||
|
|
- 缺少完整的着色器反射(输入/输出语义、资源绑定等)
|
|||
|
|
|
|||
|
|
**建议**: 添加完整的着色器反射接口:
|
|||
|
|
```cpp
|
|||
|
|
struct ShaderReflection {
|
|||
|
|
std::vector<VertexInputAttribute> vertexInputs;
|
|||
|
|
std::vector<FragmentOutput> fragmentOutputs;
|
|||
|
|
std::vector<ResourceBinding> resourceBindings;
|
|||
|
|
std::vector<PushConstantRange> pushConstantRanges;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
virtual ShaderReflection GetReflection() const = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. 管线状态对象问题
|
|||
|
|
|
|||
|
|
#### 5.1 缺少管线缓存
|
|||
|
|
|
|||
|
|
**当前问题**: 每次创建 PSO 都需要完整编译,没有缓存机制。
|
|||
|
|
|
|||
|
|
**建议**: 添加管线缓存接口:
|
|||
|
|
```cpp
|
|||
|
|
class RHIPipelineCache {
|
|||
|
|
public:
|
|||
|
|
virtual void Save(const char* filePath) = 0;
|
|||
|
|
virtual void Load(const char* filePath) = 0;
|
|||
|
|
virtual size_t GetDataSize() const = 0;
|
|||
|
|
virtual void* GetData() const = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Device 接口
|
|||
|
|
virtual RHIPipelineCache* CreatePipelineCache() = 0;
|
|||
|
|
virtual RHIPipelineState* CreatePipelineState(
|
|||
|
|
const PipelineStateDesc& desc,
|
|||
|
|
RHIPipelineCache* cache = nullptr
|
|||
|
|
) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 6. 缺失的关键功能
|
|||
|
|
|
|||
|
|
#### 6.1 缺少资源别名 (Aliasing) 支持
|
|||
|
|
|
|||
|
|
**设计文档提到**: "求同存异"
|
|||
|
|
|
|||
|
|
**问题**: D3D12 和 Vulkan 都支持资源别名(多个资源共享同一块内存),但当前 RHI 没有暴露。
|
|||
|
|
|
|||
|
|
**建议**: 添加资源别名支持:
|
|||
|
|
```cpp
|
|||
|
|
struct ResourceAliasingDesc {
|
|||
|
|
RHIResource* overlappingResources;
|
|||
|
|
uint32_t resourceCount;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
virtual void AliasingResources(
|
|||
|
|
const ResourceAliasingDesc& desc
|
|||
|
|
) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 6.2 缺少多队列支持
|
|||
|
|
|
|||
|
|
**当前问题**:
|
|||
|
|
- RHICommandQueue 只支持单一类型
|
|||
|
|
- 缺少多队列同步机制
|
|||
|
|
|
|||
|
|
**建议**: 添加跨队列同步支持:
|
|||
|
|
```cpp
|
|||
|
|
struct QueueSyncDesc {
|
|||
|
|
RHICommandQueue* waitQueue;
|
|||
|
|
RHIFence* fence;
|
|||
|
|
uint64_t value;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
virtual void QueueSubmit(
|
|||
|
|
const QueueSubmitDesc& desc,
|
|||
|
|
const QueueSyncDesc* waitSyncs = nullptr,
|
|||
|
|
uint32_t waitSyncCount = 0,
|
|||
|
|
const QueueSyncDesc* signalSyncs = nullptr,
|
|||
|
|
uint32_t signalSyncCount = 0
|
|||
|
|
) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 6.3 缺少 RenderGraph 支持
|
|||
|
|
|
|||
|
|
**设计文档提到**: "渲染管线层(SRP/Render Graph)"
|
|||
|
|
|
|||
|
|
**问题**: 当前 RHI 接口没有为 RenderGraph 提供必要的支持:
|
|||
|
|
- 缺少资源传递机制
|
|||
|
|
- 缺少自动屏障生成
|
|||
|
|
- 缺少资源生命周期跟踪
|
|||
|
|
|
|||
|
|
**建议**: 添加 RenderGraph 友好的接口:
|
|||
|
|
```cpp
|
|||
|
|
struct ResourceTransition {
|
|||
|
|
RHIResource* resource;
|
|||
|
|
ResourceStates fromState;
|
|||
|
|
ResourceStates toState;
|
|||
|
|
uint32_t queueFamily; // 支持跨队列传递
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
virtual void TransitionResources(
|
|||
|
|
const ResourceTransition* transitions,
|
|||
|
|
uint32_t count
|
|||
|
|
) = 0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、实现问题
|
|||
|
|
|
|||
|
|
### 1. OpenGL 后端状态管理
|
|||
|
|
|
|||
|
|
**问题**: OpenGL 是状态机模型,需要仔细管理状态。
|
|||
|
|
|
|||
|
|
**建议检查点**:
|
|||
|
|
- [ ] 是否正确跟踪当前绑定的 VAO/VBO/纹理
|
|||
|
|
- [ ] 是否正确跟踪当前激活的着色器程序
|
|||
|
|
- [ ] 是否正确跟踪混合/深度/模板状态
|
|||
|
|
- [ ] 是否有不必要的冗余状态切换
|
|||
|
|
|
|||
|
|
**建议**: 实现状态跟踪器:
|
|||
|
|
```cpp
|
|||
|
|
class OpenGLStateTracker {
|
|||
|
|
public:
|
|||
|
|
void BindTexture(uint32_t unit, uint32_t texture);
|
|||
|
|
void BindVertexArray(uint32_t vao);
|
|||
|
|
void UseProgram(uint32_t program);
|
|||
|
|
// ... 避免重复绑定
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. 资源状态跟踪不一致
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- D3D12 需要精确的资源状态跟踪
|
|||
|
|
- OpenGL 没有显式的资源状态概念
|
|||
|
|
- 当前实现在 OpenGL 后端如何处理状态?
|
|||
|
|
|
|||
|
|
**建议**:
|
|||
|
|
- OpenGL 后端可以忽略资源状态(或仅用于调试)
|
|||
|
|
- 或者实现软件状态跟踪用于验证
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. 内存管理缺失
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- 缺少显式的 GPU 内存分配接口
|
|||
|
|
- D3D12 有 Heap 概念,OpenGL 没有
|
|||
|
|
|
|||
|
|
**建议**: 添加内存分配抽象:
|
|||
|
|
```cpp
|
|||
|
|
struct HeapDesc {
|
|||
|
|
HeapType type; // Default, Upload, Readback
|
|||
|
|
uint64_t size;
|
|||
|
|
HeapFlags flags;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
class RHIHeap {
|
|||
|
|
public:
|
|||
|
|
virtual void* Map() = 0;
|
|||
|
|
virtual void Unmap() = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 资源创建时指定堆
|
|||
|
|
struct BufferDesc {
|
|||
|
|
// ...
|
|||
|
|
RHIHeap* heap; // 可选
|
|||
|
|
uint64_t heapOffset; // 可选
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. 错误处理机制
|
|||
|
|
|
|||
|
|
**当前问题**:
|
|||
|
|
- 大部分方法返回 `void` 或 `bool`
|
|||
|
|
- 缺少详细的错误信息
|
|||
|
|
|
|||
|
|
**建议**: 添加结果类型:
|
|||
|
|
```cpp
|
|||
|
|
template<typename T>
|
|||
|
|
struct RHIResult {
|
|||
|
|
T value;
|
|||
|
|
RHIErrorCode error;
|
|||
|
|
const char* errorMessage;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 使用示例
|
|||
|
|
RHIResult<RHITexture*> result = device->CreateTexture(desc);
|
|||
|
|
if (result.error != RHIErrorCode::Success) {
|
|||
|
|
LogError(result.errorMessage);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、与设计理念的对比
|
|||
|
|
|
|||
|
|
### 设计理念 vs 实际实现
|
|||
|
|
|
|||
|
|
| 设计理念 | 设计文档要求 | 当前实现状态 | 问题 |
|
|||
|
|
|---------|------------|------------|------|
|
|||
|
|
| **求同存异** | 提取 API 共性 | ✅ 部分实现 | 描述符系统偏向 D3D12 |
|
|||
|
|
| **分层抽象** | 清晰的层级结构 | ✅ 已实现 | 层级清晰 |
|
|||
|
|
| **特性降级** | 能力检测和替代方案 | ⚠️ 部分实现 | RHICapabilities 存在但使用不明确 |
|
|||
|
|
| **底层逃逸** | 允许访问原生 API | ✅ 已实现 | GetNativeHandle() 接口存在 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、优先级建议
|
|||
|
|
|
|||
|
|
### 高优先级(影响功能正确性)
|
|||
|
|
|
|||
|
|
1. **统一资源生命周期管理** - 防止内存泄漏
|
|||
|
|
2. **完善错误处理机制** - 便于调试
|
|||
|
|
3. **OpenGL 状态跟踪** - 避免渲染错误
|
|||
|
|
|
|||
|
|
### 中优先级(影响性能和可维护性)
|
|||
|
|
|
|||
|
|
4. **重构描述符系统** - 提高 OpenGL 效率
|
|||
|
|
5. **统一着色器编译接口** - 提高易用性
|
|||
|
|
6. **添加命令分配器** - 提高性能
|
|||
|
|
|
|||
|
|
### 低优先级(增强功能)
|
|||
|
|
|
|||
|
|
7. **添加管线缓存** - 加速启动
|
|||
|
|
8. **添加资源别名支持** - 优化内存
|
|||
|
|
9. **RenderGraph 支持** - 高级功能
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、总结
|
|||
|
|
|
|||
|
|
### 优点
|
|||
|
|
|
|||
|
|
1. ✅ 分层架构清晰,符合设计文档
|
|||
|
|
2. ✅ 核心抽象接口基本完整
|
|||
|
|
3. ✅ D3D12 后端实现较为完善
|
|||
|
|
4. ✅ 测试覆盖较好
|
|||
|
|
|
|||
|
|
### 主要问题
|
|||
|
|
|
|||
|
|
1. ❌ 描述符系统设计偏向 D3D12,OpenGL 后端效率可能受影响
|
|||
|
|
2. ❌ 资源生命周期管理不一致,存在泄漏风险
|
|||
|
|
3. ❌ 着色器编译接口职责混乱
|
|||
|
|
4. ⚠️ 缺少部分高级功能(资源别名、多队列同步等)
|
|||
|
|
|
|||
|
|
### 建议
|
|||
|
|
|
|||
|
|
建议按照优先级逐步改进,首先解决高优先级问题,确保引擎的稳定性和正确性。
|