diff --git a/RHI模块测试重构.md b/RHI模块测试重构.md new file mode 100644 index 00000000..6f7607da --- /dev/null +++ b/RHI模块测试重构.md @@ -0,0 +1,778 @@ +# RHI 模块单元测试重构计划 + +## 1. 项目背景 + +本文档分析 XCEngine RHI(渲染硬件抽象层)模块的三个单元测试层次的覆盖度和质量问题,并提出重构建议。 + +### 1.1 测试层次结构 + +| 层次 | 位置 | 测试数量 | 特点 | +|------|------|----------|------| +| **RHI 抽象层** | `tests/RHI/unit/` | 75 tests × 2 backends = 150 | 参数化测试,跨 D3D12/OpenGL | +| **D3D12 后端** | `tests/RHI/D3D12/unit/` | ~58 tests | 非参数化,直接测试 D3D12 API | +| **OpenGL 后端** | `tests/RHI/OpenGL/unit/` | ~58 tests | 非参数化,直接测试 OpenGL API | + +### 1.2 当前测试文件分布 + +#### RHI 抽象层 (`tests/RHI/unit/`) + +| 文件 | 测试数 | 测试类 | +|------|--------|--------| +| `test_device.cpp` | 8 | RHIDevice | +| `test_buffer.cpp` | 8 | RHIBuffer | +| `test_texture.cpp` | 5 | RHITexture | +| `test_swap_chain.cpp` | 4 | RHISwapChain | +| `test_command_list.cpp` | 14 | RHICommandList | +| `test_command_queue.cpp` | 6 | RHICommandQueue | +| `test_shader.cpp` | 9 | RHIShader | +| `test_fence.cpp` | 10 | RHIFence | +| `test_sampler.cpp` | 4 | RHISampler | +| `test_factory.cpp` | 5 | RHIFactory | + +#### D3D12 后端 (`tests/RHI/D3D12/unit/`) + +| 文件 | 测试数 | 测试类/功能 | +|------|--------|-------------| +| `test_device.cpp` | 6 | D3D12Device | +| `test_fence.cpp` | 9 | D3D12Fence | +| `test_command_queue.cpp` | 2 | D3D12CommandQueue | +| `test_command_allocator.cpp` | 3 | D3D12CommandAllocator | +| `test_command_list.cpp` | 2 | D3D12CommandList | +| `test_buffer.cpp` | 6 | D3D12Buffer | +| `test_texture.cpp` | 4 | D3D12Texture | +| `test_descriptor_heap.cpp` | 9 | D3D12DescriptorHeap | +| `test_shader.cpp` | 3 | D3D12Shader (trivial) | +| `test_root_signature.cpp` | 2 | D3D12RootSignature | +| `test_pipeline_state.cpp` | 2 | D3D12 PSO (trivial) | +| `test_views.cpp` | 4 | RTV/DSV/CBV | +| `test_swap_chain.cpp` | 5 | D3D12SwapChain | + +#### OpenGL 后端 (`tests/RHI/OpenGL/unit/`) + +| 文件 | 测试数 | 测试类/功能 | +|------|--------|-------------| +| `test_device.cpp` | 2 | OpenGLDevice | +| `test_buffer.cpp` | 6 | OpenGLBuffer | +| `test_fence.cpp` | 8 | OpenGLFence | +| `test_texture.cpp` | 5 | OpenGLTexture | +| `test_shader.cpp` | 4 | OpenGLShader | +| `test_pipeline_state.cpp` | 3 | OpenGLPipelineState | +| `test_vertex_array.cpp` | 5 | OpenGLVertexArray | +| `test_command_list.cpp` | 9 | OpenGLCommandList | +| `test_render_target_view.cpp` | 4 | OpenGLRenderTargetView | +| `test_depth_stencil_view.cpp` | 4 | OpenGLDepthStencilView | +| `test_swap_chain.cpp` | 3 | OpenGLSwapChain | +| `test_sampler.cpp` | 4 | OpenGLSampler | + +--- + +## 2. 严重问题(P0 - 必须修复) + +### 2.1 `tests/RHI/unit/test_shader.cpp` - 9个测试完全相同 + +**严重程度**: 🔴 致命 + +**问题描述**: + +所有 9 个 Shader 测试逻辑完全相同,都只测试空描述符返回 nullptr: + +```cpp +// 文件: tests/RHI/unit/test_shader.cpp + +// 9个测试全部是这种结构 +TEST_P(RHITestFixture, Shader_Compile_EmptyDesc_ReturnsNullptr) { + RHIShader* shader = GetDevice()->CompileShader({}); // 空描述符 + EXPECT_EQ(shader, nullptr); // 期望返回nullptr +} + +TEST_P(RHITestFixture, Shader_GetType_WithNullShader) { + RHIShader* shader = GetDevice()->CompileShader({}); // 空描述符 + EXPECT_EQ(shader, nullptr); // 期望返回nullptr - 与上面完全相同! +} + +TEST_P(RHITestFixture, Shader_IsValid_WithNullShader) { + RHIShader* shader = GetDevice()->CompileShader({}); + EXPECT_EQ(shader, nullptr); // 与上面完全相同! +} +// ... 剩下的6个测试也是同样的模式 +``` + +**影响**: + +- Shader 模块完全没有真实功能测试 +- Shader 编译、绑定、uniform 设置等功能完全未覆盖 +- 这 9 个测试等效于只有 1 个测试 + +**修复建议**: + +```cpp +// 1. 保留错误处理测试 (1-2个) +TEST_P(RHITestFixture, Shader_Compile_EmptyDesc_ReturnsNullptr) { + RHIShader* shader = GetDevice()->CompileShader({}); + EXPECT_EQ(shader, nullptr); +} + +// 2. 添加真实 shader 编译测试 (新增) +// 注意: 需要 shader 文件或内嵌 GLSL/HLSL 源码 +TEST_P(RHITestFixture, Shader_Compile_ValidShader) { + ShaderCompileDesc desc = {}; + desc.entryPoint = "main"; + desc.shaderType = ShaderType::Vertex; + // 需要实际的 shader 源码或文件路径 + RHIShader* shader = GetDevice()->CompileShader(desc); + ASSERT_NE(shader, nullptr); + shader->Shutdown(); + delete shader; +} + +// 3. 添加 shader 绑定测试 (新增) +TEST_P(RHITestFixture, Shader_Bind_ValidShader) { + // 编译 shader 后绑定 + RHIShader* shader = GetDevice()->CompileShader(validDesc); + ASSERT_NE(shader, nullptr); + + RHICommandList* cmdList = GetDevice()->CreateCommandList({}); + cmdList->Reset(); + cmdList->SetShader(shader); + cmdList->Close(); + + cmdList->Shutdown(); + delete cmdList; + shader->Shutdown(); + delete shader; +} + +// 4. 添加 uniform 设置测试 (新增) +TEST_P(RHITestFixture, Shader_SetUniform_Int) { + RHIShader* shader = GetDevice()->CompileShader(validDesc); + ASSERT_NE(shader, nullptr); + + shader->SetInt("uniformName", 42); + shader->Shutdown(); + delete shader; +} + +TEST_P(RHITestFixture, Shader_SetUniform_Float) { + // ... +} + +// 5. 添加 GetNativeHandle 测试 (保留一个) +TEST_P(RHITestFixture, Shader_GetNativeHandle_ValidShader) { + RHIShader* shader = GetDevice()->CompileShader(validDesc); + ASSERT_NE(shader, nullptr); + EXPECT_NE(shader->GetNativeHandle(), nullptr); + shader->Shutdown(); + delete shader; +} +``` + +--- + +### 2.2 关键 RHI 抽象类完全没有测试 + +**严重程度**: 🔴 致命 + +**问题描述**: + +以下 RHI 抽象类在 `tests/RHI/unit/` 中完全没有测试: + +| 类 | 说明 | 影响 | +|----|------|------| +| `RHIPipelineState` | 管线状态对象 | 无法验证 PSO 创建、配置、绑定 | +| `RHIPipelineLayout` | 管线布局 | 无法验证 descriptor layout | +| `RHIDescriptorPool` | 描述符池 | Descriptor 管理完全未覆盖 | +| `RHIDescriptorSet` | 描述符集 | 无法验证 descriptor 更新 | +| `RHIRenderPass` | 渲染通道 | RenderPass API 完全未覆盖 | +| `RHIFramebuffer` | 帧缓冲 | Framebuffer API 完全未覆盖 | + +**修复建议 - 新增 `test_pipeline_state.cpp`**: + +```cpp +// 文件: tests/RHI/unit/test_pipeline_state.cpp +#include "fixtures/RHITestFixture.h" +#include "XCEngine/RHI/RHIPipelineState.h" +#include "XCEngine/RHI/RHIDevice.h" + +using namespace XCEngine::RHI; + +TEST_P(RHITestFixture, PipelineState_Create) { + GraphicsPipelineDesc desc = {}; + // 配置基本描述符 + RHIPipelineState* pso = GetDevice()->CreatePipelineState(desc); + ASSERT_NE(pso, nullptr); + pso->Shutdown(); + delete pso; +} + +TEST_P(RHITestFixture, PipelineState_SetGet_RasterizerState) { + GraphicsPipelineDesc desc = {}; + RHIPipelineState* pso = GetDevice()->CreatePipelineState(desc); + ASSERT_NE(pso, nullptr); + + RasterizerDesc rasterizer = {}; + rasterizer.cullMode = CullMode::Back; + pso->SetRasterizerState(rasterizer); + + EXPECT_EQ(pso->GetRasterizerState().cullMode, CullMode::Back); + pso->Shutdown(); + delete pso; +} + +TEST_P(RHITestFixture, PipelineState_SetGet_BlendState) { + // ... +} + +TEST_P(RHITestFixture, PipelineState_SetGet_DepthStencilState) { + // ... +} + +TEST_P(RHITestFixture, PipelineState_Finalize) { + GraphicsPipelineDesc desc = {}; + RHIPipelineState* pso = GetDevice()->CreatePipelineState(desc); + ASSERT_NE(pso, nullptr); + + bool finalized = pso->Finalize(); + EXPECT_TRUE(finalized); + EXPECT_TRUE(pso->IsFinalized()); + + pso->Shutdown(); + delete pso; +} + +TEST_P(RHITestFixture, PipelineState_Bind_Unbind) { + // ... +} +``` + +**修复建议 - 新增 `test_render_pass.cpp`**: + +```cpp +// 文件: tests/RHI/unit/test_render_pass.cpp +#include "fixtures/RHITestFixture.h" +#include "XCEngine/RHI/RHIRenderPass.h" + +using namespace XCEngine::RHI; + +TEST_P(RHITestFixture, RenderPass_Create) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + colorAttachment.loadOp = LoadAction::Clear; + colorAttachment.storeOp = StoreAction::Store; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + EXPECT_EQ(renderPass->GetColorAttachmentCount(), 1); + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, RenderPass_BeginEnd) { + // 需要先创建 framebuffer + // cmdList->BeginRenderPass(renderPass, framebuffer, ...); + // cmdList->EndRenderPass(); +} +``` + +**修复建议 - 新增 `test_framebuffer.cpp`**: + +```cpp +// 文件: tests/RHI/unit/test_framebuffer.cpp +#include "fixtures/RHITestFixture.h" +#include "XCEngine/RHI/RHIFramebuffer.h" + +using namespace XCEngine::RHI; + +TEST_P(RHITestFixture, Framebuffer_Create) { + // 先创建 renderPass 和 texture + RHIRenderPass* renderPass = ...; + RHITexture* texture = ...; + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {}); + + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer(renderPass, 1, &rtv, nullptr, 256, 256); + ASSERT_NE(fb, nullptr); + + EXPECT_EQ(fb->GetWidth(), 256); + EXPECT_EQ(fb->GetHeight(), 256); + EXPECT_TRUE(fb->IsValid()); + + fb->Shutdown(); + delete fb; +} +``` + +**修复建议 - 新增 `test_descriptor.cpp`**: + +```cpp +// 文件: tests/RHI/unit/test_descriptor.cpp +#include "fixtures/RHITestFixture.h" +#include "XCEngine/RHI/RHIDescriptorPool.h" +#include "XCEngine/RHI/RHIDescriptorSet.h" + +using namespace XCEngine::RHI; + +TEST_P(RHITestFixture, DescriptorPool_Create) { + DescriptorPoolDesc desc = {}; + desc.maxSets = 10; + desc.poolSize = 100; + + RHIDescriptorPool* pool = GetDevice()->CreateDescriptorPool(desc); + ASSERT_NE(pool, nullptr); + pool->Shutdown(); +} + +TEST_P(RHITestFixture, DescriptorSet_Allocate_Update) { + // 创建 pool + RHIDescriptorPool* pool = GetDevice()->CreateDescriptorPool(desc); + + // 创建 layout + DescriptorSetLayoutDesc layoutDesc = {}; + DescriptorSetLayoutBinding binding = {}; + binding.binding = 0; + binding.type = DescriptorType::CBV; + binding.count = 1; + layoutDesc.bindings = &binding; + layoutDesc.bindingCount = 1; + + RHIDescriptorSet* set = GetDevice()->CreateDescriptorSet(pool, layoutDesc); + ASSERT_NE(set, nullptr); + + EXPECT_EQ(set->GetBindingCount(), 1); + + set->Shutdown(); + pool->Shutdown(); +} +``` + +--- + +## 3. 中等问题(P1 - 应该修复) + +### 3.1 `tests/RHI/unit/test_command_list.cpp` - 大量测试传递 nullptr + +**问题描述**: + +很多 CommandList 测试只验证调用不崩溃,不验证实际功能: + +```cpp +// 这些测试传递 nullptr,无法验证真实功能 +TEST_P(RHITestFixture, CommandList_ClearRenderTarget) { + // ... 创建了 texture 但实际传递 nullptr + cmdList->ClearRenderTarget(static_cast(nullptr), color); + // 没有验证任何实际行为 +} + +TEST_P(RHITestFixture, CommandList_TransitionBarrier) { + // 创建了 texture 但传递 nullptr + cmdList->TransitionBarrier(static_cast(nullptr), ...); +} + +TEST_P(RHITestFixture, CommandList_SetVertexBuffer_WithResourceView) { + // 直接传递 nullptr + RHIResourceView* buffer = nullptr; + cmdList->SetVertexBuffers(0, 1, &buffer, nullptr, nullptr); +} +``` + +**缺少的真实测试**: + +| 测试内容 | 状态 | 建议 | +|----------|------|------| +| 有资源的 SetVertexBuffer | ❌ | 添加 | +| 有资源的 SetIndexBuffer | ❌ | 添加 | +| 有资源的 ClearRenderTarget | ❌ | 添加 | +| BeginRenderPass/EndRenderPass | ❌ | 添加 | +| Dispatch (计算着色器) | ❌ | 添加 | +| SetGraphicsDescriptorSets | ❌ | 添加 | +| SetComputeDescriptorSets | ❌ | 添加 | + +**修复建议**: + +```cpp +// 新增真实资源测试 +TEST_P(RHITestFixture, CommandList_SetVertexBuffer_WithRealBuffer) { + // 1. 创建 buffer + BufferDesc bufDesc = {}; + bufDesc.size = 1024; + RHIBuffer* buffer = GetDevice()->CreateBuffer(bufDesc); + ASSERT_NE(buffer, nullptr); + + // 2. 创建 cmdList 并设置 + RHICommandList* cmdList = GetDevice()->CreateCommandList({}); + cmdList->Reset(); + + RHIResourceView* bufferView = buffer->GetView(); + cmdList->SetVertexBuffers(0, 1, &bufferView, nullptr, nullptr); + + cmdList->Close(); + + // 3. 清理 + cmdList->Shutdown(); + buffer->Shutdown(); + delete buffer; + delete cmdList; +} + +TEST_P(RHITestFixture, CommandList_BeginEndRenderPass) { + // 1. 创建 renderPass + AttachmentDesc colorDesc = {}; + colorDesc.format = Format::R8G8B8A8_UNorm; + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorDesc, nullptr); + ASSERT_NE(renderPass, nullptr); + + // 2. 创建 framebuffer + TextureDesc texDesc = {}; + texDesc.width = 256; + texDesc.height = 256; + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {}); + + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer(renderPass, 1, &rtv, nullptr, 256, 256); + + // 3. 测试 Begin/End + RHICommandList* cmdList = GetDevice()->CreateCommandList({}); + cmdList->Reset(); + + Rect renderArea = {0, 0, 256, 256}; + ClearValue clearValue = {}; + cmdList->BeginRenderPass(renderPass, fb, renderArea, 1, &clearValue); + cmdList->EndRenderPass(); + + cmdList->Close(); + + // 清理 + cmdList->Shutdown(); + fb->Shutdown(); + delete fb; + delete rtv; + texture->Shutdown(); + delete texture; + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, CommandList_Dispatch) { + // 1. 创建 compute shader + ShaderCompileDesc csDesc = {}; + RHIShader* computeShader = GetDevice()->CompileShader(csDesc); + // ... + + // 2. 创建并设置 compute pipeline + RHIPipelineState* computePSO = GetDevice()->CreatePipelineState(computeDesc); + computePSO->SetComputeShader(computeShader); + computePSO->Finalize(); + + // 3. 测试 Dispatch + RHICommandList* cmdList = GetDevice()->CreateCommandList({}); + cmdList->Reset(); + cmdList->SetPipelineState(computePSO); + cmdList->Dispatch(8, 8, 1); + cmdList->Close(); + + // 清理 + cmdList->Shutdown(); + computePSO->Shutdown(); + delete computePSO; + computeShader->Shutdown(); + delete computeShader; +} +``` + +--- + +### 3.2 D3D12 后端测试问题 + +| 文件 | 问题 | 建议 | +|------|------|------| +| `test_pipeline_state.cpp` | 只测试 struct 默认值 | 添加真实 PSO 创建测试 | +| `test_shader.cpp` | 只做字符串比较 | 添加 shader 编译测试 | +| `test_root_signature.cpp` | 只测试空签名和 CBV | 添加复杂参数测试 | +| `test_command_list.cpp` | 只有 2 个测试 | 补充 Reset/ClearRenderTargetView 等 | + +**修复建议 - `test_pipeline_state.cpp`**: + +```cpp +// 当前只有 trivial 测试 +TEST_F(D3D12TestFixture, PipelineState_Get_GraphicsPipelineDescDefaults) { + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + // 只验证 struct 默认值 + EXPECT_EQ(psoDesc.PrimitiveTopologyType, ...); +} + +// 应该添加 +TEST_F(D3D12TestFixture, PipelineState_Create_GraphicsPipeline) { + // 1. 创建 root signature + D3D12_ROOT_SIGNATURE_DESC rootSigDesc = {}; + D3D12RootSignature rootSig; + rootSig.Initialize(GetDevice()->GetDevice(), rootSigDesc); + + // 2. 创建 shader (需要 HLSL 源码或编译好的 bytecode) + D3D12Shader* vs = new D3D12Shader(); + vs->CompileFromFile(L"shaders/vertex.cso"); + + // 3. 创建 PSO + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.pRootSignature = rootSig.GetRootSignature(); + psoDesc.VS = vs->GetBytecode(); + // ... 设置其他字段 + + ID3D12PipelineState* d3d12PSO = nullptr; + HRESULT hr = GetDevice()->GetDevice()->CreateGraphicsPipelineState( + &psoDesc, IID_PPV_ARGS(&d3d12PSO)); + ASSERT_EQ(hr, S_OK); + + d3d12PSO->Release(); + rootSig.Shutdown(); +} +``` + +--- + +### 3.3 OpenGL 后端测试问题 + +| 文件 | 问题 | 建议 | +|------|------|------| +| `test_command_list.cpp` | 缺少 SetShader/SetUniform 测试 | 添加 | +| `test_shader.cpp` | 无 compute shader 测试 | 添加 GL_COMPUTE_SHADER | +| 无 compute 相关测试 | 缺少 Dispatch 测试 | 添加 | + +**修复建议**: + +```cpp +// 新增 test_compute.cpp +TEST_F(OpenGLTestFixture, ComputeShader_Compile) { + const char* computeSrc = R"( + #version 460 + layout(local_size_x = 1, local_size_y = 1) in; + void main() { + // compute shader + } + )"; + + OpenGLShader computeShader; + bool compiled = computeShader.CompileCompute(computeSrc); + EXPECT_TRUE(compiled); +} + +TEST_F(OpenGLTestFixture, CommandList_Dispatch) { + // 编译 compute shader + OpenGLShader computeShader; + computeShader.CompileCompute(computeSrc); + + // 创建 pipeline state + OpenGLPipelineState pipeline; + pipeline.SetComputeShader(&computeShader); + pipeline.Apply(); + + // 测试 Dispatch + OpenGLCommandList cmdList; + cmdList.Reset(); + cmdList.SetPipelineState(&pipeline); + cmdList.Dispatch(8, 8, 1); + cmdList.Close(); +} +``` + +--- + +## 4. 测试覆盖度矩阵 + +### 4.1 RHI 抽象层覆盖度 + +| RHI 抽象接口 | 覆盖状态 | 备注 | +|--------------|----------|------| +| Device 创建/初始化 | ✅ 已覆盖 | | +| Buffer 创建/映射 | ✅ 已覆盖 | | +| Texture 创建 | ✅ 已覆盖 | | +| SwapChain | ✅ 已覆盖 | | +| CommandList 基础 | ⚠️ 太弱 | 大部分传递 nullptr | +| CommandQueue | ✅ 已覆盖 | | +| Fence | ✅ 已覆盖 | | +| Sampler | ✅ 已覆盖 | | +| Shader 编译 | ❌ 无效 | 9个测试完全相同 | +| **PipelineState** | ❌ 无测试 | **必须添加** | +| **PipelineLayout** | ❌ 无测试 | **必须添加** | +| **DescriptorPool** | ❌ 无测试 | **必须添加** | +| **DescriptorSet** | ❌ 无测试 | **必须添加** | +| **RenderPass** | ❌ 无测试 | **必须添加** | +| **Framebuffer** | ❌ 无测试 | **必须添加** | +| **Compute/Dispatch** | ❌ 无测试 | **必须添加** | +| Resource Barriers | ⚠️ 太弱 | 传递 nullptr | + +### 4.2 D3D12 后端覆盖度 + +| D3D12 特定功能 | 覆盖状态 | 备注 | +|----------------|----------|------| +| 设备特性检测 | ✅ 已覆盖 | | +| Descriptor Heap | ✅ 已覆盖 | | +| Command Allocator | ✅ 已覆盖 | | +| Fence Timeline | ✅ 已覆盖 | | +| RootSignature | ⚠️ 弱 | 只测试空签名和 CBV | +| PipelineState | ⚠️ 弱 | 只测试 struct 默认值 | +| Shader | ⚠️ 弱 | 只做字符串比较 | +| SwapChain | ✅ 已覆盖 | | +| Views (RTV/DSV) | ✅ 已覆盖 | | + +### 4.3 OpenGL 后端覆盖度 + +| OpenGL 特定功能 | 覆盖状态 | 备注 | +|----------------|----------|------| +| Buffer (VBO/IBO/UBO) | ✅ 已覆盖 | | +| Texture (2D/Cube) | ✅ 已覆盖 | | +| VertexArray | ✅ 已覆盖 | | +| Shader 编译 | ✅ 已覆盖 | | +| PipelineState | ✅ 已覆盖 | | +| Sampler | ✅ 已覆盖 | | +| Fence | ✅ 已覆盖 | | +| SwapChain | ✅ 已覆盖 | | +| **Compute Shader** | ❌ 无测试 | **必须添加** | +| **Dispatch** | ❌ 无测试 | **必须添加** | +| Memory Barrier | ❌ 无测试 | 可选 | + +--- + +## 5. 重构优先级总结 + +### P0 - 必须修复(影响功能验证) + +| 优先级 | 问题 | 工作量 | 影响 | +|--------|------|--------|------| +| 1 | Shader 测试重构 | 中 | 9个测试无效 | +| 2 | 添加 PipelineState 测试 | 大 | RHI 核心组件无测试 | +| 3 | 添加 RenderPass 测试 | 中 | 重要 API 未覆盖 | +| 4 | 添加 Framebuffer 测试 | 中 | 重要 API 未覆盖 | + +### P1 - 应该修复(提高覆盖率) + +| 优先级 | 问题 | 工作量 | 影响 | +|--------|------|--------|------| +| 5 | 添加 DescriptorPool/Set 测试 | 中 | 已实现未测试 | +| 6 | CommandList 测试增强 | 大 | 大部分传递 nullptr | +| 7 | 添加 Compute/Dispatch 测试 | 中 | 重要功能缺失 | +| 8 | D3D12 PSO/shader 测试增强 | 小 | 当前测试 trivial | + +### P2 - 可以修复(完善细节) + +| 优先级 | 问题 | 工作量 | 影响 | +|--------|------|--------|------| +| 9 | OpenGL CommandList SetShader 测试 | 小 | 缺失测试 | +| 10 | 复杂 RootSignature 参数测试 | 小 | 当前只测试 CBV | +| 11 | OpenGL Compute Shader 测试 | 小 | 缺失测试 | + +--- + +## 6. 新增测试文件清单 + +| 文件路径 | 测试内容 | 优先级 | +|----------|----------|--------| +| `tests/RHI/unit/test_pipeline_state.cpp` | PipelineState 创建/配置/绑定 | P0 | +| `tests/RHI/unit/test_render_pass.cpp` | RenderPass 创建/Begin/End | P0 | +| `tests/RHI/unit/test_framebuffer.cpp` | Framebuffer 创建/绑定 | P0 | +| `tests/RHI/unit/test_descriptor.cpp` | DescriptorPool/Set 创建/更新 | P1 | +| `tests/RHI/unit/test_compute.cpp` | Compute shader/Dispatch | P1 | +| `tests/RHI/unit/test_pipeline_layout.cpp` | PipelineLayout 创建 | P2 | + +--- + +## 7. 测试质量改进建议 + +### 7.1 测试命名规范 + +遵循 `Component_Category_SubBehavior` 格式: + +``` +Good: +- Buffer_Create_DefaultHeap +- CommandList_SetViewport_ValidRect +- Shader_Compile_ValidSource + +Bad: +- Test1 +- BufferTest +- test_buffer +``` + +### 7.2 测试结构规范 + +每个测试应包含: + +```cpp +TEST_P(RHITestFixture, CommandList_SetVertexBuffer_WithRealBuffer) { + // 1. Arrange - 准备测试数据 + BufferDesc bufDesc = {}; + bufDesc.size = 1024; + RHIBuffer* buffer = GetDevice()->CreateBuffer(bufDesc); + ASSERT_NE(buffer, nullptr); // 使用 ASSERT 防止空指针解引用 + + // 2. Act - 执行被测操作 + RHICommandList* cmdList = GetDevice()->CreateCommandList({}); + cmdList->Reset(); + RHIResourceView* view = buffer->GetView(); + cmdList->SetVertexBuffers(0, 1, &view, nullptr, nullptr); + cmdList->Close(); + + // 3. Assert - 验证结果 + // 注意:有些测试难以直接验证结果,可以通过不崩溃来间接验证 + + // 4. Cleanup - 清理资源 + cmdList->Shutdown(); + delete cmdList; + buffer->Shutdown(); + delete buffer; +} +``` + +### 7.3 避免的问题 + +```cpp +// 问题1: 测试逻辑完全相同 +TEST_P(RHITestFixture, Shader_Test1) { shader = nullptr; EXPECT_EQ(shader, nullptr); } +TEST_P(RHITestFixture, Shader_Test2) { shader = nullptr; EXPECT_EQ(shader, nullptr); } // 相同! + +// 问题2: 传递 nullptr 不验证功能 +TEST_P(RHITestFixture, CommandList_Test) { + cmdList->ClearRenderTarget(nullptr, color); // 无意义 +} + +// 问题3: 不清理资源 +TEST_P(RHITestFixture, Buffer_Test) { + RHIBuffer* buffer = device->CreateBuffer(desc); + // 忘记 buffer->Shutdown() 和 delete buffer +} + +// 问题4: 缺少 ASSERT +TEST_P(RHITestFixture, Buffer_Test) { + RHIBuffer* buffer = device->CreateBuffer(desc); + buffer->SetData(...); // 如果 buffer 是 nullptr 则崩溃 + // 应该先 ASSERT_NE(buffer, nullptr); +} +``` + +--- + +## 8. 附录:测试执行命令 + +```bash +# 增量构建 RHI 测试 +cmake --build . --target rhi_unit_tests --config Debug +cmake --build . --target rhi_d3d12_tests --config Debug +cmake --build . --target rhi_opengl_tests --config Debug + +# 运行 RHI 抽象层测试 (同时验证 D3D12 和 OpenGL) +ctest -R "^D3D12/|^OpenGL/" -C Debug --output-on-failure + +# 运行 D3D12 后端专用测试 +ctest -R "D3D12TestFixture|SwapChainTestFixture" -C Debug --output-on-failure + +# 运行 OpenGL 后端专用测试 +ctest -R "OpenGLTestFixture" -C Debug --output-on-failure + +# 运行所有 RHI 测试 +ctest -R "D3D12|OpenGL|RHITestFixture" -C Debug --output-on-failure +``` + +--- + +**文档版本**: 1.0 +**最后更新**: 2026-03-25 +**作者**: XCEngine Team diff --git a/RHI_Design_Issues.md b/docs/plan/used/RHI_Design_Issues.md similarity index 100% rename from RHI_Design_Issues.md rename to docs/plan/used/RHI_Design_Issues.md diff --git a/minimal.ppm b/minimal.ppm deleted file mode 100644 index 2217d8f0..00000000 Binary files a/minimal.ppm and /dev/null differ