# D3D12 后端测试设计 ## 1. 概述 本文档描述 XCEngine D3D12 渲染后端的测试框架设计,旨在为 D3D12 各组件提供全面、规范的自动化测试覆盖。 ### 1.1 测试目标 - 验证 D3D12 各组件 API 的正确性 - 确保组件间的协作正常工作 - 捕获资源泄漏和内存错误 - 支持持续集成(CI)自动化测试 ### 1.2 现有问题 当前 `tests/D3D12/main.cpp` 存在以下问题: | 问题 | 说明 | |------|------| | 非自动化测试 | 截图和对比需手动触发 | | 缺乏单元测试 | 只有一个 Win32 图形程序,无法使用 Google Test | | 维护困难 | GT.ppm 是纯黑色图像,对比无实际意义 | | 容差问题 | 1% 阈值对硬件差异过于敏感 | ## 2. 测试目录结构 ### 2.1 资源目录(保留现有 tests/D3D12) 现有 `tests/D3D12` 目录保留,作为测试资源文件来源: ``` tests/D3D12/ ├── Res/ # 测试资源文件(供新测试框架使用) │ ├── Shader/ │ │ ├── test_vs.hlsl # 测试用顶点着色器 │ │ ├── test_ps.hlsl # 测试用像素着色器 │ │ ├── test_gs.hlsl # 测试用几何着色器 │ │ ├── test_cs.hlsl # 测试用计算着色器 │ │ └── test_pattern.hlsl # 图案化测试着色器 │ ├── Texture/ │ │ ├── test_256x256.png # 测试用 2D 纹理 │ │ └── test_cube.dds # 测试用立方体纹理 │ └── Golden/ │ ├── clear_color_gt.ppm # 清除颜色基准图像 │ ├── pattern_checker_gt.ppm # 棋盘格基准图像 │ └── gradient_gt.ppm # 渐变基准图像 ├── main.cpp # 现有渲染示例(保留) ├── CMakeLists.txt # 现有构建配置(保留) └── ... # 其他现有文件(保留) ``` ### 2.2 新测试框架目录 在 `tests/D3D12_engine/test/` 下创建新的测试模块: ``` tests/D3D12_engine/test/ # 新测试目录 ├── CMakeLists.txt # 测试构建配置 ├── fixtures/ │ ├── D3D12TestFixture.h # 基础测试夹具 │ └── D3D12TestFixture.cpp # 夹具实现 ├── test_device.cpp # D3D12Device 测试 ├── test_command_queue.cpp # D3D12CommandQueue 测试 ├── test_command_allocator.cpp # D3D12CommandAllocator 测试 ├── test_command_list.cpp # D3D12CommandList 测试 ├── test_buffer.cpp # D3D12Buffer 测试 ├── test_texture.cpp # D3D12Texture 测试 ├── test_descriptor_heap.cpp # D3D12DescriptorHeap 测试 ├── test_pipeline_state.cpp # D3D12PipelineState 测试 ├── test_root_signature.cpp # D3D12RootSignature 测试 ├── test_fence.cpp # D3D12Fence 测试 ├── test_shader.cpp # D3D12Shader 测试 ├── test_views.cpp # RTV/DSV/SRV/UAV 测试 └── test_screenshot.cpp # 截图功能测试 ``` engine/src/RHI/D3D12/ ├── test/ # 新测试目录 │ ├── CMakeLists.txt # 测试构建配置 │ ├── fixtures/ │ │ ├── D3D12TestFixture.h # 基础测试夹具 │ │ ├── D3D12ResourceFixture.h # 资源测试夹具 │ │ └── D3D12LeakCheckFixture.h # # 泄漏检测夹具 │ ├── test_device.cpp # D3D12Device 测试 │ ├── test_command_queue.cpp # D3D12CommandQueue 测试 │ ├── test_command_allocator.cpp # D3D12CommandAllocator 测试 │ ├── test_command_list.cpp # D3D12CommandList 测试 │ ├── test_buffer.cpp # D3D12Buffer 测试 │ ├── test_texture.cpp # D3D12Texture 测试 │ ├── test_descriptor_heap.cpp # D3D12DescriptorHeap 测试 │ ├── test_pipeline_state.cpp # D3D12PipelineState 测试 │ ├── test_root_signature.cpp # D3D12RootSignature 测试 │ ├── test_fence.cpp # D3D12Fence 测试 │ ├── test_shader.cpp # D3D12Shader 测试 │ ├── test_views.cpp # RTV/DSV/SRV/UAV 测试 │ ├── test_types.cpp # 类型转换测试 │ └── test_screenshot.cpp # 截图功能测试 │ ├── D3D12Device.cpp # 现有源文件 ├── D3D12CommandQueue.cpp # 现有源文件 └── ... # 其他现有源文件 ``` ### 2.3 构建配置说明 新的测试框架通过 CMake 添加到引擎构建中: ```cmake # engine/CMakeLists.txt 中添加 if(BUILD_D3D12_TESTS) enable_testing() add_subdirectory(src/RHI/D3D12/test) endif() ``` ```cmake # engine/src/RHI/D3D12/test/CMakeLists.txt cmake_minimum_required(VERSION 3.15) project(D3D12EngineTests) set(CMAKE_CXX_STANDARD 17) # 查找 Google Test find_package(GTest REQUIRED) # 测试源文件 set(TEST_SOURCES test_device.cpp test_command_queue.cpp test_buffer.cpp # ... 其他测试文件 ) add_executable(d3d12_engine_tests ${TEST_SOURCES}) target_link_libraries(d3d12_engine_tests PRIVATE d3d12 dxgi d3dcompiler XCEngine GTest::gtest GTest::gtest_main ) # 设置资源目录路径 target_compile_definitions(d3d12_engine_tests PRIVATE TEST_RESOURCES_DIR="${CMAKE_SOURCE_DIR}/../../tests/D3D12/Res" ) add_test(NAME D3D12EngineTests COMMAND d3d12_engine_tests) ## 3. 测试夹具设计 ### 3.1 基础夹具 ```cpp // fixtures/D3D12TestFixture.h #pragma once #include #include #include using namespace Microsoft::WRL; class D3D12TestFixture : public ::testing::Test { protected: static void SetUpTestSuite() { // 创建全局 D3D12 设备(所有测试共享) HRESULT hr = D3D12CreateDevice( nullptr, // 默认适配器 D3D_FEATURE_LEVEL_12_0, // 最低支持特性等级 IID_PPV_ARGS(&mDevice) ); ASSERT_TRUE(SUCCEEDED(hr)) << "Failed to create D3D12 device"; } static void TearDownTestSuite() { mDevice.Reset(); } void SetUp() override { // 创建命令队列 D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; HRESULT hr = mDevice->CreateCommandQueue( &queueDesc, IID_PPV_ARGS(&mCommandQueue) ); ASSERT_TRUE(SUCCEEDED(hr)); // 创建命令分配器 hr = mDevice->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator) ); ASSERT_TRUE(SUCCEEDED(hr)); // 创建命令列表 hr = mDevice->CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList) ); ASSERT_TRUE(SUCCEEDED(hr)); } void TearDown() override { // 等待所有命令执行完成 WaitForGPU(); mCommandList.Reset(); mCommandAllocator.Reset(); mCommandQueue.Reset(); } // 常用辅助方法 ID3D12Device* GetDevice() { return mDevice.Get(); } ID3D12CommandQueue* GetCommandQueue() { return mCommandQueue.Get(); } ID3D12CommandList* GetCommandList() { return mCommandList.Get(); } void WaitForGPU() { ComPtr fence; UINT64 fenceValue = 1; HRESULT hr = mDevice->CreateFence( 0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence) ); if (SUCCEEDED(hr)) { mCommandQueue->Signal(fence.Get(), fenceValue); HANDLE eventHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr); fence->SetEventOnCompletion(fenceValue, eventHandle); WaitForSingleObject(eventHandle, INFINITE); CloseHandle(eventHandle); } } private: static ComPtr mDevice; ComPtr mCommandQueue; ComPtr mCommandAllocator; ComPtr mCommandList; }; // 静态成员定义 ComPtr D3D12TestFixture::mDevice; ``` ### 3.2 资源测试夹具 ```cpp // fixtures/D3D12ResourceFixture.h #pragma once #include "D3D12TestFixture.h" class D3D12ResourceFixture : public D3D12TestFixture { protected: void CreateUploadBuffer(size_t size, ID3D12Resource** outResource) { CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD); CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(size); HRESULT hr = GetDevice()->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(outResource) ); ASSERT_TRUE(SUCCEEDED(hr)); } void CreateDefaultBuffer(size_t size, ID3D12Resource** outResource) { CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT); CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(size); HRESULT hr = GetDevice()->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(outResource) ); ASSERT_TRUE(SUCCEEDED(hr)); } }; ``` ## 4. 测试分类 ### 4.1 单元测试 测试单一 API 的基本功能,不依赖图形硬件渲染结果。 ```cpp TEST(D3D12_Buffer, Create_ValidSize_ReturnsSuccess) { const size_t bufferSize = 1024; D3D12Buffer buffer; bool result = buffer.Initialize(GetDevice(), bufferSize, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); ASSERT_TRUE(result); ASSERT_NE(buffer.GetResource(), nullptr); ASSERT_EQ(buffer.GetResource()->GetDesc().Width, bufferSize); } TEST(D3D12_Buffer, Map_ValidRange_ReturnsValidPointer) { D3D12Buffer buffer; buffer.Initialize(GetDevice(), 256, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); void* mappedData = buffer.Map(0, nullptr); ASSERT_NE(mappedData, nullptr); // 写入测试数据 memset(mappedData, 0xAB, 256); buffer.Unmap(0, nullptr); } TEST(D3D12_DescriptorHeap, Create_RTVHeap_ReturnsValidHeap) { D3D12DescriptorHeap heap; bool result = heap.Initialize(GetDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 4); ASSERT_TRUE(result); ASSERT_NE(heap.GetDescriptorHeap(), nullptr); } ``` ### 4.2 集成测试 测试多个组件的协作,例如 Buffer 数据上传到 GPU。 ```cpp TEST(D3D12_Buffer, UploadToGPU_DataIntegrity) { const size_t dataSize = sizeof(float) * 4; float testData[] = { 1.0f, 2.0f, 3.0f, 4.0f }; // 创建默认缓冲(GPU 只读) D3D12Buffer gpuBuffer; gpuBuffer.Initialize(GetDevice(), dataSize, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_HEAP_TYPE_DEFAULT); // 创建上传缓冲(CPU 可写) D3D12Buffer uploadBuffer; uploadBuffer.Initialize(GetDevice(), dataSize, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); // 复制数据到上传缓冲 void* mappedData = uploadBuffer.Map(0, nullptr); memcpy(mappedData, testData, dataSize); uploadBuffer.Unmap(0, nullptr); // 通过命令列表复制 GetCommandList()->CopyBufferRegion( gpuBuffer.GetResource(), 0, uploadBuffer.GetResource(), 0, dataSize ); // 执行并等待 GetCommandList()->Close(); ID3D12CommandList* cmdLists[] = { GetCommandList() }; GetCommandQueue()->ExecuteCommandLists(1, cmdLists); WaitForGPU(); // 验证:使用映射读取返回的数据 // (需要转换状态到 GENERIC_READ) } ``` ### 4.3 渲染结果测试 渲染到纹理并验证像素数据,而非渲染到屏幕。 #### 4.3.1 测试策略 | 方案 | 优点 | 缺点 | |------|------|------| | 渲染到 RTT | 无窗口依赖,可 CI | 需要 Mipmap 比较算法 | | Shader 输出测试值 | 精确验证 | 需要特殊 Shader | | Golden Image 对比 | 直观 | 维护成本高 | #### 4.3.2 推荐方案:渲染特定图案 生成一个已知的测试图案(如渐变、棋盘格),渲染后读取像素验证: ```cpp TEST(D3D12_RenderTarget, ClearColor_VerifyPixelValue) { // 创建渲染目标纹理 const uint32_t width = 64; const uint32_t height = 64; D3D12Texture renderTarget; renderTarget.InitializeAsRenderTarget( GetDevice(), width, height, DXGI_FORMAT_R8G8B8A8_UNORM ); // 创建 RTV D3D12DescriptorHeap rtvHeap; rtvHeap.Initialize(GetDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1); D3D12RenderTargetView rtv; rtv.InitializeAt( GetDevice(), renderTarget.GetResource(), rtvHeap.GetCPUDescriptorHandleForHeapStart(), nullptr ); // 清空为特定颜色 float clearColor[] = { 0.25f, 0.5f, 0.75f, 1.0f }; // R=64, G=128, B=192 GetCommandList()->ClearRenderTargetView( rtvHeap.GetCPUDescriptorHandleForHeapStart(), clearColor, 0, nullptr ); // 转换状态用于读取 GetCommandList()->TransitionBarrier( renderTarget.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE ); GetCommandList()->Close(); ID3D12CommandList* cmdLists[] = { GetCommandList() }; GetCommandQueue()->ExecuteCommandLists(1, cmdLists); WaitForGPU(); // 读取像素数据 std::vector pixels(width * height * 4); ReadBackTexture(GetDevice(), GetCommandQueue(), renderTarget.GetResource(), pixels.data(), pixels.size()); // 验证中心像素 uint32_t centerIndex = (height / 2 * width + width / 2) * 4; EXPECT_NEAR(pixels[centerIndex + 0], 64, 2); // R EXPECT_NEAR(pixels[centerIndex + 1], 128, 2); // G EXPECT_NEAR(pixels[centerIndex + 2], 192, 2); // B } ``` #### 4.3.3 图案化渲染测试 使用纯色 Shader 渲染特定图案: ```hlsl // TestPattern.hlsl - 输出棋盘格图案 float4 PSMain(PSInput input) : SV_Target { uint2 pixelPos = uint2(input.Position.xy); bool isWhite = ((pixelPos.x / 8) % 2) == ((pixelPos.y / 8) % 2); return isWhite ? float4(1,1,1,1) : float4(0,0,0,1); } ``` ### 4.4 性能测试 使用 Google Benchmark 或简单计时: ```cpp TEST(D3D12_Performance, BufferCreation_1000Times) { const int iterations = 1000; auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { D3D12Buffer buffer; buffer.Initialize(GetDevice(), 1024, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); printf("Created %d buffers in %ld ms\n", iterations, duration.count()); } ``` ## 5. 组件测试详情 ### 5.1 D3D12Device ```cpp TEST(D3D12_Device, CheckFeatureSupport_D3D12OK) { D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevels = {}; featureLevels.NumFeatureLevels = 1; featureLevels.pFeatureLevelsRequested = &mFeatureLevel; HRESULT hr = GetDevice()->CheckFeatureSupport( D3D12_FEATURE_FEATURE_LEVELS, &featureLevels, sizeof(featureLevels) ); ASSERT_TRUE(SUCCEEDED(hr)); ASSERT_EQ(featureLevels.MaxSupportedFeatureLevel, D3D_FEATURE_LEVEL_12_0); } ``` ### 5.2 D3D12CommandList ```cpp TEST(D3D12_CommandList, Reset_AfterClose_Succeeds) { // 第一次使用 GetCommandList()->Close(); // 重置并再次使用 HRESULT hr = GetCommandList()->Reset(mCommandAllocator.Get(), nullptr); ASSERT_TRUE(SUCCEEDED(hr)); } ``` ### 5.3 D3D12Fence ```cpp TEST(D3D12_Fence, SignalAndWait) { ComPtr fence; UINT64 fenceValue = 1; HRESULT hr = GetDevice()->CreateFence( 0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence) ); ASSERT_TRUE(SUCCEEDED(hr)); // Signal GetCommandQueue()->Signal(fence.Get(), fenceValue); // 等待 ASSERT_EQ(fence->GetCompletedValue(), fenceValue); } ``` ## 6. 资源泄漏检测 ### 6.1 使用 D3D12 Debug Layer ```cpp #ifdef _DEBUG class D3D12LeakDetector { public: static void BeginFrame() { if (sDebugDevice) { sDebugDevice->SetName(L"Leak Detection Frame Start"); } } static void EndFrame() { if (sDebugDevice) { sDebugDevice->SetName(L"Leak Detection Frame End"); sDebugDevice->ReportLiveDeviceObjects( D3D12_RDO_FLAGS::D3D12_RDO_FLAG_NONE ); } } static void SetDebugDevice(ID3D12DebugDevice* device) { sDebugDevice = device; } private: static ID3D12DebugDevice* sDebugDevice; }; #endif ``` ### 6.2 在测试夹具中使用 ```cpp class D3D12LeakCheckFixture : public D3D12TestFixture { protected: void TearDown() override { D3D12TestFixture::TearDown(); #ifdef _DEBUG D3D12LeakDetector::EndFrame(); #endif } }; ``` ## 7. 构建配置 ### 7.1 CMakeLists.txt 修改 ```cmake cmake_minimum_required(VERSION 3.15) project(D3D12Tests) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Google Test include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://gitee.com/mirrors/googletest.git GIT_TAG v1.14.0 ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) enable_testing() # 测试源文件 set(TEST_SOURCES test_device.cpp test_command_queue.cpp test_command_allocator.cpp test_command_list.cpp test_buffer.cpp test_texture.cpp test_descriptor_heap.cpp test_pipeline_state.cpp test_root_signature.cpp test_fence.cpp test_shader.cpp test_views.cpp ) add_executable(d3d12_tests ${TEST_SOURCES}) target_link_libraries(d3d12_tests PRIVATE d3d12 dxgi d3dcompiler XCEngine GTest::gtest GTest::gtest_main ) target_include_directories(d3d12_tests PRIVATE ${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_CURRENT_SOURCE_DIR}/fixtures ) add_test(NAME D3D12Tests COMMAND d3d12_tests) ``` ## 8. CI 集成 ### 8.1 Windows CI 配置示例 ```yaml # .github/workflows/d3d12-tests.yml name: D3D12 Tests on: [push, pull_request] jobs: test: runs-on: windows-latest steps: - uses: actions/checkout@v3 - name: Configure run: cmake -B build -S . -G "Visual Studio 17 2022" - name: Build run: cmake --build build --config Debug - name: Run Tests run: ctest --test-dir build -C Debug --output-on-failure ``` ## 9. 测试覆盖矩阵 | 组件 | 单元测试 | 集成测试 | 渲染测试 | 性能测试 | |------|:--------:|:--------:|:--------:|:--------:| | D3D12Device | ✓ | - | - | ✓ | | D3D12CommandQueue | ✓ | ✓ | - | ✓ | | D3D12CommandAllocator | ✓ | - | - | - | | D3D12CommandList | ✓ | ✓ | - | - | | D3D12Buffer | ✓ | ✓ | - | ✓ | | D3D12Texture | ✓ | ✓ | ✓ | ✓ | | D3D12DescriptorHeap | ✓ | - | - | - | | D3D12PipelineState | ✓ | - | - | - | | D3D12RootSignature | ✓ | - | - | - | | D3D12Fence | ✓ | ✓ | - | - | | D3D12SwapChain | - | - | ✓ | - | | D3D12Shader | ✓ | - | - | - | | RTV/DSV/SRV/UAV | ✓ | ✓ | ✓ | - | ## 10. 组件详细测试用例设计 基于对所有 D3D12 组件源代码的深入分析,以下是每个组件的具体可测试 API 点和测试用例设计。 ### 10.1 D3D12Device 测试 **文件**: `test_device.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_WithDebugLayer` | 启用 Debug Layer 后设备创建成功 | | 初始化 | `Initialize_WithoutDebugLayer` | 不启用 Debug Layer 正常创建 | | 初始化 | `Initialize_InvalidAdapter` | 无效适配器返回失败 | | 适配器 | `EnumerateAdapters_ReturnsValidList` | 返回至少一个适配器 | | 适配器 | `EnumerateAdapters_ContainsRequiredInfo` | 适配器信息包含 Name/VRAM | | 特性查询 | `CheckFeatureSupport_FeatureLevels` | 查询支持的特性等级 | | 特性查询 | `CheckFeatureSupport_D3D12Options` | 查询 D3D12 选项支持 | | 描述符 | `GetDescriptorHandleIncrementSize_RTV` | RTV 描述符增量大小正确 | | 描述符 | `GetDescriptorHandleIncrementSize_DSV` | DSV 描述符增量大小正确 | | 描述符 | `GetDescriptorHandleIncrementSize_CBV_SRV_UAV` | SRV/CBV/UAV 增量正确 | | 工厂方法 | `CreateCommandQueue_ReturnsValidObject` | 创建命令队列成功 | | 工厂方法 | `CreateBuffer_ReturnsValidObject` | 创建 Buffer 成功 | | 工厂方法 | `CreateTexture_ReturnsValidObject` | 创建 Texture 成功 | | 工厂方法 | `CreateDescriptorHeap_ReturnsValidObject` | 创建描述符堆成功 | | 工厂方法 | `CreatePipelineState_ReturnsValidObject` | 创建 PSO 成功 | | 工厂方法 | `CreateRootSignature_ReturnsValidObject` | 创建根签名成功 | | 设备状态 | `SetDeviceRemoved_MarksAsRemoved` | 设备移除标记正确 | | 设备状态 | `IsDeviceRemoved_ReturnsCorrectState` | 移除状态查询正确 | **测试代码示例**: ```cpp TEST(D3D12Device, Initialize_WithDebugLayer_Success) { D3D12Device device; bool result = device.Initialize(true); ASSERT_TRUE(result); ASSERT_NE(device.GetDevice(), nullptr); // 验证 Debug Layer 已启用 ComPtr debugDevice; if (SUCCEEDED(device.GetDevice()->QueryInterface(IID_PPV_ARGS(&debugDevice)))) { ASSERT_NE(debugDevice.Get(), nullptr); } device.Shutdown(); } TEST(D3D12Device, EnumerateAdapters_ContainsValidInfo) { D3D12Device device; device.Initialize(); auto adapters = device.EnumerateAdapters(); ASSERT_FALSE(adapters.empty()); for (const auto& info : adapters) { EXPECT_FALSE(info.name.empty()); EXPECT_GE(info.vram, 0); } } TEST(D3D12Device, CreateBuffer_FactoryMethod) { D3D12Device device; device.Initialize(); BufferDesc desc; desc.size = 1024; desc.usage = ResourceUsage::VertexBuffer; auto* buffer = device.CreateBuffer(desc); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->GetSize(), 1024); } ``` --- ### 10.2 D3D12CommandQueue 测试 **文件**: `test_command_queue.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_DirectQueue` | Direct 类型队列创建成功 | | 初始化 | `Initialize_ComputeQueue` | Compute 类型队列创建成功 | | 初始化 | `Initialize_CopyQueue` | Copy 类型队列创建成功 | | 同步 | `Signal_SetsFenceValue` | Signal 设置围栏值 | | 同步 | `Wait_BlocksUntilSignal` | Wait 阻塞直到信号 | | 同步 | `SignalAndWait_Completes` | 信号后等待完成 | | 同步 | `WaitForIdle_BlocksAllWork` | 等待空闲完成所有工作 | | 查询 | `GetTimestampFrequency_ReturnsValid` | 时间戳频率有效 | | 查询 | `GetType_ReturnsCorrect` | 队列类型正确 | **测试代码示例**: ```cpp TEST(D3D12CommandQueue, Signal_SetsFenceValue) { D3D12Fence fence; fence.Initialize(GetDevice(), 0); GetCommandQueue()->Signal(fence.GetFence(), 1); // 需要等待完成 fence.Wait(1); EXPECT_EQ(fence.GetCompletedValue(), 1); } TEST(D3D12CommandQueue, WaitForIdle_BlocksAllWork) { // 先执行一些命令 D3D12Buffer buffer; buffer.Initialize(GetDevice(), 256, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); GetCommandList()->Close(); ID3D12CommandList* lists[] = { GetCommandList() }; GetCommandQueue()->ExecuteCommandLists(1, lists); // 等待空闲 GetCommandQueue()->WaitForIdle(); // 验证完成 EXPECT_TRUE(true); // 如果没崩溃说明成功 } ``` --- ### 10.3 D3D12CommandAllocator 测试 **文件**: `test_command_allocator.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_DirectType` | Direct 类型创建成功 | | 初始化 | `Initialize_ComputeType` | Compute 类型创建成功 | | 初始化 | `Initialize_CopyType` | Copy 类型创建成功 | | 重置 | `Reset_AfterCommandList` | 命令列表执行后重置成功 | | 状态 | `IsReady_BeforeAndAfterReset` | 重置前后状态正确 | **测试代码示例**: ```cpp TEST(D3D12CommandAllocator, Reset_AfterCommandListExecution) { D3D12CommandAllocator allocator; allocator.Initialize(GetDevice(), CommandQueueType::Direct); // 执行命令列表 GetCommandList()->Close(); ID3D12CommandList* lists[] = { GetCommandList() }; GetCommandQueue()->ExecuteCommandLists(1, lists); WaitForGPU(); // 重置分配器 allocator.Reset(); // 验证可以创建新的命令列表 ComPtr newList; HRESULT hr = GetDevice()->CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.GetCommandAllocator(), nullptr, IID_PPV_ARGS(&newList) ); ASSERT_TRUE(SUCCEEDED(hr)); } ``` --- ### 10.4 D3D12CommandList 测试 **文件**: `test_command_list.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 生命周期 | `Initialize_CreatesValidList` | 创建成功 | | 生命周期 | `Reset_ReusesAllocator` | 重置成功 | | 生命周期 | `Close_BeforeExecute` | 关闭后可以执行 | | 资源屏障 | `TransitionBarrier_ValidStates` | 状态转换正确 | | 资源屏障 | `UAVBarrier_CreatesBarrier` | UAV 屏障创建 | | 资源屏障 | `AliasBarrier_CreatesBarrier` | 别名屏障创建 | | 状态设置 | `SetViewport_SetsCorrectValues` | 视口设置正确 | | 状态设置 | `SetScissorRect_SetsCorrectValues` | 裁剪矩形设置正确 | | 状态设置 | `SetPrimitiveTopology_TriangleList` | 图元拓扑设置 | | 绘制 | `Draw_ValidParameters` | 绘制调用参数正确 | | 绘制 | `DrawIndexed_ValidParameters` | 索引绘制调用正确 | | 清除 | `ClearRenderTargetView_ClearsColor` | 清除颜色正确 | | 清除 | `ClearDepthStencilView_ClearsDepth` | 清除深度模板正确 | | 复制 | `CopyBuffer_TransfersData` | Buffer 复制正确 | | 复制 | `CopyTexture_TransfersData` | Texture 复制正确 | | 查询 | `BeginQuery_StartsQuery` | 查询开始 | | 查询 | `EndQuery_EndsQuery` | 查询结束 | | 查询 | `ResolveQueryData_CopiesResults` | 查询数据解析 | **测试代码示例**: ```cpp TEST(D3D12CommandList, TransitionBarrier_ValidStates) { D3D12Buffer buffer; buffer.Initialize(GetDevice(), 1024, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); // 从通用读取状态转换到渲染目标状态 GetCommandList()->TransitionBarrier( buffer.GetResource(), ResourceStates::GenericRead, ResourceStates::RenderTarget ); GetCommandList()->Close(); // 执行命令 ID3D12CommandList* lists[] = { GetCommandList() }; GetCommandQueue()->ExecuteCommandLists(1, lists); WaitForGPU(); // 成功执行无崩溃 SUCCEED(); } TEST(D3D12CommandList, ClearRenderTargetView_VerifyColor) { // 创建渲染目标 D3D12Texture rt; D3D12DescriptorHeap heap; heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 1); // 简化测试:创建 RTV // ... float clearColor[] = { 0.25f, 0.5f, 0.75f, 1.0f }; GetCommandList()->ClearRenderTargetView( heap.GetCPUDescriptorHandleForHeapStart(), clearColor, 0, nullptr ); GetCommandList()->Close(); ID3D12CommandList* lists[] = { GetCommandList() }; GetCommandQueue()->ExecuteCommandLists(1, lists); WaitForGPU(); SUCCEED(); } ``` --- ### 10.5 D3D12Buffer 测试 **文件**: `test_buffer.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_DefaultHeap` | 默认堆创建成功 | | 初始化 | `Initialize_UploadHeap` | 上传堆创建成功 | | 初始化 | `Initialize_ReadbackHeap` | 回读堆创建成功 | | 初始化 | `InitializeFromExisting_WrapsResource` | 包装现有资源 | | 初始化 | `InitializeWithData_CopiesData` | 带数据初始化复制数据 | | 数据操作 | `Map_ReturnsValidPointer` | Map 返回有效指针 | | 数据操作 | `Unmap_ReleasesPointer` | Unmap 释放指针 | | 数据操作 | `UpdateData_ModifiesContent` | 更新数据正确 | | 属性 | `GetGPUVirtualAddress_ReturnsValid` | GPU 虚拟地址有效 | | 属性 | `GetDesc_ReturnsCorrectDesc` | 描述符正确 | | 属性 | `GetSize_ReturnsCorrectSize` | 大小正确 | | 状态 | `GetState_ReturnsCurrent` | 状态查询正确 | | 状态 | `SetState_ChangesState` | 状态设置正确 | **测试代码示例**: ```cpp TEST(D3D12Buffer, Initialize_UploadHeap_MapsSuccessfully) { D3D12Buffer buffer; bool result = buffer.Initialize(GetDevice(), 1024, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_TYPE_UPLOAD); ASSERT_TRUE(result); // Map 操作 void* data = buffer.Map(0, nullptr); ASSERT_NE(data, nullptr); // 写入测试数据 memset(data, 0xAB, 1024); buffer.Unmap(0, nullptr); // 再次 Map 验证 data = buffer.Map(0, nullptr); ASSERT_NE(data, nullptr); // 验证数据 uint8_t* byteData = static_cast(data); for (int i = 0; i < 1024; ++i) { EXPECT_EQ(byteData[i], 0xAB); } buffer.Unmap(0, nullptr); } TEST(D3D12Buffer, InitializeWithData_DataIntegrity) { const size_t dataSize = 256; std::vector testData(dataSize); for (size_t i = 0; i < dataSize; ++i) { testData[i] = static_cast(i & 0xFF); } D3D12Buffer buffer; bool result = buffer.InitializeWithData( GetDevice(), GetCommandList(), testData.data(), dataSize, ResourceStates::VertexAndConstantBuffer ); ASSERT_TRUE(result); WaitForGPU(); // 验证 GPU 地址有效 EXPECT_NE(buffer.GetGPUVirtualAddress(), 0); } ``` --- ### 10.6 D3D12Texture 测试 **文件**: `test_texture.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_2DTexture` | 2D 纹理创建成功 | | 初始化 | `Initialize_3DTexture` | 3D 纹理创建成功 | | 初始化 | `Initialize_CubeTexture` | 立方体纹理创建成功 | | 初始化 | `Initialize_WithMipLevels` | 多 Mip 级别创建 | | 初始化 | `Initialize_DepthStencil` | 深度模板纹理创建 | | 初始化 | `InitializeFromData_CopiesPixels` | 带像素数据初始化 | | 属性 | `GetWidth_ReturnsCorrect` | 宽度正确 | | 属性 | `GetHeight_ReturnsCorrect` | 高度正确 | | 属性 | `GetDepth_ReturnsCorrect` | 深度正确 | | 属性 | `GetMipLevels_ReturnsCorrect` | Mip 级别数正确 | | 属性 | `GetFormat_ReturnsCorrect` | 格式正确 | | 状态 | `GetState_ReturnsCurrent` | 状态查询正确 | | 状态 | `SetState_ChangesState` | 状态设置正确 | **测试代码示例**: ```cpp TEST(D3D12Texture, Initialize_2DTexture_CorrectDimensions) { D3D12Texture texture; D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D( DXGI_FORMAT_R8G8B8A8_UNORM, 512, 512 ); bool result = texture.Initialize(GetDevice(), desc, D3D12_RESOURCE_STATE_COMMON); ASSERT_TRUE(result); EXPECT_EQ(texture.GetWidth(), 512); EXPECT_EQ(texture.GetHeight(), 512); EXPECT_EQ(texture.GetDepth(), 1); EXPECT_EQ(texture.GetMipLevels(), 1); } TEST(D3D12Texture, InitializeDepthStencil_CorrectFormat) { D3D12Texture depthStencil; bool result = depthStencil.InitializeDepthStencil( GetDevice(), 1280, 720, DXGI_FORMAT_D24_UNORM_S8_UINT ); ASSERT_TRUE(result); EXPECT_EQ(depthStencil.GetFormat(), Format::D24_UNorm_S8_UInt); } ``` --- ### 10.7 D3D12DescriptorHeap 测试 **文件**: `test_descriptor_heap.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_RTVHeap` | RTV 堆创建成功 | | 初始化 | `Initialize_DSVHeap` | DSV 堆创建成功 | | 初始化 | `Initialize_CBV_SRV_UAVHeap` | CBV/SRV/UAV 堆创建 | | 初始化 | `Initialize_SamplerHeap` | 采样器堆创建 | | 初始化 | `Initialize_ShaderVisible` | 着色器可见堆创建 | | 句柄 | `GetCPUDescriptorHandle_ValidIndex` | CPU 句柄正确 | | 句柄 | `GetGPUDescriptorHandle_ValidIndex` | GPU 句柄正确 | | 句柄 | `GetCPUDescriptorHandleForHeapStart` | 起始句柄正确 | | 属性 | `GetDescriptorCount_ReturnsCorrect` | 描述符数量正确 | | 属性 | `GetDescriptorSize_ReturnsCorrect` | 描述符大小正确 | | 属性 | `GetType_ReturnsCorrect` | 类型正确 | **测试代码示例**: ```cpp TEST(D3D12DescriptorHeap, Initialize_SRVHeapWithShaderVisible) { D3D12DescriptorHeap heap; bool result = heap.Initialize( GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 16, true // shader visible ); ASSERT_TRUE(result); EXPECT_EQ(heap.GetDescriptorCount(), 16); EXPECT_EQ(heap.GetType(), DescriptorHeapType::CBV_SRV_UAV); // 验证 GPU 句柄可用 auto gpuHandle = heap.GetGPUDescriptorHandle(0); EXPECT_NE(gpuHandle.ptr, 0); } TEST(D3D12DescriptorHeap, GetDescriptorSize_MatchesIncrement) { D3D12DescriptorHeap heap; heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4); UINT incrementSize = GetDevice()->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE_RTV ); EXPECT_EQ(heap.GetDescriptorSize(), incrementSize); } ``` --- ### 10.8 D3D12Shader 测试 **文件**: `test_shader.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 编译 | `CompileFromFile_VertexShader` | VS 编译成功 | | 编译 | `CompileFromFile_PixelShader` | PS 编译成功 | | 编译 | `CompileFromFile_GeometryShader` | GS 编译成功 | | 编译 | `CompileFromFile_ComputeShader` | CS 编译成功 | | 编译 | `CompileFromFile_InvalidFile` | 无效文件返回失败 | | 编译 | `CompileFromFile_InvalidEntry` | 无效入口点返回失败 | | 编译 | `CompileFromFile_InvalidTarget` | 无效目标返回失败 | | 字节码 | `GetD3D12Bytecode_ReturnsValid` | 字节码有效 | | 字节码 | `GetBytecodeSize_ReturnsNonZero` | 字节码大小非零 | | 属性 | `GetType_ReturnsCorrect` | 类型正确 | **测试代码示例**: ```cpp TEST(D3D12Shader, CompileFromFile_VertexShader_Success) { D3D12Shader shader; bool result = shader.CompileFromFile( L"Res/Shader/test_vs.hlsl", "main", "vs_5_1" ); ASSERT_TRUE(result); EXPECT_EQ(shader.GetType(), ShaderType::Vertex); EXPECT_GT(shader.GetBytecodeSize(), 0); } TEST(D3D12Shader, CompileFromFile_InvalidFile_ReturnsFalse) { D3D12Shader shader; bool result = shader.CompileFromFile( L"NonExistent.hlsl", "main", "vs_5_1" ); EXPECT_FALSE(result); } ``` --- ### 10.9 D3D12PipelineState 测试 **文件**: `test_pipeline_state.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_GraphicsPipeline` | 图形管线创建成功 | | 初始化 | `Initialize_ComputePipeline` | 计算管线创建成功 | | 工厂方法 | `CreateDesc_ValidParameters` | 描述符创建正确 | | 工厂方法 | `CreateInputElement_ValidParams` | 输入元素创建正确 | | 属性 | `GetPipelineState_ReturnsValid` | PSO 对象有效 | | 属性 | `GetType_ReturnsCorrect` | 类型正确 | **测试代码示例**: ```cpp TEST(D3D12PipelineState, Initialize_GraphicsPipeline_Success) { // 创建根签名 D3D12RootSignature rootSig; D3D12_ROOT_PARAMETER params[1] = {}; params[0] = D3D12RootSignature::CreateCBV(0); D3D12_ROOT_SIGNATURE_DESC rsDesc = D3D12RootSignature::CreateDesc(params, 1); rootSig.Initialize(GetDevice(), rsDesc); // 编译简单 Shader D3D12Shader vs, ps; vs.CompileFromFile(L"Res/Shader/test_vs.hlsl", "main", "vs_5_1"); ps.CompileFromFile(L"Res/Shader/test_ps.hlsl", "main", "ps_5_1"); // 创建 PSO D3D12PipelineState pso; auto desc = D3D12PipelineState::CreateDesc( rootSig.GetRootSignature(), vs.GetD3D12Bytecode(), ps.GetD3D12Bytecode(), {}, // GS 0, nullptr // input elements ); bool result = pso.Initialize(GetDevice(), desc); ASSERT_TRUE(result); ASSERT_NE(pso.GetPipelineState(), nullptr); } ``` --- ### 10.10 D3D12RootSignature 测试 **文件**: `test_root_signature.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_ValidDesc` | 有效描述符创建成功 | | 初始化 | `Initialize_WithCBV` | 带 CBV 参数创建 | | 初始化 | `Initialize_WithSRV` | 带 SRV 参数创建 | | 初始化 | `Initialize_WithDescriptorTable` | 带描述符表创建 | | 初始化 | `Initialize_WithStaticSampler` | 带静态采样器创建 | | 工厂方法 | `CreateCBV_ReturnsValid` | CBV 创建正确 | | 工厂方法 | `CreateSRV_ReturnsValid` | SRV 创建正确 | | 工厂方法 | `CreateUAV_ReturnsValid` | UAV 创建正确 | | 工厂方法 | `Create32BitConstants_ReturnsValid` | 常量创建正确 | | 工厂方法 | `CreateDescriptorTable_ReturnsValid` | 描述符表创建正确 | | 工厂方法 | `CreateSamplerDesc_ReturnsValid` | 采样器描述创建正确 | | 属性 | `GetRootSignature_ReturnsValid` | 根签名对象有效 | | 属性 | `GetParameterCount_ReturnsCorrect` | 参数数量正确 | **测试代码示例**: ```cpp TEST(D3D12RootSignature, Initialize_WithDescriptorTable) { D3D12_DESCRIPTOR_RANGE ranges[1]; ranges[0] = D3D12RootSignature::CreateDescriptorRange( D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1 ); D3D12_ROOT_PARAMETER params[1]; params[0] = D3D12RootSignature::CreateDescriptorTable(1, ranges); D3D12_ROOT_SIGNATURE_DESC desc = D3D12RootSignature::CreateDesc(params, 1); D3D12RootSignature rootSig; bool result = rootSig.Initialize(GetDevice(), desc); ASSERT_TRUE(result); ASSERT_NE(rootSig.GetRootSignature(), nullptr); EXPECT_EQ(rootSig.GetParameterCount(), 1); } TEST(D3D12RootSignature, CreateSamplerDesc_AllParameters) { auto desc = D3D12RootSignature::CreateSamplerDesc( FilterMode::Linear, TextureAddressMode::Clamp, 16.0f ); EXPECT_EQ(desc.Filter, D3D12_FILTER_MIN_MAG_MIP_LINEAR); EXPECT_EQ(desc.AddressU, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); EXPECT_EQ(desc.MaxLOD, 16.0f); } ``` --- ### 10.11 视图测试 (RTV/DSV/SRV/UAV/CBV) **文件**: `test_views.cpp` **可测试 API 点**: | 组件 | 测试用例 | 验证内容 | |------|----------|----------| | RTV | `Initialize_2DTexture` | 2D 纹理 RTV 创建 | | RTV | `CreateDesc_ValidFormat` | RTV 描述符创建 | | DSV | `Initialize_DepthTexture` | 深度纹理 DSV 创建 | | DSV | `CreateDesc_ValidFormat` | DSV 描述符创建 | | SRV | `Initialize_Texture` | 纹理 SRV 创建 | | SRV | `CreateDesc_WithMipLevels` | 带 Mip 级别 SRV | | UAV | `Initialize_Buffer` | Buffer UAV 创建 | | UAV | `Initialize_Texture` | Texture UAV 创建 | | CBV | `Initialize_Buffer` | Buffer CBV 创建 | | CBV | `Initialize_AutoDesc` | 自动描述符 CBV | **测试代码示例**: ```cpp TEST(D3D12RenderTargetView, Initialize_2DTexture_Success) { D3D12Texture texture; D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D( DXGI_FORMAT_R8G8B8A8_UNORM, 256, 256 ); texture.Initialize(GetDevice(), desc); D3D12DescriptorHeap heap; heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 1); D3D12RenderTargetView rtv; rtv.Initialize( GetDevice(), texture.GetResource(), nullptr ); EXPECT_NE(rtv.GetCPUDescriptorHandle().ptr, 0); } TEST(D3D12DepthStencilView, Initialize_DepthTexture_Success) { D3D12Texture depthTexture; depthTexture.InitializeDepthStencil( GetDevice(), 1280, 720, DXGI_FORMAT_D24_UNORM_S8_UINT ); D3D12DescriptorHeap heap; heap.Initialize(GetDevice(), DescriptorHeapType::DSV, 1); D3D12DepthStencilView dsv; auto desc = D3D12DepthStencilView::CreateDesc(Format::D24_UNorm_S8_UInt); dsv.Initialize( GetDevice(), depthTexture.GetResource(), &desc ); EXPECT_NE(dsv.GetCPUDescriptorHandle().ptr, 0); } ``` --- ### 10.12 D3D12Fence 测试 **文件**: `test_fence.cpp` **可测试 API 点**: | 测试类别 | 测试用例 | 验证内容 | |----------|----------|----------| | 初始化 | `Initialize_DefaultValue` | 默认初始值创建 | | 初始化 | `Initialize_CustomValue` | 自定义初始值创建 | | 同步 | `Signal_SetsValue` | Signal 设置值 | | 同步 | `Wait_BlocksUntilSignaled` | Wait 阻塞等待 | | 同步 | `GetCompletedValue_ReturnsCurrent` | 完成值查询 | | 事件 | `GetEventHandle_ReturnsValid` | 事件句柄有效 | **测试代码示例**: ```cpp TEST(D3D12Fence, Signal_UpdatesCompletedValue) { D3D12Fence fence; fence.Initialize(GetDevice(), 0); GetCommandQueue()->Signal(fence.GetFence(), 5); fence.Wait(5); EXPECT_EQ(fence.GetCompletedValue(), 5); } ``` --- ### 10.13 类型转换测试 **文件**: `test_types.cpp` **可测试 API 点**: | 组件 | 测试用例 | 验证内容 | |------|----------|----------| | D3D12Enum | `ToD3D12_FillMode_All` | 所有填充模式转换 | | D3D12Enum | `ToD3D12_CullMode_All` | 所有剔除模式转换 | | D3D12Enum | `ToD3D12_ComparisonFunc_All` | 所有比较函数转换 | | D3D12Enum | `ToD3D12_BlendOp_All` | 所有混合操作转换 | | D3D12Enum | `ToD3D12_Format_Common` | 常用格式转换 | | D3D12Enum | `ToD3D12_ResourceStates_All` | 所有资源状态转换 | | D3D12Types | `ToD3D12_Viewport_Correct` | Viewport 转换正确 | | D3D12Types | `ToD3D12_TextureDesc_Correct` | 纹理描述符转换正确 | | D3D12Types | `ToD3D12_BufferDesc_Correct` | Buffer 描述符转换正确 | | D3D12Common | `CheckFormatSupport_CommonFormats` | 常用格式支持检查 | | D3D12Common | `IsRenderTargetFormatSupported_Common` | 渲染目标格式支持 | **测试代码示例**: ```cpp TEST(D3D12Enum, ToD3D12_FillMode_All) { EXPECT_EQ(ToD3D12(FillMode::Wireframe), D3D12_FILL_MODE_WIREFRAME); EXPECT_EQ(ToD3D12(FillMode::Solid), D3D12_FILL_MODE_SOLID); } TEST(D3D12Enum, ToD3D12_ResourceStates_All) { EXPECT_EQ(ToD3D12(ResourceStates::Common), D3D12_RESOURCE_STATE_COMMON); EXPECT_EQ(ToD3D12(ResourceStates::RenderTarget), D3D12_RESOURCE_STATE_RENDER_TARGET); EXPECT_EQ(ToD3D12(ResourceStates::DepthWrite), D3D12_RESOURCE_STATE_DEPTH_WRITE); EXPECT_EQ(ToD3D12(ResourceStates::VertexAndConstantBuffer), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); EXPECT_EQ(ToD3D12(ResourceStates::IndexBuffer), D3D12_RESOURCE_STATE_INDEX_BUFFER); } ``` --- ## 11. 测试实现优先级 根据组件的依赖关系和重要性,建议按以下顺序实现测试: ### Phase 1: 核心基础设施 (高优先级) 1. **D3D12Device** - 所有其他组件依赖 2. **D3D12CommandQueue** - 命令执行基础 3. **D3D12CommandAllocator** - 命令分配基础 4. **D3D12Fence** - 同步基础 ### Phase 2: 资源管理 (中优先级) 5. **D3D12Buffer** - 最常用的资源 6. **D3D12Texture** - 纹理资源 7. **D3D12DescriptorHeap** - 描述符管理 ### Phase 3: 渲染管线 (中优先级) 8. **D3D12Shader** - 着色器编译 9. **D3D12RootSignature** - 根签名 10. **D3D12PipelineState** - 管线状态 ### Phase 4: 视图和命令 (低优先级) 11. **RTV/DSV/SRV/UAV/CBV** - 各种视图 12. **D3D12CommandList** - 命令录制 ### Phase 5: 高级功能 (最低优先级) 13. **D3D12SwapChain** - 需要窗口 14. **D3D12Screenshot** - 截图功能 15. **类型转换** - 辅助函数测试 --- ## 12. 测试数据文件 测试所需资源文件从 `tests/D3D12/Res/` 目录获取: ``` tests/D3D12/Res/ ├── Shader/ │ ├── test_vs.hlsl # 测试用顶点着色器 │ ├── test_ps.hlsl # 测试用像素着色器 │ ├── test_gs.hlsl # 测试用几何着色器 │ ├── test_cs.hlsl # 测试用计算着色器 │ └── test_pattern.hlsl # 图案化测试着色器 ├── Texture/ │ ├── test_256x256.png # 测试用 2D 纹理 │ └── test_cube.dds # 测试用立方体纹理 └── Golden/ ├── clear_color_gt.ppm # 清除颜色基准图像 ├── pattern_checker_gt.ppm # 棋盘格基准图像 └── gradient_gt.ppm # 渐变基准图像 ``` ### 12.1 在测试中引用资源 ```cpp // 通过编译定义获取资源路径 #ifndef TEST_RESOURCES_DIR #define TEST_RESOURCES_DIR "tests/D3D12/Res" #endif TEST(D3D12Shader, CompileFromFile_VertexShader) { D3D12Shader shader; // 构建资源路径 std::wstring shaderPath = std::wstring(TEST_RESOURCES_DIR) + L"/Shader/test_vs.hlsl"; bool result = shader.CompileFromFile(shaderPath.c_str(), "main", "vs_5_1"); ASSERT_TRUE(result); } --- ## 13. 构建计划与执行流程 本文档描述的测试框架将按照以下计划逐步构建,每完成一个步骤即进行测试、提交并推送。 ### 13.1 构建计划总览 | 步骤 | 文件 | 内容 | 验证方式 | |------|------|------|----------| | **步骤 1: 基础设施搭建** | | 1.1 | `engine/src/RHI/D3D12/test/CMakeLists.txt` | 测试构建配置 | CMake 配置检查 | | 1.2 | `engine/src/RHI/D3D12/test/fixtures/D3D12TestFixture.h` | 基础测试夹具 | 编译通过 | | **步骤 2: 核心组件测试** | | 2.1 | `test_device.cpp` | D3D12Device 初始化、适配器枚举、特性查询 | 运行测试 | | 2.2 | `test_fence.cpp` | D3D12Fence 同步、Signal/Wait | 运行测试 | | **步骤 3: 命令系统测试** | | 3.1 | `test_command_queue.cpp` | 队列创建、执行命令、同步 | 运行测试 | | 3.2 | `test_command_allocator.cpp` | 分配器创建、重置 | 运行测试 | | 3.3 | `test_command_list.cpp` | 命令录制、资源屏障、绘制 | 运行测试 | | **步骤 4: 资源测试** | | 4.1 | `test_buffer.cpp` | Buffer 创建、Map/Unmap、数据上传 | 运行测试 | | 4.2 | `test_texture.cpp` | 纹理创建、深度模板 | 运行测试 | | 4.3 | `test_descriptor_heap.cpp` | 描述符堆创建、句柄获取 | 运行测试 | | **步骤 5: 渲染管线测试** | | 5.1 | `test_shader.cpp` | Shader 编译 | 运行测试 | | 5.2 | `test_root_signature.cpp` | 根签名创建、参数 | 运行测试 | | 5.3 | `test_pipeline_state.cpp` | PSO 创建 | 运行测试 | | **步骤 6: 视图测试** | | 6.1 | `test_views.cpp` | RTV/DSV/SRV/UAV/CBV | 运行测试 | ### 13.2 执行流程 每完成一个步骤,按以下流程操作: ```bash # 1. 编译项目 cmake --build build --config Debug # 2. 运行测试 ctest --test-dir build -C Debug --output-on-failure # 3. 提交更改 git add . git commit -m "test: 添加 D3D12 Device 测试" # 4. 推送更改 git push ``` ### 13.3 CMake 集成方式 采用方案 A:在 `tests/CMakeLists.txt` 中添加子目录引用。 在 `tests/CMakeLists.txt` 中添加: ```cmake # 在现有 add_subdirectory 行之后添加 add_subdirectory(D3D12) ``` 创建 `tests/D3D12/CMakeLists.txt` 用于将 `engine/src/RHI/D3D12/test` 添加到构建: ```cmake # tests/D3D12/CMakeLists.txt cmake_minimum_required(VERSION 3.15) project(D3D12Integration) # 将 engine 测试目录添加到父级 CMake add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../engine/src/RHI/D3D12/test ${CMAKE_BINARY_DIR}/D3D12_test) ``` ### 13.4 测试夹具代码结构 基础测试夹具 `D3D12TestFixture.h` 提供以下功能: - 全局 D3D12 设备(所有测试共享) - 每测试周期的命令队列、分配器、命令列表 - GPU 等待辅助方法 - 资源清理保证 ### 13.5 资源路径配置 测试资源从 `tests/D3D12/Res/` 获取,通过 CMake 编译定义传递路径: ```cmake target_compile_definitions(d3d12_tests PRIVATE TEST_RESOURCES_DIR="${CMAKE_SOURCE_DIR}/tests/D3D12/Res" ) ``` ### 13.6 注意事项 1. **测试隔离**:每个测试用例应独立创建所需的资源 2. **资源清理**:在 TearDown 中确保等待 GPU 完成后释放资源 3. **调试层**:建议在 Debug 构建设中启用 D3D12 Debug Layer 4. **窗口依赖**:SwapChain 等需要窗口的测试放在最后 --- ## 14. 后续改进 - [x] 在 `tests/D3D12_engine/test/` 创建测试夹具文件 - [ ] 实现 Phase 1 核心基础设施测试 - [ ] 实现 Phase 2 资源管理测试 - [ ] 实现 Phase 3 渲染管线测试 - [ ] 实现 Phase 4 视图和命令测试 - [ ] 实现 Phase 5 高级功能测试 - [ ] 添加资源泄漏检测工具 - [ ] 完善渲染结果测试图案 - [ ] 添加性能基准测试 - [ ] 配置 CI 自动测试 - [ ] 支持 Vulkan 后端测试(复用测试夹具抽象)