Files
XCEngine/.trae/documents/RHI模块审查报告.md
ssdfasd 008fb98dee refactor(editor): Complete architecture refactoring
- SceneManager: remove singleton, use dependency injection via EditorContext
- SelectionManager: already interface-based via ISelectionManager
- Panel: now receives IEditorContext for accessing managers
- HierarchyPanel: migrated to use IEditorContext instead of singletons
- Add ISceneManager interface and SceneManagerImpl
- EditorContextImpl: holds all editor subsystems

Architecture now follows dependency injection pattern:
Application -> EditorContext -> SceneManager/SelectionManager
EditorLayer -> Panels (receive context via SetContext)

All Manager singletons removed: EditorSceneManager::Get(), SelectionManager::Get()
2026-03-25 15:51:27 +08:00

11 KiB
Raw Blame History

RHI 模块审查报告

审查范围

基于设计文档 RHI模块总览.md 中定义的设计理念,对当前 RHI 模块的设计和实现进行全面审查。


一、设计问题

1. 接口一致性问题

1.1 RHIResource 基类接口过于简单

当前实现 (RHIResource.h):

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 基类接口:

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):

struct DescriptorPoolDesc {
    uint32_t maxDescriptorCount;  // D3D12 风格
    // ...
};

问题:

  • OpenGL 没有原生的描述符池概念
  • OpenGL 后端需要完全模拟,可能效率低下
  • 缺少 DescriptorSetLayout 的概念

建议: 参考 Vulkan 的描述符系统设计:

// 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)
  • 缺少批量绑定描述符集的能力

建议: 添加描述符集绑定接口:

virtual void BindDescriptorSet(
    uint32_t setIndex, 
    RHIDescriptorSet* descriptorSet,
    RHIPipelineLayout* layout
) = 0;

3. 命令列表设计问题

3.1 命令列表生命周期管理

当前问题:

  • 缺少命令列表池化管理
  • 每帧可能需要重新创建命令列表

建议: 添加命令分配器概念:

class RHICommandAllocator {
public:
    virtual void Reset() = 0;  // 重用命令列表内存
};

// Device 接口
virtual RHICommandAllocator* CreateCommandAllocator(
    CommandListType type
) = 0;

3.2 屏障接口不够清晰

当前实现:

virtual void ResourceBarrier(
    uint32_t numBarriers, 
    ResourceBarrierDesc* barriers
) = 0;

virtual void ExecuteBarriers() = 0;  // 这个方法名容易误解

问题: ExecuteBarriers 方法名暗示有延迟执行,但实际行为不明确。

建议:

  • 明确屏障是立即执行还是批量执行
  • 考虑添加 FlushBarriers() 方法名替代 ExecuteBarriers()

4. 着色器系统问题

4.1 编译接口职责混乱

当前实现 (RHIShader.h):

class RHIShader {
public:
    // RHIShader 自己有编译方法
    virtual bool CompileFromFile(...) = 0;
    virtual bool Compile(...) = 0;
};

// RHIDevice 也有编译方法
virtual RHIShader* CompileShader(const ShaderCompileDesc& desc) = 0;

问题: 编译职责分散,用户不知道该用哪个接口。

建议: 统一着色器创建流程:

// 方案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() 方法
  • 缺少完整的着色器反射(输入/输出语义、资源绑定等)

建议: 添加完整的着色器反射接口:

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 都需要完整编译,没有缓存机制。

建议: 添加管线缓存接口:

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 没有暴露。

建议: 添加资源别名支持:

struct ResourceAliasingDesc {
    RHIResource* overlappingResources;
    uint32_t resourceCount;
};

virtual void AliasingResources(
    const ResourceAliasingDesc& desc
) = 0;

6.2 缺少多队列支持

当前问题:

  • RHICommandQueue 只支持单一类型
  • 缺少多队列同步机制

建议: 添加跨队列同步支持:

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 友好的接口:

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/纹理
  • 是否正确跟踪当前激活的着色器程序
  • 是否正确跟踪混合/深度/模板状态
  • 是否有不必要的冗余状态切换

建议: 实现状态跟踪器:

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 没有

建议: 添加内存分配抽象:

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. 错误处理机制

当前问题:

  • 大部分方法返回 voidbool
  • 缺少详细的错误信息

建议: 添加结果类型:

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 状态跟踪 - 避免渲染错误

中优先级(影响性能和可维护性)

  1. 重构描述符系统 - 提高 OpenGL 效率
  2. 统一着色器编译接口 - 提高易用性
  3. 添加命令分配器 - 提高性能

低优先级(增强功能)

  1. 添加管线缓存 - 加速启动
  2. 添加资源别名支持 - 优化内存
  3. RenderGraph 支持 - 高级功能

五、总结

优点

  1. 分层架构清晰,符合设计文档
  2. 核心抽象接口基本完整
  3. D3D12 后端实现较为完善
  4. 测试覆盖较好

主要问题

  1. 描述符系统设计偏向 D3D12OpenGL 后端效率可能受影响
  2. 资源生命周期管理不一致,存在泄漏风险
  3. 着色器编译接口职责混乱
  4. ⚠️ 缺少部分高级功能(资源别名、多队列同步等)

建议

建议按照优先级逐步改进,首先解决高优先级问题,确保引擎的稳定性和正确性。