From 0a19fdfb0f6ceb748ee0308a0aff24a1c409f87d Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 20 Mar 2026 17:37:09 +0800 Subject: [PATCH] OpenGL: Restructure tests similar to D3D12 layout - Move old test files to new unit/integration structure - Add OpenGL Test Fixture - Update CMakeLists.txt for new layout - Add OpenGL_Test_Restructuring_Plan.md --- OpenGL_Test_Restructuring_Plan.md | 731 ++++++++++++++++++ engine/src/RHI/OpenGL/OpenGLSwapChain.cpp | 8 +- tests/RHI/OpenGL/CMakeLists.txt | 53 +- tests/RHI/OpenGL/integration/CMakeLists.txt | 11 + tests/RHI/OpenGL/unit/CMakeLists.txt | 59 ++ .../{ => unit}/fixtures/OpenGLTestFixture.cpp | 74 +- .../{ => unit}/fixtures/OpenGLTestFixture.h | 10 +- tests/RHI/OpenGL/{ => unit}/test_buffer.cpp | 0 .../OpenGL/{ => unit}/test_command_list.cpp | 0 .../{ => unit}/test_depth_stencil_view.cpp | 0 tests/RHI/OpenGL/{ => unit}/test_device.cpp | 0 tests/RHI/OpenGL/{ => unit}/test_fence.cpp | 0 .../OpenGL/{ => unit}/test_pipeline_state.cpp | 0 .../{ => unit}/test_render_target_view.cpp | 0 tests/RHI/OpenGL/{ => unit}/test_sampler.cpp | 0 tests/RHI/OpenGL/{ => unit}/test_shader.cpp | 0 .../RHI/OpenGL/{ => unit}/test_swap_chain.cpp | 0 tests/RHI/OpenGL/{ => unit}/test_texture.cpp | 0 .../OpenGL/{ => unit}/test_vertex_array.cpp | 0 19 files changed, 843 insertions(+), 103 deletions(-) create mode 100644 OpenGL_Test_Restructuring_Plan.md create mode 100644 tests/RHI/OpenGL/integration/CMakeLists.txt create mode 100644 tests/RHI/OpenGL/unit/CMakeLists.txt rename tests/RHI/OpenGL/{ => unit}/fixtures/OpenGLTestFixture.cpp (68%) rename tests/RHI/OpenGL/{ => unit}/fixtures/OpenGLTestFixture.h (84%) rename tests/RHI/OpenGL/{ => unit}/test_buffer.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_command_list.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_depth_stencil_view.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_device.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_fence.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_pipeline_state.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_render_target_view.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_sampler.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_shader.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_swap_chain.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_texture.cpp (100%) rename tests/RHI/OpenGL/{ => unit}/test_vertex_array.cpp (100%) diff --git a/OpenGL_Test_Restructuring_Plan.md b/OpenGL_Test_Restructuring_Plan.md new file mode 100644 index 00000000..36aab7e6 --- /dev/null +++ b/OpenGL_Test_Restructuring_Plan.md @@ -0,0 +1,731 @@ +# 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_.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 + $/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 + $/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 + $/ + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/compare_ppm.py + $/ + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py + $/ +) + +# Register integration test with CTest +add_test(NAME OpenGL_Minimal_Integration + COMMAND ${Python3_EXECUTABLE} $/run_integration_test.py + $ + minimal.ppm + ${CMAKE_CURRENT_SOURCE_DIR}/minimal/GT_minimal.ppm + 5 + WORKING_DIRECTORY $ +) +``` + +--- + +## 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 mDevice; + ComPtr 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) \ No newline at end of file diff --git a/engine/src/RHI/OpenGL/OpenGLSwapChain.cpp b/engine/src/RHI/OpenGL/OpenGLSwapChain.cpp index 8d08255b..a43f6629 100644 --- a/engine/src/RHI/OpenGL/OpenGLSwapChain.cpp +++ b/engine/src/RHI/OpenGL/OpenGLSwapChain.cpp @@ -109,7 +109,13 @@ void OpenGLSwapChain::Present(uint32_t syncInterval, uint32_t flags) { } void OpenGLSwapChain::Resize(uint32_t width, uint32_t height) { - Resize(static_cast(width), static_cast(height)); + m_width = width; + m_height = height; + glfwSetWindowSize(m_window, width, height); + int w, h; + glfwGetFramebufferSize(m_window, &w, &h); + m_framebufferWidth = w; + m_framebufferHeight = h; } void OpenGLSwapChain::SetFullscreen(bool fullscreen) { diff --git a/tests/RHI/OpenGL/CMakeLists.txt b/tests/RHI/OpenGL/CMakeLists.txt index 1cabed26..4a133765 100644 --- a/tests/RHI/OpenGL/CMakeLists.txt +++ b/tests/RHI/OpenGL/CMakeLists.txt @@ -5,54 +5,5 @@ project(OpenGLEngineTests) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../.. ABSOLUTE) - -find_package(OpenGL REQUIRED) - -include_directories(${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include/) -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_sampler.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 -) - -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/Res" -) - -enable_testing() -add_test(NAME OpenGLEngineTests COMMAND opengl_engine_tests) +add_subdirectory(unit) +add_subdirectory(integration) \ No newline at end of file diff --git a/tests/RHI/OpenGL/integration/CMakeLists.txt b/tests/RHI/OpenGL/integration/CMakeLists.txt new file mode 100644 index 00000000..b8d4d4d0 --- /dev/null +++ b/tests/RHI/OpenGL/integration/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) + +project(OpenGL_Integration) + +set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine) + +find_package(Python3 REQUIRED) + +enable_testing() + +message(STATUS "OpenGL integration tests placeholder - to be implemented in Phase 5") \ No newline at end of file diff --git a/tests/RHI/OpenGL/unit/CMakeLists.txt b/tests/RHI/OpenGL/unit/CMakeLists.txt new file mode 100644 index 00000000..958273d8 --- /dev/null +++ b/tests/RHI/OpenGL/unit/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.15) + +get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE) + +find_package(OpenGL REQUIRED) + +include_directories(${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include/) +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 + $/Res +) + +enable_testing() +add_test(NAME OpenGLEngineTests COMMAND opengl_engine_tests) \ No newline at end of file diff --git a/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp b/tests/RHI/OpenGL/unit/fixtures/OpenGLTestFixture.cpp similarity index 68% rename from tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp rename to tests/RHI/OpenGL/unit/fixtures/OpenGLTestFixture.cpp index d02edafb..c353085d 100644 --- a/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp +++ b/tests/RHI/OpenGL/unit/fixtures/OpenGLTestFixture.cpp @@ -2,45 +2,51 @@ using namespace XCEngine::RHI; -GLFWwindow* OpenGLTestFixture::m_window = nullptr; -bool OpenGLTestFixture::m_contextInitialized = false; -OpenGLDevice* OpenGLTestFixture::m_device = nullptr; +static bool s_glfwInitialized = false; +static bool s_gladInitialized = false; -void OpenGLTestFixture::SetUpTestSuite() { - if (!glfwInit()) { - GTEST_SKIP() << "Failed to initialize GLFW"; - return; +void OpenGLTestFixture::SetUp() { + if (!s_glfwInitialized) { + if (!glfwInit()) { + GTEST_SKIP() << "Failed to initialize GLFW"; + return; + } + + 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); + + s_glfwInitialized = true; } - 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"; - glfwTerminate(); return; } glfwMakeContextCurrent(m_window); - - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { - GTEST_SKIP() << "Failed to initialize GLAD"; - glfwDestroyWindow(m_window); - m_window = nullptr; - glfwTerminate(); - return; + + if (!s_gladInitialized) { + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { + GTEST_SKIP() << "Failed to initialize GLAD"; + glfwDestroyWindow(m_window); + m_window = nullptr; + return; + } + s_gladInitialized = true; } - - m_contextInitialized = true; m_device = new OpenGLDevice(); m_device->CreateRenderWindow(640, 480, "Test Window", false); + + ClearGLErrors(); } -void OpenGLTestFixture::TearDownTestSuite() { +void OpenGLTestFixture::TearDown() { + ResetGLState(); + if (m_device) { delete m_device; m_device = nullptr; @@ -50,26 +56,6 @@ void OpenGLTestFixture::TearDownTestSuite() { glfwDestroyWindow(m_window); m_window = nullptr; } - glfwTerminate(); - m_contextInitialized = false; -} - -void OpenGLTestFixture::SetUp() { - if (!m_window || !m_contextInitialized) { - GTEST_SKIP() << "OpenGL context not available"; - return; - } - - glfwMakeContextCurrent(m_window); - ClearGLErrors(); -} - -void OpenGLTestFixture::TearDown() { - GLenum error = glGetError(); - if (error != GL_NO_ERROR) { - } - - ResetGLState(); } void OpenGLTestFixture::MakeContextCurrent() { @@ -135,4 +121,4 @@ void OpenGLTestFixture::ResetGLState() { glDepthFunc(GL_LESS); glStencilFunc(GL_ALWAYS, 0, 0xFF); glBlendFunc(GL_ONE, GL_ZERO); -} +} \ No newline at end of file diff --git a/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h b/tests/RHI/OpenGL/unit/fixtures/OpenGLTestFixture.h similarity index 84% rename from tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h rename to tests/RHI/OpenGL/unit/fixtures/OpenGLTestFixture.h index 9274d2f7..30f4e9da 100644 --- a/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h +++ b/tests/RHI/OpenGL/unit/fixtures/OpenGLTestFixture.h @@ -11,9 +11,6 @@ namespace RHI { class OpenGLTestFixture : public ::testing::Test { protected: - static void SetUpTestSuite(); - static void TearDownTestSuite(); - void SetUp() override; void TearDown() override; @@ -27,9 +24,8 @@ protected: void ResetGLState(); private: - static GLFWwindow* m_window; - static bool m_contextInitialized; - static OpenGLDevice* m_device; + GLFWwindow* m_window = nullptr; + OpenGLDevice* m_device = nullptr; }; } // namespace RHI @@ -50,4 +46,4 @@ private: call; \ EXPECT_EQ(glGetError(), GL_NO_ERROR) \ << "GL error after " << #call; \ - } while(0) + } while(0) \ No newline at end of file diff --git a/tests/RHI/OpenGL/test_buffer.cpp b/tests/RHI/OpenGL/unit/test_buffer.cpp similarity index 100% rename from tests/RHI/OpenGL/test_buffer.cpp rename to tests/RHI/OpenGL/unit/test_buffer.cpp diff --git a/tests/RHI/OpenGL/test_command_list.cpp b/tests/RHI/OpenGL/unit/test_command_list.cpp similarity index 100% rename from tests/RHI/OpenGL/test_command_list.cpp rename to tests/RHI/OpenGL/unit/test_command_list.cpp diff --git a/tests/RHI/OpenGL/test_depth_stencil_view.cpp b/tests/RHI/OpenGL/unit/test_depth_stencil_view.cpp similarity index 100% rename from tests/RHI/OpenGL/test_depth_stencil_view.cpp rename to tests/RHI/OpenGL/unit/test_depth_stencil_view.cpp diff --git a/tests/RHI/OpenGL/test_device.cpp b/tests/RHI/OpenGL/unit/test_device.cpp similarity index 100% rename from tests/RHI/OpenGL/test_device.cpp rename to tests/RHI/OpenGL/unit/test_device.cpp diff --git a/tests/RHI/OpenGL/test_fence.cpp b/tests/RHI/OpenGL/unit/test_fence.cpp similarity index 100% rename from tests/RHI/OpenGL/test_fence.cpp rename to tests/RHI/OpenGL/unit/test_fence.cpp diff --git a/tests/RHI/OpenGL/test_pipeline_state.cpp b/tests/RHI/OpenGL/unit/test_pipeline_state.cpp similarity index 100% rename from tests/RHI/OpenGL/test_pipeline_state.cpp rename to tests/RHI/OpenGL/unit/test_pipeline_state.cpp diff --git a/tests/RHI/OpenGL/test_render_target_view.cpp b/tests/RHI/OpenGL/unit/test_render_target_view.cpp similarity index 100% rename from tests/RHI/OpenGL/test_render_target_view.cpp rename to tests/RHI/OpenGL/unit/test_render_target_view.cpp diff --git a/tests/RHI/OpenGL/test_sampler.cpp b/tests/RHI/OpenGL/unit/test_sampler.cpp similarity index 100% rename from tests/RHI/OpenGL/test_sampler.cpp rename to tests/RHI/OpenGL/unit/test_sampler.cpp diff --git a/tests/RHI/OpenGL/test_shader.cpp b/tests/RHI/OpenGL/unit/test_shader.cpp similarity index 100% rename from tests/RHI/OpenGL/test_shader.cpp rename to tests/RHI/OpenGL/unit/test_shader.cpp diff --git a/tests/RHI/OpenGL/test_swap_chain.cpp b/tests/RHI/OpenGL/unit/test_swap_chain.cpp similarity index 100% rename from tests/RHI/OpenGL/test_swap_chain.cpp rename to tests/RHI/OpenGL/unit/test_swap_chain.cpp diff --git a/tests/RHI/OpenGL/test_texture.cpp b/tests/RHI/OpenGL/unit/test_texture.cpp similarity index 100% rename from tests/RHI/OpenGL/test_texture.cpp rename to tests/RHI/OpenGL/unit/test_texture.cpp diff --git a/tests/RHI/OpenGL/test_vertex_array.cpp b/tests/RHI/OpenGL/unit/test_vertex_array.cpp similarity index 100% rename from tests/RHI/OpenGL/test_vertex_array.cpp rename to tests/RHI/OpenGL/unit/test_vertex_array.cpp