Files
XCEngine/docs/D3D12后端测试设计.md

1594 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 新测试框架目录
`engine/src/RHI/D3D12/` 下创建新的测试模块:
```
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 <gtest/gtest.h>
#include <d3d12.h>
#include <wrl/client.h>
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<ID3D12Fence> 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<ID3D12Device> mDevice;
ComPtr<ID3D12CommandQueue> mCommandQueue;
ComPtr<ID3D12CommandAllocator> mCommandAllocator;
ComPtr<ID3D12CommandList> mCommandList;
};
//
ComPtr<ID3D12Device> 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<uint8_t> 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<std::chrono::milliseconds>(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<ID3D12Fence> 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<ID3D12DebugDevice> 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<ID3D12GraphicsCommandList> 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<uint8_t*>(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<uint8_t> testData(dataSize);
for (size_t i = 0; i < dataSize; ++i) {
testData[i] = static_cast<uint8_t>(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. 后续改进
- [ ]`engine/src/RHI/D3D12/test/` 创建测试夹具文件
- [ ] 实现 Phase 1 核心基础设施测试
- [ ] 实现 Phase 2 资源管理测试
- [ ] 实现 Phase 3 渲染管线测试
- [ ] 实现 Phase 4 视图和命令测试
- [ ] 实现 Phase 5 高级功能测试
- [ ] 添加资源泄漏检测工具
- [ ] 完善渲染结果测试图案
- [ ] 添加性能基准测试
- [ ] 配置 CI 自动测试
- [ ] 支持 Vulkan 后端测试(复用测试夹具抽象)