Files
XCEngine/docs/used/OpenGL_Test_Restructuring_Plan.md
ssdfasd 16e2065c6c Unified logging: Replace LogSystem with EditorConsoleSink
- Created EditorConsoleSink (implements ILogSink interface)
- EditorConsoleSink stores logs in memory buffer (max 1000 entries)
- Added to Debug::Logger in Application::Initialize()
- ConsolePanel now reads from EditorConsoleSink via static GetInstance()
- Removed separate LogSystem singleton
- Removed editor/src/Core/LogEntry.h (no longer needed)

Now Editor and Engine share the same Debug::Logger, with ConsolePanel
displaying logs via EditorConsoleSink.
2026-03-25 16:13:02 +08:00

731 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenGL 测试架构重构方案
本文档是 XCEngine 测试规范的 OpenGL 专项补充,旨在将 OpenGL 后端测试体系重构为与 D3D12 同样规范的标准架构。
**前置阅读**:
- [tests/TEST_SPEC.md](../tests/TEST_SPEC.md) - 通用测试规范
- [tests/RHI/D3D12/TEST_SPEC.md](./D3D12/TEST_SPEC.md) - D3D12 专项规范(参考模板)
**规范版本**: 1.0
**最后更新**: 2026-03-20
---
## 1. 现状分析
### 1.1 当前目录结构
```
tests/RHI/OpenGL/
├── CMakeLists.txt # 混乱的一级配置
├── fixtures/
│ ├── OpenGLTestFixture.h
│ └── OpenGLTestFixture.cpp
├── test_device.cpp
├── test_buffer.cpp # 被注释,未启用
├── test_fence.cpp # 被注释,未启用
├── test_texture.cpp
├── test_shader.cpp # 被注释,未启用
├── test_pipeline_state.cpp
├── test_vertex_array.cpp
├── test_command_list.cpp
├── test_render_target_view.cpp
├── test_depth_stencil_view.cpp
├── test_swap_chain.cpp
├── test_sampler.cpp
└── Res/ # 空文件夹,无实际资源
├── Data/
├── Shader/
└── Texture/
```
### 1.2 当前测试统计
| 组件 | 文件 | 测试数 | 状态 |
|------|------|--------|------|
| Device | `test_device.cpp` | 6 | ✅ 启用 |
| Buffer | `test_buffer.cpp` | 6 | ❌ 被注释 |
| Fence | `test_fence.cpp` | 5 | ❌ 被注释 |
| Texture | `test_texture.cpp` | 4 | ✅ 启用 |
| Shader | `test_shader.cpp` | 4 | ❌ 被注释 |
| PipelineState | `test_pipeline_state.cpp` | 3 | ✅ 启用 |
| VertexArray | `test_vertex_array.cpp` | 1 | ✅ 启用 |
| CommandList | `test_command_list.cpp` | 14 | ✅ 启用 |
| Sampler | `test_sampler.cpp` | 2 | ✅ 启用 |
| SwapChain | `test_swap_chain.cpp` | 3 | ✅ 启用 |
| RTV | `test_render_target_view.cpp` | 2 | ✅ 启用 |
| DSV | `test_depth_stencil_view.cpp` | 2 | ✅ 启用 |
| **总计** | | **52** | **37 启用, 15 被注释** |
### 1.3 与 D3D12 对比
| 方面 | D3D12 (规范) | OpenGL (现状) |
|------|-------------|--------------|
| **目录分层** | `unit/` + `integration/` 分离 | 全部扁平 |
| **CMake 结构** | 顶层 + unit + integration 三级 | 仅一级 |
| **Fixture 设计** | 每测试独立设备 | 静态共享上下文 |
| **被注释测试** | 无 | 3 个文件被注释 |
| **集成测试** | 有 (Python + Golden Image) | **缺失** |
| **测试规范文档** | `TEST_SPEC.md` | **缺失** |
| **Res 资源** | 按测试隔离 | 空文件夹 |
| **资源复制** | POST_BUILD 自动复制 | 未配置 |
---
## 2. 问题详解
### 2.1 Fixture 设计缺陷
**当前问题**:
```cpp
// OpenGL - 静态成员所有测试共享,存在状态污染
static GLFWwindow* m_window; // 共享窗口
static bool m_contextInitialized; // 共享状态
static OpenGLDevice* m_device; // 共享设备
```
**D3D12 规范做法**:
```cpp
// 每个测试独立创建设备
void D3D12TestFixture::SetUp() {
D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0, ...);
}
```
**OpenGL 重构方向**:
```cpp
// 方案A: 每测试创建独立上下文 (推荐)
class OpenGLTestFixture : public ::testing::Test {
protected:
void SetUp() override {
// 创建独立 OpenGL 上下文
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
m_window = glfwCreateWindow(640, 480, "Test", nullptr, nullptr);
glfwMakeContextCurrent(m_window);
gladLoadGLLoader(...);
}
};
```
### 2.2 CMake 配置混乱
**当前问题**:
```cmake
# 硬编码绝对路径
include_directories(${CMAKE_SOURCE_DIR}/tests/OpenPackage/include/)
include_directories(${CMAKE_SOURCE_DIR}/tests/OpenPackage/lib/)
link_directories(${CMAKE_SOURCE_DIR}/tests/OpenPackage/lib/)
# 被注释的测试源文件
# test_buffer.cpp
# test_fence.cpp
# test_shader.cpp
```
**D3D12 规范做法**:
```cmake
# 顶层 CMakeLists.txt 仅做 add_subdirectory
add_subdirectory(unit)
add_subdirectory(integration)
# unit/CMakeLists.txt 独立配置
# integration/CMakeLists.txt 独立配置
```
### 2.3 资源目录为空
当前 `Res/` 下的 `Data/``Shader/``Texture/` 均为空目录,没有实际测试资源。
---
## 3. 重构目标
### 3.1 目标目录结构
```
tests/RHI/OpenGL/
├── CMakeLists.txt # 顶层配置
├── TEST_SPEC.md # 本文档 (OpenGL 专项)
├── TEST_IMPROVEMENT_PLAN.md # 改进计划
├── unit/
│ ├── CMakeLists.txt # 单元测试构建
│ ├── fixtures/
│ │ ├── OpenGLTestFixture.h
│ │ └── OpenGLTestFixture.cpp
│ ├── test_device.cpp
│ ├── test_buffer.cpp
│ ├── test_fence.cpp
│ ├── test_texture.cpp
│ ├── test_shader.cpp
│ ├── test_pipeline_state.cpp
│ ├── test_vertex_array.cpp
│ ├── test_command_list.cpp
│ ├── test_render_target_view.cpp
│ ├── test_depth_stencil_view.cpp
│ ├── test_swap_chain.cpp
│ └── test_sampler.cpp
└── integration/
├── CMakeLists.txt
├── run_integration_test.py # 公共脚本
├── compare_ppm.py # PPM 图像比对
├── run.bat # Windows 启动脚本
├── minimal/ # 最小化测试
│ ├── main.cpp
│ ├── GT_minimal.ppm
│ └── Res/
│ └── Shader/
│ ├── simple.vert
│ └── simple.frag
└── render_model/ # 模型渲染测试
├── main.cpp
├── GT.ppm
└── Res/
├── Image/
├── Model/
└── Shader/
```
### 3.2 测试数量目标
| 组件 | 当前 | 重构后 | 变化 |
|------|------|--------|------|
| Device | 6 | 6 | - |
| Buffer | 0 (被注释) | 6 | +6 |
| Fence | 0 (被注释) | 5 | +5 |
| Texture | 4 | 4 | - |
| Shader | 0 (被注释) | 4 | +4 |
| PipelineState | 3 | 3 | - |
| VertexArray | 1 | 2 | +1 |
| CommandList | 14 | 14 | - |
| Sampler | 2 | 2 | - |
| SwapChain | 3 | 3 | - |
| RTV | 2 | 2 | - |
| DSV | 2 | 2 | - |
| **单元测试总计** | **37** | **53** | **+16** |
| **集成测试** | **0** | **2** | **+2** |
---
## 4. 分阶段实施计划
### Phase 1: 目录结构重构
**目标**: 建立与 D3D12 一致的目录分层
**步骤**:
1. 创建 `unit/``integration/` 子目录
2. 移动现有测试文件到 `unit/`
3. 创建顶层 `CMakeLists.txt``add_subdirectory`
4. 重构 `unit/CMakeLists.txt` 独立配置
**目录变更**:
```
# 重构前
tests/RHI/OpenGL/CMakeLists.txt
tests/RHI/OpenGL/test_*.cpp
tests/RHI/OpenGL/fixtures/
# 重构后
tests/RHI/OpenGL/CMakeLists.txt # 顶层,仅 add_subdirectory
tests/RHI/OpenGL/unit/CMakeLists.txt # 单元测试构建
tests/RHI/OpenGL/unit/test_*.cpp # 测试文件
tests/RHI/OpenGL/unit/fixtures/ # Fixture
tests/RHI/OpenGL/integration/ # 新增
tests/RHI/OpenGL/integration/...
```
### Phase 2: Fixture 重构
**目标**: 消除静态共享状态,避免测试间污染
**重构内容**:
```cpp
// OpenGLTestFixture.h 重构
class OpenGLTestFixture : public ::testing::Test {
protected:
void SetUp() override;
void TearDown() override;
GLFWwindow* GetWindow() { return m_window; }
void MakeContextCurrent();
void DoneContextCurrent();
void ClearGLErrors();
bool CheckGLError(const char* file, int line);
void ResetGLState();
private:
GLFWwindow* m_window = nullptr;
OpenGLDevice* m_device = nullptr;
};
// OpenGLTestFixture.cpp 重构
void OpenGLTestFixture::SetUp() {
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
m_window = glfwCreateWindow(640, 480, "OpenGL Tests", nullptr, nullptr);
if (!m_window) {
GTEST_SKIP() << "Failed to create GLFW window";
return;
}
glfwMakeContextCurrent(m_window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
GTEST_SKIP() << "Failed to initialize GLAD";
glfwDestroyWindow(m_window);
m_window = nullptr;
return;
}
m_device = new OpenGLDevice();
m_device->CreateRenderWindow(640, 480, "Test Window", false);
ClearGLErrors();
}
void OpenGLTestFixture::TearDown() {
ResetGLState();
if (m_device) {
delete m_device;
m_device = nullptr;
}
if (m_window) {
glfwDestroyWindow(m_window);
m_window = nullptr;
}
}
```
### Phase 3: 启用被注释的测试
**目标**: 激活 `test_buffer.cpp``test_fence.cpp``test_shader.cpp`
**步骤**:
1. 取消 CMakeLists.txt 中的注释
2. 检查并修复可能的编译错误
3. 运行测试验证
**预期新增测试**:
- Buffer: 6 tests
- Fence: 5 tests
- Shader: 4 tests
### Phase 4: 完善单元测试
**目标**: 补充缺失的测试用例
**新增测试计划**:
| 组件 | 新增测试 | 说明 |
|------|---------|------|
| VertexArray | 1 | `VertexArray_Bind_MultipleAttributes` |
### Phase 5: 建立集成测试体系
**目标**: 建立与 D3D12 一致的集成测试框架
**步骤**:
1. 创建 `integration/` 目录结构
2. 复制 `run_integration_test.py``compare_ppm.py`
3. 创建 `minimal/` 集成测试
4. 创建 `render_model/` 集成测试
5. 配置 CTest 注册
**minimal/ 集成测试**:
```cpp
// integration/minimal/main.cpp
// 渲染一个简单三角形,输出 PPM 截图
int main() {
// 1. 初始化 GLFW + OpenGL
// 2. 创建窗口
// 3. 渲染简单场景
// 4. 截图保存为 minimal.ppm
// 5. 退出
}
```
**Golden Image 生成流程**:
1. 在干净硬件环境运行集成测试
2. 截图保存为 `GT_<name>.ppm`
3. 人工验证截图正确性
4. 提交到版本控制
### Phase 6: 编写测试规范文档
**目标**: 创建 `TEST_SPEC.md``TEST_IMPROVEMENT_PLAN.md`
---
## 5. CMake 重构详细方案
### 5.1 顶层 CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.15)
project(OpenGLEngineTests)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(unit)
add_subdirectory(integration)
```
### 5.2 unit/CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.15)
get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE)
find_package(OpenGL REQUIRED)
include_directories(${PROJECT_ROOT_DIR}/engine/include)
include_directories(${PROJECT_ROOT_DIR}/engine/src)
link_directories(${CMAKE_SOURCE_DIR}/tests/OpenGL/package/lib/)
find_package(GTest REQUIRED)
set(TEST_SOURCES
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/src/glad.c
fixtures/OpenGLTestFixture.cpp
test_device.cpp
test_buffer.cpp
test_fence.cpp
test_texture.cpp
test_shader.cpp
test_pipeline_state.cpp
test_vertex_array.cpp
test_command_list.cpp
test_render_target_view.cpp
test_depth_stencil_view.cpp
test_swap_chain.cpp
test_sampler.cpp
)
add_executable(opengl_engine_tests ${TEST_SOURCES})
target_link_libraries(opengl_engine_tests PRIVATE
opengl32
glfw3
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(opengl_engine_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${PROJECT_ROOT_DIR}/engine/include
${PROJECT_ROOT_DIR}/engine/src
)
target_compile_definitions(opengl_engine_tests PRIVATE
TEST_RESOURCES_DIR="${PROJECT_ROOT_DIR}/tests/RHI/OpenGL/integration/minimal/Res"
)
add_custom_command(TARGET opengl_engine_tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${PROJECT_ROOT_DIR}/tests/RHI/OpenGL/integration/minimal/Res
$<TARGET_FILE_DIR:opengl_engine_tests>/Res
)
enable_testing()
add_test(NAME OpenGLEngineTests COMMAND opengl_engine_tests)
```
### 5.3 integration/CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.15)
project(OpenGL_Integration)
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine)
find_package(Python3 REQUIRED)
enable_testing()
# Minimal test
add_executable(OpenGL_Minimal
WIN32
minimal/main.cpp
)
target_include_directories(OpenGL_Minimal PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/minimal
${ENGINE_ROOT_DIR}/include
)
target_link_libraries(OpenGL_Minimal PRIVATE
opengl32
glfw3
d3d12
dxgi
d3dcompiler
XCEngine
)
# Copy Res folder
add_custom_command(TARGET OpenGL_Minimal POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/minimal/Res
$<TARGET_FILE_DIR:OpenGL_Minimal>/Res
)
# Copy test scripts
add_custom_command(TARGET OpenGL_Minimal POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/run.bat
$<TARGET_FILE_DIR:OpenGL_Minimal>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/compare_ppm.py
$<TARGET_FILE_DIR:OpenGL_Minimal>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
$<TARGET_FILE_DIR:OpenGL_Minimal>/
)
# Register integration test with CTest
add_test(NAME OpenGL_Minimal_Integration
COMMAND ${Python3_EXECUTABLE} $<TARGET_FILE_DIR:OpenGL_Minimal>/run_integration_test.py
$<TARGET_FILE:OpenGL_Minimal>
minimal.ppm
${CMAKE_CURRENT_SOURCE_DIR}/minimal/GT_minimal.ppm
5
WORKING_DIRECTORY $<TARGET_FILE_DIR:OpenGL_Minimal>
)
```
---
## 6. 测试前缀对应表
| 类名 | 测试前缀 |
|------|---------|
| OpenGLDevice | Device |
| OpenGLBuffer | Buffer |
| OpenGLFence | Fence |
| OpenGLTexture | Texture |
| OpenGLShader | Shader |
| OpenGLPipelineState | PipelineState |
| OpenGLVertexArray | VertexArray |
| OpenGLCommandList | CommandList |
| OpenGLSampler | Sampler |
| OpenGLSwapChain | SwapChain |
| OpenGLRenderTargetView | RTV |
| OpenGLDepthStencilView | DSV |
---
## 7. 测试执行
### 7.1 单元测试
```bash
# 方式 1: 使用统一脚本
python scripts/run_tests.py --unit-only
# 方式 2: 直接使用 CTest
cd build/tests/RHI/OpenGL/unit
ctest -C Debug --output-on-failure
```
### 7.2 集成测试
```bash
# 方式 1: 使用统一脚本
python scripts/run_tests.py --integration
# 方式 2: 直接使用 CTest
cd build/tests/RHI/OpenGL/integration
ctest -C Debug --output-on-failure
```
### 7.3 构建和测试
```bash
# 构建
cmake --build . --target OpenGL_Minimal --config Debug
# 运行测试
python scripts/run_tests.py --build
```
---
## 8. CI 集成
### 8.1 GitHub Actions 配置
```yaml
name: OpenGL Tests
on: [push, pull_request]
jobs:
test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Debug
- name: Build OpenGL Tests
run: cmake --build build --target opengl_engine_tests OpenGL_Minimal --config Debug
- name: Run Unit Tests
run: cd build/tests/RHI/OpenGL/unit && ctest -C Debug --output-on-failure
- name: Run Integration Tests
run: python scripts/run_tests.py --integration
```
### 8.2 CI 模式
`--ci` 模式会跳过需要 GUI 的集成测试:
```bash
python scripts/run_tests.py --ci # 仅运行单元测试
```
---
## 9. 文件清单
### 9.1 需创建的文件
| 文件路径 | 说明 |
|---------|------|
| `tests/RHI/OpenGL/CMakeLists.txt` | 顶层 CMake 配置 |
| `tests/RHI/OpenGL/TEST_SPEC.md` | OpenGL 专项规范 |
| `tests/RHI/OpenGL/TEST_IMPROVEMENT_PLAN.md` | 改进计划 |
| `tests/RHI/OpenGL/unit/CMakeLists.txt` | 单元测试构建配置 |
| `tests/RHI/OpenGL/integration/CMakeLists.txt` | 集成测试构建配置 |
| `tests/RHI/OpenGL/integration/run_integration_test.py` | 集成测试运行脚本 |
| `tests/RHI/OpenGL/integration/compare_ppm.py` | PPM 图像比对脚本 |
| `tests/RHI/OpenGL/integration/run.bat` | Windows 启动脚本 |
| `tests/RHI/OpenGL/integration/minimal/main.cpp` | 最小化集成测试 |
| `tests/RHI/OpenGL/integration/minimal/GT_minimal.ppm` | Golden Image |
| `tests/RHI/OpenGL/integration/minimal/Res/Shader/*.vert` | Vertex Shader |
| `tests/RHI/OpenGL/integration/minimal/Res/Shader/*.frag` | Fragment Shader |
### 9.2 需修改的文件
| 文件路径 | 修改内容 |
|---------|---------|
| `tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h` | 重构 Fixture 接口 |
| `tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp` | 重构 Fixture 实现 |
| `tests/RHI/OpenGL/CMakeLists.txt` | 简化为 add_subdirectory |
### 9.3 需移动的文件
| 原路径 | 新路径 |
|-------|-------|
| `tests/RHI/OpenGL/test_*.cpp` | `tests/RHI/OpenGL/unit/test_*.cpp` |
| `tests/RHI/OpenGL/fixtures/*` | `tests/RHI/OpenGL/unit/fixtures/*` |
---
## 10. OpenGL 与 D3D12 测试差异说明
### 10.1 平台特性差异
| 方面 | D3D12 | OpenGL |
|------|-------|--------|
| **设备创建** | `D3D12CreateDevice()` 每测试独立 | 共享 GLFWcontext + Glad |
| **上下文管理** | 无 | GLFWwindow 生命周期 |
| **错误检查** | `HRESULT` 返回值 | `glGetError()` 状态码 |
| **渲染目标** | RTV/DSV descriptor | OpenGL Framebuffer Object |
| **同步原语** | `ID3D12Fence` | `glFenceSync` + `glClientWaitSync` |
| **管线状态** | PSO 对象 | OpenGL State Machine |
| **资源绑定** | CommandList + DescriptorHeap | `glBindBuffer`, `glBindTexture` |
### 10.2 Fixture 设计差异
```cpp
// D3D12 - 每测试独立 COM 对象
class D3D12TestFixture : public ::testing::Test {
ComPtr<ID3D12Device> mDevice;
ComPtr<ID3D12CommandQueue> mCommandQueue;
};
// OpenGL - 需要共享 context但每测试独立 window
class OpenGLTestFixture : public ::testing::Test {
GLFWwindow* m_window; // 每测试独立
OpenGLDevice* m_device; // 每测试独立
};
```
### 10.3 测试资源差异
| 方面 | D3D12 | OpenGL |
|------|-------|--------|
| **Shader 格式** | HLSL (.hlsl) | GLSL (.vert, .frag, .geom) |
| **纹理格式** | DDS | PNG/BMP/TGA |
| **模型格式** | 自定义 .lhsm | 自定义 .lhsm |
---
## 11. 已知问题与待办
### 11.1 Phase 1 待办
- [ ] 创建 `unit/``integration/` 目录
- [ ] 移动测试文件到 `unit/`
- [ ] 创建顶层 `CMakeLists.txt`
- [ ] 重构 `unit/CMakeLists.txt`
### 11.2 Phase 2 待办
- [ ] 重构 `OpenGLTestFixture` 消除静态成员
- [ ] 验证测试隔离效果
### 11.3 Phase 3 待办
- [ ] 启用 `test_buffer.cpp`
- [ ] 启用 `test_fence.cpp`
- [ ] 启用 `test_shader.cpp`
- [ ] 修复编译错误
### 11.4 Phase 4 待办
- [ ] 补充 `VertexArray_Bind_MultipleAttributes` 测试
### 11.5 Phase 5 待办
- [ ] 创建 `integration/` 目录结构
- [ ] 复制并适配 `run_integration_test.py`
- [ ] 复制并适配 `compare_ppm.py`
- [ ] 创建 `minimal/` 集成测试
- [ ] 创建 `render_model/` 集成测试
- [ ] 生成 Golden Image
### 11.6 Phase 6 待办
- [ ] 编写 `TEST_SPEC.md`
- [ ] 编写 `TEST_IMPROVEMENT_PLAN.md`
---
## 12. 规范更新记录
| 版本 | 日期 | 变更 |
|------|------|------|
| 1.0 | 2026-03-20 | 初始版本,参考 D3D12 TEST_SPEC.md 制定重构方案 |
---
**规范版本**: 1.0
**最后更新**: 2026-03-20
**前置文档**:
- [tests/TEST_SPEC.md](../tests/TEST_SPEC.md)
- [tests/RHI/D3D12/TEST_SPEC.md](./D3D12/TEST_SPEC.md)