diff --git a/docs/RHI抽象层设计与实现.md b/docs/RHI抽象层设计与实现.md index e69de29b..1e6ab2f5 100644 --- a/docs/RHI抽象层设计与实现.md +++ b/docs/RHI抽象层设计与实现.md @@ -0,0 +1,404 @@ +# 跨平台 RHI 渲染架构设计文档 +## 1. 项目背景 +本项目旨在参考 Unity 渲染架构,为已有的 **OpenGL** 和 **Direct3D 12** 图形 API 后端设计统一的**渲染硬件抽象层(RHI)**,屏蔽 API 差异,实现引擎上层逻辑与底层图形 API 的解耦。 + +### 现有项目结构 +``` +engine/ +├── include/XCEngine/RHI/ +│ ├── Enums.h # 通用枚举定义 +│ ├── Types.h # 通用结构体定义 +│ ├── D3D12/ # D3D12 后端实现 +│ └── OpenGL/ # OpenGL 后端实现 +└── src/RHI/ + ├── D3D12/ # D3D12 源码 + └── OpenGL/ # OpenGL 源码 +``` + + +## 2. 核心设计理念 +### 2.1 总体原则 +**求同存异,分层抽象,特性降级,底层逃逸** +- **求同存异**:优先提取 API 共性作为核心抽象,差异部分通过模拟/降级处理 +- **分层抽象**:通过清晰的层级结构隔离 API 差异 +- **特性降级**:对高级特性提供能力检测和替代方案 +- **底层逃逸**:允许直接访问原生 API 以满足极端需求 + +### 2.2 差异分类与处理策略 +| 差异类型 | 典型示例 | 处理方案 | +|----------|----------|----------| +| 概念命名不同但本质相似 | D3D12 `CommandList` ≈ OpenGL 状态机绘制 | 直接统一抽象 | +| 显式控制 vs 隐式管理 | D3D12 `DescriptorHeap` vs OpenGL 纹理单元 | 提供"自动模式"+"显式模式" | +| API 独有高级特性 | D3D12 光线追踪、网格着色器 | 特性检测 + 降级方案 + 底层逃逸 | + + +## 3. RHI 分层架构 +### 3.1 通用分层模型 +``` +┌─────────────────────────────────┐ +│ 引擎功能层(场景/材质/光照) │ +├─────────────────────────────────┤ +│ 渲染管线层(SRP/Render Graph) │ +├─────────────────────────────────┤ +│ RHI 抽象层(统一接口) │ ← 核心设计重点 +├─────────────────────────────────┤ +│ API 后端层(D3D12/OpenGL) │ +├─────────────────────────────────┤ +│ 驱动/硬件层 │ +└─────────────────────────────────┘ +``` + +### 3.2 层级职责说明 +1. **引擎功能层**:提供场景管理、材质系统、光照等高级功能接口 +2. **渲染管线层**:定义渲染流程(前向/延迟渲染) +3. **RHI 抽象层**:统一图形 API 接口,屏蔽差异 +4. **API 后端层**:针对具体 API 的实现 +5. **驱动/硬件层**:最终执行渲染指令 + + +## 4. RHI 抽象基类设计 +### 4.1 目录结构调整建议 +``` +include/XCEngine/RHI/ +├── Enums.h # 通用枚举 +├── Types.h # 通用结构体 +├── RHICapabilities.h # 硬件能力检测 +├── RHIDevice.h # 设备抽象(核心入口) +├── RHICommandQueue.h # 命令队列抽象 +├── RHICommandList.h # 命令列表抽象 +├── RHIBuffer.h # 缓冲区抽象 +├── RHITexture.h # 纹理抽象 +├── RHIShader.h # 着色器抽象 +├── RHIPipelineLayout.h # 管线布局抽象(替代 RootSignature) +├── RHIPipelineState.h # 管线状态抽象 +├── RHISwapChain.h # 交换链抽象 +├── RHIFence.h # 同步栅栏抽象 +├── RHIDescriptorPool.h # 描述符池抽象 +└── RHIFactory.h # RHI 工厂类 +``` + +### 4.2 核心抽象基类定义 +#### 4.2.1 通用类型定义(Types.h) +```cpp +#pragma once +#include + +// 通用资源格式 +enum class RHIFormat { + R8G8B8A8_UNORM, + D32_FLOAT, + // ... 其他格式 +}; + +// 缓冲区用途 +enum class RHIBufferUsage { + VertexBuffer, + IndexBuffer, + ConstantBuffer, + // ... 其他用途 +}; + +// 通用缓冲区描述 +struct RHIBufferDesc { + uint64_t size; + RHIBufferUsage usage; + // ... 其他参数 +}; + +// 通用纹理描述 +struct RHITextureDesc { + uint32_t width; + uint32_t height; + RHIFormat format; + // ... 其他参数 +}; + +// 通用渲染通道描述 +struct RHIRenderPassDesc { + struct Attachment { + RHITexture* texture; + float clearColor[4]; + // ... 其他参数 + } colorAttachments[8]; + uint32_t colorAttachmentCount; + Attachment depthStencilAttachment; + // ... 其他参数 +}; +``` + +#### 4.2.2 硬件能力检测(RHICapabilities.h) +```cpp +#pragma once + +struct RHICapabilities { + bool bSupportsRayTracing = false; + bool bSupportsMeshShaders = false; + bool bSupportsExplicitMultiThreading = false; + // ... 其他特性 +}; +``` + +#### 4.2.3 设备抽象(RHIDevice.h) +```cpp +#pragma once +#include "Types.h" +#include "RHICapabilities.h" + +class RHISwapChain; +class RHICommandQueue; +class RHIBuffer; +class RHITexture; + +class RHIDevice { +public: + virtual ~RHIDevice() = default; + + // 初始化设备 + virtual bool Initialize(const RHIDeviceDesc& desc) = 0; + + // 资源创建接口 + virtual RHIBuffer* CreateBuffer(const RHIBufferDesc& desc) = 0; + virtual RHITexture* CreateTexture(const RHITextureDesc& desc) = 0; + virtual RHICommandQueue* CreateCommandQueue(const RHICommandQueueDesc& desc) = 0; + virtual RHISwapChain* CreateSwapChain(const RHISwapChainDesc& desc) = 0; + + // 硬件能力查询 + virtual const RHICapabilities& GetCapabilities() const = 0; + + // 底层逃逸口(谨慎使用) + virtual void* GetNativeDevice() = 0; + + // 清理 + virtual void Shutdown() = 0; +}; +``` + +#### 4.2.4 命令列表抽象(RHICommandList.h) +```cpp +#pragma once +#include "Types.h" + +class RHIBuffer; +class RHITexture; +class RHIPipelineState; + +class RHICommandList { +public: + virtual ~RHICommandList() = default; + + // 命令录制 + virtual void Begin() = 0; + virtual void End() = 0; + + // 渲染通道 + virtual void BeginRenderPass(const RHIRenderPassDesc& desc) = 0; + virtual void EndRenderPass() = 0; + + // 状态设置 + virtual void SetPipelineState(RHIPipelineState* pso) = 0; + virtual void SetVertexBuffers(uint32_t slot, RHIBuffer* const* buffers, uint32_t count) = 0; + virtual void SetIndexBuffer(RHIBuffer* buffer, RHIFormat format, uint32_t offset) = 0; + + // 绘制命令 + virtual void DrawIndexed(uint32_t indexCount, uint32_t instanceCount, + uint32_t startIndex, int32_t baseVertex, + uint32_t startInstance) = 0; + + // 资源拷贝 + virtual void CopyBuffer(RHIBuffer* dst, RHIBuffer* src, uint64_t size) = 0; +}; +``` + +#### 4.2.5 管线布局抽象(RHIPipelineLayout.h) +```cpp +#pragma once +#include "Types.h" + +// 管线布局描述(统一描述资源绑定规则) +struct RHIPipelineLayoutDesc { + uint32_t constantBufferCount; + uint32_t textureCount; + uint32_t samplerCount; + // ... 其他参数 +}; + +class RHIPipelineLayout { +public: + virtual ~RHIPipelineLayout() = default; + // 具体实现由后端处理 +}; +``` + +#### 4.2.6 RHI 工厂类(RHIFactory.h) +```cpp +#pragma once +#include "RHIDevice.h" +#include + +enum class RHIType { + D3D12, + OpenGL, + // 未来扩展 Vulkan/Metal +}; + +class RHIFactory { +public: + // 根据类型创建 RHI 设备 + static RHIDevice* CreateRHIDevice(RHIType type); + + // 根据字符串选择(从配置文件读取) + static RHIDevice* CreateRHIDevice(const std::string& typeName); +}; +``` + + +## 5. 核心差异处理方案 +### 5.1 根签名 vs OpenGL 资源绑定 +- **D3D12**:显式 `RootSignature` 定义资源绑定规则 +- **OpenGL**:隐式通过 `glUniformLocation`、`glBindTextureUnit` 绑定 +- **解决方案**: + - 用 `RHIPipelineLayout` 统一抽象 + - D3D12 后端:内部创建 `ID3D12RootSignature` + - OpenGL 后端:存储绑定点数量,绘制时自动调用 `glBind*` + +### 5.2 描述符堆 vs OpenGL 纹理单元 +- **D3D12**:显式 `DescriptorHeap` 管理资源视图 +- **OpenGL**:隐式通过 `glActiveTexture` 绑定 +- **解决方案**: + - 用 `RHIDescriptorPool` 统一抽象 + - 提供"自动模式"(默认)和"显式模式"(可选) + - D3D12 后端:从堆分配槽位,更新描述符 + - OpenGL 后端:维护绑定点计数器,绘制时自动绑定 + +### 5.3 多线程命令录制 +- **D3D12**:原生支持多线程录制不同 `CommandList` +- **OpenGL**:状态机模型,单线程上下文 +- **解决方案**: + - 抽象层统一提供多线程接口 + - D3D12 后端:真·并行提交 + - OpenGL 后端:命令缓冲队列,单线程回放 + +### 5.4 高级特性(光线追踪等) +- **解决方案**: + 1. **特性检测**:通过 `RHICapabilities` 查询支持情况 + 2. **降级方案**:不支持时用传统路径替代 + 3. **底层逃逸**:通过 `GetNativeDevice()` 直接访问原生 API + + +## 6. 后端实现示例 +### 6.1 D3D12 设备实现(D3D12Device.h) +```cpp +#pragma once +#include "../RHIDevice.h" +#include "D3D12Common.h" + +class D3D12Device : public RHIDevice { +public: + virtual bool Initialize(const RHIDeviceDesc& desc) override; + virtual RHIBuffer* CreateBuffer(const RHIBufferDesc& desc) override; + virtual RHITexture* CreateTexture(const RHITextureDesc& desc) override; + virtual RHICommandQueue* CreateCommandQueue(const RHICommandQueueDesc& desc) override; + virtual RHISwapChain* CreateSwapChain(const RHISwapChainDesc& desc) override; + virtual const RHICapabilities& GetCapabilities() const override; + virtual void* GetNativeDevice() override { return m_pd3d12Device; } + virtual void Shutdown() override; + + // D3D12 特有接口 + ID3D12Device* GetD3D12Device() const { return m_pd3d12Device; } + +private: + ID3D12Device* m_pd3d12Device = nullptr; + ID3D12CommandQueue* m_pd3d12CommandQueue = nullptr; + RHICapabilities m_capabilities; +}; +``` + +### 6.2 OpenGL 设备实现(OpenGLDevice.h) +```cpp +#pragma once +#include "../RHIDevice.h" +#include "OpenGLCommon.h" + +class OpenGLDevice : public RHIDevice { +public: + virtual bool Initialize(const RHIDeviceDesc& desc) override; + virtual RHIBuffer* CreateBuffer(const RHIBufferDesc& desc) override; + virtual RHITexture* CreateTexture(const RHITextureDesc& desc) override; + virtual RHICommandQueue* CreateCommandQueue(const RHICommandQueueDesc& desc) override; + virtual RHISwapChain* CreateSwapChain(const RHISwapChainDesc& desc) override; + virtual const RHICapabilities& GetCapabilities() const override; + virtual void* GetNativeDevice() override { return m_context; } + virtual void Shutdown() override; + + // OpenGL 特有接口 + HGLRC GetContext() const { return m_context; } + +private: + HGLRC m_context = nullptr; + RHICapabilities m_capabilities; +}; +``` + + +## 7. 测试用例示例 +### 7.1 最小渲染循环测试 +```cpp +#include "XCEngine/RHI/RHIFactory.h" +#include "XCEngine/RHI/RHIDevice.h" +#include "XCEngine/RHI/RHISwapChain.h" +#include "XCEngine/RHI/RHICommandList.h" + +int main() { + // 1. 创建 RHI 设备(可切换 D3D12/OpenGL) + RHIDevice* pDevice = RHIFactory::CreateRHIDevice(RHIType::D3D12); + pDevice->Initialize(RHIDeviceDesc{...}); + + // 2. 创建交换链和命令队列 + RHISwapChain* pSwapChain = pDevice->CreateSwapChain(RHISwapChainDesc{...}); + RHICommandQueue* pCommandQueue = pDevice->CreateCommandQueue(RHICommandQueueDesc{...}); + RHICommandList* pCommandList = pDevice->CreateCommandList(RHICommandListDesc{...}); + + // 3. 渲染循环 + while (!ShouldQuit()) { + // 录制命令 + pCommandList->Begin(); + + // 清屏 + RHIRenderPassDesc renderPassDesc = {}; + renderPassDesc.colorAttachments[0].texture = pSwapChain->GetCurrentBackBuffer(); + renderPassDesc.colorAttachments[0].loadOp = RHIRenderPassLoadOp::Clear; + renderPassDesc.colorAttachments[0].clearColor = {0.2f, 0.4f, 0.8f, 1.0f}; + pCommandList->BeginRenderPass(renderPassDesc); + pCommandList->EndRenderPass(); + + // 提交命令 + pCommandList->End(); + pCommandQueue->SubmitCommandList(pCommandList); + + // 呈现 + pSwapChain->Present(); + } + + // 4. 清理 + pDevice->Shutdown(); + delete pDevice; + return 0; +} +``` + + +## 8. 下一步行动指南 +1. **补全抽象基类**:重点实现 `RHIDevice`、`RHICommandList`、`RHIPipelineLayout` +2. **改造现有后端**:让 `D3D12Device`、`OpenGLDevice` 继承抽象基类并实现接口 +3. **实现工厂类**:完成 `RHIFactory` 以支持动态切换后端 +4. **测试验证**:用最小渲染循环测试 D3D12 和 OpenGL 后端 +5. **扩展功能**:逐步添加资源管理、着色器跨平台、多线程渲染等功能 + + +## 9. 关键注意事项 +- **上层只调用抽象接口**:绝不直接访问 D3D12/OpenGL 特有类 +- **合理使用底层逃逸**:仅在必要时使用 `GetNativeDevice()`,并注明破坏跨平台性 +- **优先保证核心功能**:先实现 90% 常用功能的统一抽象,再处理高级特性 +- **保持设计可扩展**:为未来支持 Vulkan/Metal 预留空间 + +需要我帮你细化某个具体模块的实现代码吗? \ No newline at end of file diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLFence.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLFence.h index 12e7d7dc..9cce69aa 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLFence.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLFence.h @@ -6,23 +6,34 @@ namespace XCEngine { namespace RHI { +enum class FenceStatus { + Signaled, + Unsignaled, + Error +}; + class OpenGLFence { public: OpenGLFence(); ~OpenGLFence(); - bool Initialize(); + bool Initialize(bool signaled = false); void Shutdown(); void Signal(); - void Wait(); + void Signal(uint64_t value); + void Wait(uint64_t timeoutNs = UINT64_MAX); void Reset(); bool IsSignaled() const; + FenceStatus GetStatus() const; + uint64_t GetCompletedValue() const; + uint64_t GetCurrentValue() const { return m_fenceValue; } private: - unsigned int m_fence; - int64_t m_fenceValue; + void* m_sync; + uint64_t m_fenceValue; + uint64_t m_completedValue; bool m_signaled; }; diff --git a/engine/src/RHI/OpenGL/OpenGLFence.cpp b/engine/src/RHI/OpenGL/OpenGLFence.cpp index 8d914d63..e7492e86 100644 --- a/engine/src/RHI/OpenGL/OpenGLFence.cpp +++ b/engine/src/RHI/OpenGL/OpenGLFence.cpp @@ -2,36 +2,71 @@ #include "XCEngine/RHI/OpenGL/OpenGLFence.h" #include #include -#include -#include namespace XCEngine { namespace RHI { -OpenGLFence::OpenGLFence() : m_fence(0), m_fenceValue(0), m_signaled(false) { +OpenGLFence::OpenGLFence() + : m_sync(nullptr) + , m_fenceValue(0) + , m_completedValue(0) + , m_signaled(false) { } OpenGLFence::~OpenGLFence() { + Shutdown(); } -bool OpenGLFence::Initialize() { +bool OpenGLFence::Initialize(bool signaled) { + m_fenceValue = signaled ? 1 : 0; + m_completedValue = m_fenceValue; + m_signaled = signaled; return true; } void OpenGLFence::Shutdown() { + if (m_sync) { + glDeleteSync(static_cast(m_sync)); + m_sync = nullptr; + } } void OpenGLFence::Signal() { - glFinish(); - m_signaled = true; + glFlush(); m_fenceValue++; + m_signaled = true; } -void OpenGLFence::Wait() { - glFinish(); +void OpenGLFence::Signal(uint64_t value) { + glFlush(); + m_fenceValue = value; + m_signaled = true; +} + +void OpenGLFence::Wait(uint64_t timeoutNs) { + if (!m_signaled || !m_sync) { + glFinish(); + m_completedValue = m_fenceValue; + m_signaled = true; + return; + } + + GLsync sync = static_cast(m_sync); + GLenum result = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, timeoutNs); + while (result == GL_TIMEOUT_EXPIRED && timeoutNs > 0) { + result = glClientWaitSync(sync, 0, timeoutNs); + } + + if (result == GL_ALREADY_SIGNALED || result == GL_CONDITION_SATISFIED) { + m_completedValue = m_fenceValue; + } } void OpenGLFence::Reset() { + if (m_sync) { + glDeleteSync(static_cast(m_sync)); + m_sync = nullptr; + } m_signaled = false; } @@ -39,5 +74,24 @@ bool OpenGLFence::IsSignaled() const { return m_signaled; } +FenceStatus OpenGLFence::GetStatus() const { + if (!m_sync) { + return m_signaled ? FenceStatus::Signaled : FenceStatus::Unsignaled; + } + + GLsync sync = static_cast(m_sync); + GLint status = 0; + glGetSynciv(sync, GL_SYNC_STATUS, sizeof(status), nullptr, &status); + + if (status == GL_SIGNALED) { + return FenceStatus::Signaled; + } + return FenceStatus::Unsignaled; +} + +uint64_t OpenGLFence::GetCompletedValue() const { + return m_completedValue; +} + } // namespace RHI } // namespace XCEngine