Files
XCEngine/docs/used/D3D12后端测试设计.md
2026-03-29 01:36:53 +08:00

49 KiB
Raw Blame History

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: 核心基础设施 (高优先级)

  1. D3D12Device - 所有其他组件依赖
  2. D3D12CommandQueue - 命令执行基础
  3. D3D12CommandAllocator - 命令分配基础
  4. D3D12Fence - 同步基础

Phase 2: 资源管理 (中优先级)

  1. D3D12Buffer - 最常用的资源
  2. D3D12Texture - 纹理资源
  3. D3D12DescriptorHeap - 描述符管理

Phase 3: 渲染管线 (中优先级)

  1. D3D12Shader - 着色器编译
  2. D3D12RootSignature - 根签名
  3. D3D12PipelineState - 管线状态

Phase 4: 视图和命令 (低优先级)

  1. RTV/DSV/SRV/UAV/CBV - 各种视图
  2. D3D12CommandList - 命令录制

Phase 5: 高级功能 (最低优先级)

  1. D3D12SwapChain - 需要窗口
  2. D3D12Screenshot - 截图功能
  3. 类型转换 - 辅助函数测试

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 集成方式

采用方案 Atests/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 注意事项

  1. 测试隔离:每个测试用例应独立创建所需的资源
  2. 资源清理:在 TearDown 中确保等待 GPU 完成后释放资源
  3. 调试层:建议在 Debug 构建设中启用 D3D12 Debug Layer
  4. 窗口依赖SwapChain 等需要窗口的测试放在最后

14. 后续改进

  • tests/D3D12_engine/test/ 创建测试夹具文件
  • 实现 Phase 1 核心基础设施测试
  • 实现 Phase 2 资源管理测试
  • 实现 Phase 3 渲染管线测试
  • 实现 Phase 4 视图和命令测试
  • 实现 Phase 5 高级功能测试
  • 添加资源泄漏检测工具
  • 完善渲染结果测试图案
  • 添加性能基准测试
  • 配置 CI 自动测试
  • 支持 Vulkan 后端测试(复用测试夹具抽象)