OpenGLRenderTargetView: - Add RenderTargetType enum for different texture types - Add RenderTargetViewDesc struct with mip level, array slice, layer info - Add Initialize() with desc parameter for 2D/2DArray/3D/Cube - Add InitializeCubemap() for cubemap faces - Add Bind(count) overload for multiple framebuffers - Add Clear() methods for color and depth-stencil - Add static BindFramebuffer/UnbindFramebuffer methods OpenGLDepthStencilView: - Add DepthStencilType enum for different texture types - Add DepthStencilViewDesc struct with mip level, array slice, layer info - Add Initialize() with desc parameter for 2D/2DArray/Cube - Add InitializeCubemap() for cubemap faces - Add ClearDepth, ClearStencil, ClearDepthStencil methods - Add static BindFramebuffer/UnbindFramebuffer methods
13 KiB
13 KiB
跨平台 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 层级职责说明
- 引擎功能层:提供场景管理、材质系统、光照等高级功能接口
- 渲染管线层:定义渲染流程(前向/延迟渲染)
- RHI 抽象层:统一图形 API 接口,屏蔽差异
- API 后端层:针对具体 API 的实现
- 驱动/硬件层:最终执行渲染指令
4. RHI 抽象基类设计
4.1 目录结构调整建议
include/XCEngine/RHI/
├── RHIEnums.h # 通用枚举
├── RHITypes.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)
#pragma once
#include <cstdint>
// 通用资源格式
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)
#pragma once
struct RHICapabilities {
bool bSupportsRayTracing = false;
bool bSupportsMeshShaders = false;
bool bSupportsExplicitMultiThreading = false;
// ... 其他特性
};
4.2.3 设备抽象(RHIDevice.h)
#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)
#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)
#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)
#pragma once
#include "RHIDevice.h"
#include <string>
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 高级特性(光线追踪等)
- 解决方案:
- 特性检测:通过
RHICapabilities查询支持情况 - 降级方案:不支持时用传统路径替代
- 底层逃逸:通过
GetNativeDevice()直接访问原生 API
- 特性检测:通过
6. 后端实现示例
6.1 D3D12 设备实现(D3D12Device.h)
#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)
#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 最小渲染循环测试
#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. 下一步行动指南
- 补全抽象基类:重点实现
RHIDevice、RHICommandList、RHIPipelineLayout - 改造现有后端:让
D3D12Device、OpenGLDevice继承抽象基类并实现接口 - 实现工厂类:完成
RHIFactory以支持动态切换后端 - 测试验证:用最小渲染循环测试 D3D12 和 OpenGL 后端
- 扩展功能:逐步添加资源管理、着色器跨平台、多线程渲染等功能
9. 关键注意事项
- 上层只调用抽象接口:绝不直接访问 D3D12/OpenGL 特有类
- 合理使用底层逃逸:仅在必要时使用
GetNativeDevice(),并注明破坏跨平台性 - 优先保证核心功能:先实现 90% 常用功能的统一抽象,再处理高级特性
- 保持设计可扩展:为未来支持 Vulkan/Metal 预留空间
需要我帮你细化某个具体模块的实现代码吗?