docs: 添加 D3D12 后端测试设计文档
This commit is contained in:
596
docs/D3D12后端测试设计.md
Normal file
596
docs/D3D12后端测试设计.md
Normal file
@@ -0,0 +1,596 @@
|
|||||||
|
# D3D12 后端测试设计
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
本文档描述 XCEngine D3D12 渲染后端的测试框架设计,旨在为 D3D12 各组件提供全面、规范的自动化测试覆盖。
|
||||||
|
|
||||||
|
### 1.1 测试目标
|
||||||
|
|
||||||
|
- 验证 D3D12 各组件 API 的正确性
|
||||||
|
- 确保组件间的协作正常工作
|
||||||
|
- 捕获资源泄漏和内存错误
|
||||||
|
- 支持持续集成(CI)自动化测试
|
||||||
|
|
||||||
|
### 1.2 现有问题
|
||||||
|
|
||||||
|
当前 `tests/D3D12/main.cpp` 存在以下问题:
|
||||||
|
|
||||||
|
| 问题 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 非自动化测试 | 截图和对比需手动触发 |
|
||||||
|
| 缺乏单元测试 | 只有一个 Win32 图形程序,无法使用 Google Test |
|
||||||
|
| 维护困难 | GT.ppm 是纯黑色图像,对比无实际意义 |
|
||||||
|
| 容差问题 | 1% 阈值对硬件差异过于敏感 |
|
||||||
|
|
||||||
|
## 2. 测试目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/D3D12/
|
||||||
|
├── CMakeLists.txt # 构建配置(需改造为 Google Test)
|
||||||
|
├── fixtures/
|
||||||
|
│ └── D3D12TestFixture.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_swap_chain.cpp # D3D12SwapChain 测试(需窗口)
|
||||||
|
├── test_shader.cpp # D3D12Shader 测试
|
||||||
|
├── test_views.cpp # RTV/DSV/SRV/UAV 测试
|
||||||
|
└── test_screenshot.cpp # 渲染结果测试
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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. 后续改进
|
||||||
|
|
||||||
|
- [ ] 实现所有组件的基础单元测试
|
||||||
|
- [ ] 添加资源泄漏检测工具
|
||||||
|
- [ ] 完善渲染结果测试图案
|
||||||
|
- [ ] 添加性能基准测试
|
||||||
|
- [ ] 配置 CI 自动测试
|
||||||
|
- [ ] 支持 Vulkan 后端测试(复用测试夹具抽象)
|
||||||
Reference in New Issue
Block a user