- Rename Enums.h to RHIEnums.h - Rename Types.h to RHITypes.h - Update all include references in D3D12 headers and test files
1392 lines
41 KiB
Markdown
1392 lines
41 KiB
Markdown
# 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. 组件详细测试用例设计
|
||
|
||
基于对所有 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/
|
||
├── Shaders/
|
||
│ ├── test_vs.hlsl # 测试用顶点着色器
|
||
│ ├── test_ps.hlsl # 测试用像素着色器
|
||
│ ├── test_gs.hlsl # 测试用几何着色器
|
||
│ ├── test_cs.hlsl # 测试用计算着色器
|
||
│ └── test_pattern.hlsl # 图案化测试着色器
|
||
├── Textures/
|
||
│ ├── test_256x256.png # 测试用 2D 纹理
|
||
│ └── test_cube.dds # 测试用立方体纹理
|
||
└── Golden/
|
||
├── clear_color_gt.ppm # 清除颜色基准图像
|
||
├── pattern_checker_gt.ppm # 棋盘格基准图像
|
||
└── gradient_gt.ppm # 渐变基准图像
|
||
```
|
||
|
||
---
|
||
|
||
## 13. 后续改进
|
||
|
||
- [ ] 实现 Phase 1 核心基础设施测试
|
||
- [ ] 实现 Phase 2 资源管理测试
|
||
- [ ] 实现 Phase 3 渲染管线测试
|
||
- [ ] 实现 Phase 4 视图和命令测试
|
||
- [ ] 实现 Phase 5 高级功能测试
|
||
- [ ] 添加资源泄漏检测工具
|
||
- [ ] 完善渲染结果测试图案
|
||
- [ ] 添加性能基准测试
|
||
- [ ] 配置 CI 自动测试
|
||
- [ ] 支持 Vulkan 后端测试(复用测试夹具抽象)
|