49 KiB
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()
# 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 资源测试夹具
// 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 的基本功能,不依赖图形硬件渲染结果。
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。
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 推荐方案:渲染特定图案
生成一个已知的测试图案(如渐变、棋盘格),渲染后读取像素验证:
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 渲染特定图案:
// 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 或简单计时:
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
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
TEST(D3D12_CommandList, Reset_AfterClose_Succeeds) {
// 第一次使用
GetCommandList()->Close();
// 重置并再次使用
HRESULT hr = GetCommandList()->Reset(mCommandAllocator.Get(), nullptr);
ASSERT_TRUE(SUCCEEDED(hr));
}
5.3 D3D12Fence
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
#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 在测试夹具中使用
class D3D12LeakCheckFixture : public D3D12TestFixture {
protected:
void TearDown() override {
D3D12TestFixture::TearDown();
#ifdef _DEBUG
D3D12LeakDetector::EndFrame();
#endif
}
};
7. 构建配置
7.1 CMakeLists.txt 修改
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 配置示例
# .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 |
移除状态查询正确 |
测试代码示例:
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 |
队列类型正确 |
测试代码示例:
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 |
重置前后状态正确 |
测试代码示例:
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 |
查询数据解析 |
测试代码示例:
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 |
状态设置正确 |
测试代码示例:
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 |
状态设置正确 |
测试代码示例:
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 |
类型正确 |
测试代码示例:
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 |
类型正确 |
测试代码示例:
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 |
类型正确 |
测试代码示例:
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 |
参数数量正确 |
测试代码示例:
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 |
测试代码示例:
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 |
事件句柄有效 |
测试代码示例:
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 |
渲染目标格式支持 |
测试代码示例:
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: 核心基础设施 (高优先级)
- D3D12Device - 所有其他组件依赖
- D3D12CommandQueue - 命令执行基础
- D3D12CommandAllocator - 命令分配基础
- D3D12Fence - 同步基础
Phase 2: 资源管理 (中优先级)
- D3D12Buffer - 最常用的资源
- D3D12Texture - 纹理资源
- D3D12DescriptorHeap - 描述符管理
Phase 3: 渲染管线 (中优先级)
- D3D12Shader - 着色器编译
- D3D12RootSignature - 根签名
- D3D12PipelineState - 管线状态
Phase 4: 视图和命令 (低优先级)
- RTV/DSV/SRV/UAV/CBV - 各种视图
- D3D12CommandList - 命令录制
Phase 5: 高级功能 (最低优先级)
- D3D12SwapChain - 需要窗口
- D3D12Screenshot - 截图功能
- 类型转换 - 辅助函数测试
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 在测试中引用资源
// 通过编译定义获取资源路径
#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 中添加:
# 在现有 add_subdirectory 行之后添加
add_subdirectory(D3D12)
创建 tests/D3D12/CMakeLists.txt 用于将 engine/src/RHI/D3D12/test 添加到构建:
# 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 编译定义传递路径:
target_compile_definitions(d3d12_tests PRIVATE
TEST_RESOURCES_DIR="${CMAKE_SOURCE_DIR}/tests/D3D12/Res"
)
13.6 注意事项
- 测试隔离:每个测试用例应独立创建所需的资源
- 资源清理:在 TearDown 中确保等待 GPU 完成后释放资源
- 调试层:建议在 Debug 构建设中启用 D3D12 Debug Layer
- 窗口依赖:SwapChain 等需要窗口的测试放在最后
14. 后续改进
- 在
tests/D3D12_engine/test/创建测试夹具文件 - 实现 Phase 1 核心基础设施测试
- 实现 Phase 2 资源管理测试
- 实现 Phase 3 渲染管线测试
- 实现 Phase 4 视图和命令测试
- 实现 Phase 5 高级功能测试
- 添加资源泄漏检测工具
- 完善渲染结果测试图案
- 添加性能基准测试
- 配置 CI 自动测试
- 支持 Vulkan 后端测试(复用测试夹具抽象)