# 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 bindings; }; class RHIDescriptorSetLayout { ... }; // 2. 从 Layout 创建 Pool struct DescriptorPoolDesc { std::vector 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 vertexInputs; std::vector fragmentOutputs; std::vector resourceBindings; std::vector 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 struct RHIResult { T value; RHIErrorCode error; const char* errorMessage; }; // 使用示例 RHIResult 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. ⚠️ 缺少部分高级功能(资源别名、多队列同步等) ### 建议 建议按照优先级逐步改进,首先解决高优先级问题,确保引擎的稳定性和正确性。