refactor: improve test infrastructure and fix OpenGL GLAD initialization

- Rename D3D12Enum.h to D3D12Enums.h for naming consistency
- Fix OpenGL unit test GLAD initialization by using gladLoadGL()
  instead of gladLoadGLLoader(wglGetProcAddress) for fallback support
- Migrate remaining tests to use gtest_discover_tests for granular
  test discovery (math, core, containers, memory, threading, debug,
  components, scene, resources, input, opengl)
- Remove obsolete TEST_RESOURCES_DIR and copy_directory commands
  from OpenGL unit test CMakeLists (minimal/Res doesn't exist)
- Update TEST_SPEC.md with performance metrics and per-module
  build/test commands for faster development workflow
- Update CMake path references to use lowercase paths
This commit is contained in:
2026-03-23 00:43:02 +08:00
parent 0f0ab8922a
commit f427699ac6
100 changed files with 1191 additions and 1136 deletions

View File

@@ -4,6 +4,8 @@ project(XCEngine)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
enable_testing()
add_subdirectory(engine)
add_subdirectory(tests)
add_subdirectory(tests/OpenGL)
add_subdirectory(tests/opengl)

View File

@@ -0,0 +1 @@
---

View File

@@ -62,7 +62,7 @@ RHI 模块将上层渲染逻辑与底层图形 API 解耦,通过抽象接口
| 类 | 文档 | 描述 |
|----|------|------|
| [RHIFence](rhi-fence/fence.md) | `RHIFence.h` | 同步栅栏CPU/GPU 同步原语 |
| [RHIFence](fence/fence.md) | `RHIFence.h` | 同步栅栏CPU/GPU 同步原语 |
| [RHIPipelineState](pipeline-state/pipeline-state.md) | `RHIPipelineState.h` | 管线状态对象,封装渲染管线配置 |
| [RHISampler](sampler/sampler.md) | `RHISampler.h` | 纹理采样器,配置纹理过滤和寻址模式 |
| [RHIPipelineLayout](pipeline-layout/pipeline-layout.md) | `RHIPipelineLayout.h` | 管线布局,定义着色器资源绑定布局 |

View File

@@ -1,6 +1,6 @@
# Scene 模块概览
**命名空间**: `XCEngine::Components`
**命名空间**: `XCEngine::Scene`
**类型**: `module`

View File

@@ -23,9 +23,9 @@ Mutex 类是对 std::mutex 的封装,提供线程安全的互斥访问机制
| [`Lock`](lock.md) | 获取互斥锁(非 const |
| [`Unlock`](unlock.md) | 释放互斥锁(非 const |
| [`TryLock`](trylock.md) | 尝试获取互斥锁(非 const |
| [`lock`](lock_const.md) | 获取互斥锁constSTL 兼容) |
| [`unlock`](unlock_const.md) | 释放互斥锁constSTL 兼容) |
| [`try_lock`](try_lock_const.md) | 尝试获取互斥锁constSTL 兼容) |
| [`lock`](lock-const.md) | 获取互斥锁constSTL 兼容) |
| [`unlock`](unlock-const.md) | 释放互斥锁constSTL 兼容) |
| [`try_lock`](try-lock-const.md) | 尝试获取互斥锁constSTL 兼容) |
## 使用示例
@@ -58,6 +58,6 @@ int main() {
## 相关文档
- [SpinLock](../spinlock/spinlock.md) - 自旋锁
- [ReadWriteLock](../readwritelock/readwritelock.md) - 读写锁
- [ReadWriteLock](../read-write-lock/read-write-lock.md) - 读写锁
- [TaskSystem](../task-system/task-system.md) - 任务系统
- [../threading/threading.md](../threading.md) - 模块总览

View File

@@ -19,9 +19,9 @@
| [`Lock`](lock.md) | 获取锁(忙等待) |
| [`Unlock`](unlock.md) | 释放锁 |
| [`TryLock`](trylock.md) | 尝试获取锁(非阻塞) |
| [`lock`](lock_1.md) | STL 兼容的 Lock |
| [`unlock`](unlock_1.md) | STL 兼容的 Unlock |
| [`try_lock`](try_lock.md) | STL 兼容的 TryLock |
| [`lock`](lock-stl.md) | STL 兼容的 Lock |
| [`unlock`](unlock-stl.md) | STL 兼容的 Unlock |
| [`try_lock`](try-lock.md) | STL 兼容的 TryLock |
## STL 兼容方法
@@ -52,5 +52,5 @@ void SafeIncrement() {
## 相关文档
- [Mutex](../mutex/mutex.md) - 互斥锁
- [ReadWriteLock](../readwritelock/readwritelock.md) - 读写锁
- [ReadWriteLock](../read-write-lock/read-write-lock.md) - 读写锁
- [../threading.md](../threading.md) - 模块总览

View File

@@ -0,0 +1,34 @@
# TaskGroup::TaskGroup
```cpp
TaskGroup();
```
默认构造函数。构造一个空的任务组。
**参数:**
**返回:**
**线程安全:**
**复杂度:** O(1)
**注意:**
- 构造后的任务组不包含任何任务。
- 任务组创建后需要通过 TaskSystem::CreateTaskGroup() 实际创建。
**示例:**
```cpp
#include "XCEngine/Threading/TaskGroup.h"
XCEngine::Threading::TaskGroup group;
// 使用 group 添加任务...
```
## 相关文档
- [`TaskGroup`](task-group.md) - 返回类总览
- [`AddTask`](add-task.md) - 添加任务
- [`Wait`](wait.md) - 等待任务完成

View File

@@ -0,0 +1,40 @@
# TaskGroup::~TaskGroup
```cpp
~TaskGroup();
```
析构函数。销毁任务组。
**参数:**
**返回:**
**线程安全:** ⚠️
**复杂度:** O(n)n 为任务组中的任务数
**注意:**
- 析构前会等待所有任务完成。
- 如果有任务正在执行,会等待其完成。
- 未执行的任务将被取消。
**示例:**
```cpp
#include "XCEngine/Threading/TaskGroup.h"
{
XCEngine::Threading::TaskGroup group;
group.AddTask([]() {
// 执行任务...
});
group.Wait();
} // group 在这里销毁
```
## 相关文档
- [`TaskGroup`](task-group.md) - 返回类总览
- [`Cancel`](cancel.md) - 取消任务
- [`Wait`](wait.md) - 等待任务完成

View File

@@ -24,8 +24,8 @@ TaskGroup 用于将多个相关任务组织在一起,统一管理它们的执
| 方法 | 描述 |
|------|------|
| [`Task()`](task-group.md) | 默认构造函数 |
| [`~Task()`](task-group.md) | 析构函数 |
| [`Task()`](constructor.md) | 默认构造函数 |
| [`~Task()`](destructor.md) | 析构函数 |
| [`AddTask`](add-task.md) | 添加任务到组(支持 ITask 或 Callback |
| [`AddDependency`](add-dependency.md) | 添加任务依赖关系 |
| [`Wait`](wait.md) | 阻塞等待所有任务完成 |

View File

@@ -20,7 +20,7 @@ Threading 模块提供了一套完整的多线程编程工具,包括线程封
|------|------|------|
| [Mutex](mutex/mutex.md) | `Mutex.h` | 互斥锁 |
| [SpinLock](spinlock/spinlock.md) | `SpinLock.h` | 自旋锁 |
| [ReadWriteLock](readwritelock/readwritelock.md) | `ReadWriteLock.h` | 读写锁 |
| [ReadWriteLock](read-write-lock/read-write-lock.md) | `ReadWriteLock.h` | 读写锁 |
### 线程
@@ -33,10 +33,10 @@ Threading 模块提供了一套完整的多线程编程工具,包括线程封
| 组件 | 文件 | 描述 |
|------|------|------|
| [ITask](task/task.md) | `Task.h` | 任务基类 |
| [LambdaTask](lambdatask/lambdatask.md) | `LambdaTask.h` | Lambda 任务封装模板 |
| [LambdaTask](lambda-task/lambda-task.md) | `LambdaTask.h` | Lambda 任务封装模板 |
| [TaskGroup](task-group/task-group.md) | `TaskGroup.h` | 任务组 |
| [TaskSystem](task-system/task-system.md) | `TaskSystem.h` | 并行任务调度系统 |
| [TaskSystemConfig](tasksystemconfig/tasksystemconfig.md) | `TaskSystemConfig.h` | 任务系统配置 |
| [TaskSystemConfig](task-system-config/task-system-config.md) | `TaskSystemConfig.h` | 任务系统配置 |
## 同步原语对比

View File

@@ -85,7 +85,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/RHIEnums.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/RHIFactory.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/RHIDescriptorPool.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/D3D12/D3D12Enum.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/D3D12/D3D12Enums.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/D3D12/D3D12Device.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/D3D12/D3D12CommandQueue.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/D3D12/D3D12CommandAllocator.h
@@ -155,7 +155,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/OpenGL/OpenGLRenderTargetView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/OpenGL/OpenGLDepthStencilView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/OpenGL/OpenGLScreenshot.cpp
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/src/glad.c
${CMAKE_SOURCE_DIR}/tests/opengl/package/src/glad.c
# RHI Factory
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/RHIFactory.cpp
@@ -283,7 +283,7 @@ target_include_directories(XCEngine PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/third_party
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include
${CMAKE_SOURCE_DIR}/tests/opengl/package/include
)
if(MSVC)

View File

@@ -5,7 +5,7 @@
#include <string>
#include "../RHIBuffer.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -8,7 +8,7 @@
#include "../RHICommandList.h"
#include "../RHIEnums.h"
#include "../RHITypes.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -5,7 +5,7 @@
#include "../RHICommandQueue.h"
#include "../RHIEnums.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -4,7 +4,7 @@
#include <wrl/client.h>
#include "../RHIEnums.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -6,7 +6,7 @@
#include "../RHIEnums.h"
#include "../RHITypes.h"
#include "../RHIDescriptorPool.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -9,7 +9,7 @@
#include "../RHIDevice.h"
#include "../RHIEnums.h"
#include "../RHITypes.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -6,7 +6,7 @@
#include "../RHIPipelineState.h"
#include "../RHIEnums.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -4,7 +4,7 @@
#include <wrl/client.h>
#include <cstdint>
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -4,7 +4,7 @@
#include <wrl/client.h>
#include "../RHIEnums.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -5,7 +5,7 @@
#include <vector>
#include "../RHIEnums.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -4,7 +4,7 @@
#include <wrl/client.h>
#include "../RHISampler.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -6,7 +6,7 @@
#include <string>
#include "../RHIShader.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
#include "../RHITypes.h"
using Microsoft::WRL::ComPtr;

View File

@@ -4,7 +4,7 @@
#include <wrl/client.h>
#include "../RHIEnums.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -6,7 +6,7 @@
#include <vector>
#include "../RHISwapChain.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
#include "D3D12Texture.h"
using Microsoft::WRL::ComPtr;

View File

@@ -5,7 +5,7 @@
#include <string>
#include "../RHITexture.h"
#include "D3D12Enum.h"
#include "D3D12Enums.h"
using Microsoft::WRL::ComPtr;

View File

@@ -1,5 +1,5 @@
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
#include "XCEngine/RHI/D3D12/D3D12Enum.h"
#include "XCEngine/RHI/D3D12/D3D12Enums.h"
namespace XCEngine {
namespace RHI {

View File

@@ -217,7 +217,9 @@ RHIBuffer* D3D12Device::CreateBuffer(const BufferDesc& desc) {
RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc) {
auto* texture = new D3D12Texture();
D3D12_RESOURCE_DESC d3d12Desc = {};
d3d12Desc.Dimension = static_cast<D3D12_RESOURCE_DIMENSION>(desc.textureType);
d3d12Desc.Dimension = ToD3D12(static_cast<TextureType>(desc.textureType));
d3d12Desc.Width = desc.width;
d3d12Desc.Height = desc.height;
d3d12Desc.DepthOrArraySize = desc.depth;

BIN
minimal.ppm Normal file

Binary file not shown.

View File

@@ -37,13 +37,11 @@ add_subdirectory(containers)
add_subdirectory(memory)
add_subdirectory(threading)
add_subdirectory(debug)
add_subdirectory(Components)
add_subdirectory(Scene)
add_subdirectory(RHI)
add_subdirectory(RHI/D3D12)
add_subdirectory(RHI/OpenGL)
add_subdirectory(Resources)
add_subdirectory(Input)
add_subdirectory(components)
add_subdirectory(scene)
add_subdirectory(rhi)
add_subdirectory(resources)
add_subdirectory(input)
# ============================================================
# Test Summary

View File

@@ -8,22 +8,23 @@ set(COMPONENTS_TEST_SOURCES
test_game_object.cpp
)
add_executable(xcengine_components_tests ${COMPONENTS_TEST_SOURCES})
add_executable(components_tests ${COMPONENTS_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_components_tests PROPERTIES
set_target_properties(components_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_components_tests PRIVATE
target_link_libraries(components_tests PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_components_tests PRIVATE
target_include_directories(components_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
)
add_test(NAME ComponentTests COMMAND xcengine_components_tests)
include(GoogleTest)
gtest_discover_tests(components_tests)

View File

@@ -7,24 +7,25 @@ set(INPUT_TEST_SOURCES
test_windows_input_module.cpp
)
add_executable(xcengine_input_tests ${INPUT_TEST_SOURCES})
add_executable(input_tests ${INPUT_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_input_tests PROPERTIES
set_target_properties(input_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_input_tests
target_link_libraries(input_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_input_tests PRIVATE
target_include_directories(input_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME InputTests COMMAND xcengine_input_tests)
include(GoogleTest)
gtest_discover_tests(input_tests)

View File

@@ -5,41 +5,5 @@ project(RHIEngineTests)
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
test_factory.cpp
)
add_executable(rhi_engine_tests ${TEST_SOURCES})
target_link_libraries(rhi_engine_tests PRIVATE
opengl32
glfw3
d3d12
dxgi
d3dcompiler
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(rhi_engine_tests PRIVATE
${PROJECT_ROOT_DIR}/engine/include
${PROJECT_ROOT_DIR}/engine/src
)
enable_testing()
add_test(NAME RHIEngineTests COMMAND rhi_engine_tests)
add_subdirectory(unit)
add_subdirectory(D3D12)
add_subdirectory(OpenGL)

View File

@@ -1,11 +1,9 @@
cmake_minimum_required(VERSION 3.15)
project(D3D12EngineTests)
project(rhi_d3d12_tests)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(unit)
# Integration tests
add_subdirectory(integration)

View File

@@ -1,11 +1,7 @@
cmake_minimum_required(VERSION 3.15)
project(D3D12_Integration)
find_package(Python3 REQUIRED)
enable_testing()
add_subdirectory(minimal)
add_subdirectory(triangle)
add_subdirectory(quad)

View File

@@ -31,10 +31,10 @@ target_link_libraries(D3D12_Minimal PRIVATE
add_custom_command(TARGET D3D12_Minimal POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/compare_ppm.py
$<TARGET_FILE_DIR:D3D12_Minimal>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/run_integration_test.py
$<TARGET_FILE_DIR:D3D12_Minimal>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -40,10 +40,10 @@ add_custom_command(TARGET D3D12_Quad POST_BUILD
add_custom_command(TARGET D3D12_Quad POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/compare_ppm.py
$<TARGET_FILE_DIR:D3D12_Quad>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/run_integration_test.py
$<TARGET_FILE_DIR:D3D12_Quad>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -38,10 +38,10 @@ add_custom_command(TARGET D3D12_Sphere POST_BUILD
add_custom_command(TARGET D3D12_Sphere POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/compare_ppm.py
$<TARGET_FILE_DIR:D3D12_Sphere>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/run_integration_test.py
$<TARGET_FILE_DIR:D3D12_Sphere>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -37,10 +37,10 @@ add_custom_command(TARGET D3D12_Triangle POST_BUILD
add_custom_command(TARGET D3D12_Triangle POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/compare_ppm.py
$<TARGET_FILE_DIR:D3D12_Triangle>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/D3D12/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/d3d12/integration/run_integration_test.py
$<TARGET_FILE_DIR:D3D12_Triangle>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -21,9 +21,9 @@ set(TEST_SOURCES
test_swap_chain.cpp
)
add_executable(d3d12_engine_tests ${TEST_SOURCES})
add_executable(rhi_d3d12_tests ${TEST_SOURCES})
target_link_libraries(d3d12_engine_tests PRIVATE
target_link_libraries(rhi_d3d12_tests PRIVATE
d3d12
dxgi
d3dcompiler
@@ -32,14 +32,12 @@ target_link_libraries(d3d12_engine_tests PRIVATE
GTest::gtest_main
)
target_include_directories(d3d12_engine_tests PRIVATE
target_include_directories(rhi_d3d12_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${PROJECT_ROOT_DIR}/engine/include
${PROJECT_ROOT_DIR}/engine/src
)
enable_testing()
include(GoogleTest)
gtest_discover_tests(d3d12_engine_tests)
gtest_discover_tests(rhi_d3d12_tests)

View File

@@ -1,4 +1,8 @@
#include "D3D12TestFixture.h"
#include <memory>
namespace XCEngine {
namespace RHI {
void D3D12TestFixture::SetUpTestSuite() {
}
@@ -7,65 +11,67 @@ void D3D12TestFixture::TearDownTestSuite() {
}
void D3D12TestFixture::SetUp() {
HRESULT hr = D3D12CreateDevice(
nullptr,
D3D_FEATURE_LEVEL_12_0,
IID_PPV_ARGS(&mDevice)
);
m_device = std::make_unique<D3D12Device>();
if (FAILED(hr)) {
GTEST_SKIP() << "Failed to create D3D12 device";
RHIDeviceDesc desc;
desc.enableDebugLayer = false;
desc.enableGPUValidation = false;
if (!m_device->Initialize(desc)) {
GTEST_SKIP() << "Failed to initialize D3D12Device";
return;
}
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
hr = mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue));
if (FAILED(hr)) {
m_commandQueue = std::make_unique<D3D12CommandQueue>();
if (!m_commandQueue->Initialize(m_device->GetDevice(), CommandQueueType::Direct)) {
GTEST_SKIP() << "Failed to create command queue";
return;
}
hr = mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator));
if (FAILED(hr)) {
m_commandAllocator = std::make_unique<D3D12CommandAllocator>();
if (!m_commandAllocator->Initialize(m_device->GetDevice(), CommandQueueType::Direct)) {
GTEST_SKIP() << "Failed to create command allocator";
return;
}
hr = mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList));
if (FAILED(hr)) {
m_commandList = std::make_unique<D3D12CommandList>();
if (!m_commandList->Initialize(m_device->GetDevice(), CommandQueueType::Direct, m_commandAllocator->GetCommandAllocator())) {
GTEST_SKIP() << "Failed to create command list";
return;
}
}
void D3D12TestFixture::TearDown() {
if (mCommandQueue) {
if (m_commandQueue) {
WaitForGPU();
}
mCommandList.Reset();
mCommandAllocator.Reset();
mCommandQueue.Reset();
mDevice.Reset();
if (m_commandList) {
m_commandList->Shutdown();
m_commandList.reset();
}
if (m_commandAllocator) {
m_commandAllocator->Shutdown();
m_commandAllocator.reset();
}
if (m_commandQueue) {
m_commandQueue->Shutdown();
m_commandQueue.reset();
}
if (m_device) {
m_device->Shutdown();
m_device.reset();
}
}
void D3D12TestFixture::WaitForGPU() {
if (!mCommandQueue || !mDevice) return;
if (!m_commandQueue) return;
ComPtr<ID3D12Fence> fence;
UINT64 fenceValue = 1;
auto fence = std::make_unique<D3D12Fence>();
if (fence->Initialize(m_device->GetDevice(), 0)) {
m_commandQueue->Signal(fence.get(), 1);
fence->Wait(1);
}
}
HRESULT hr = mDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
if (SUCCEEDED(hr)) {
mCommandQueue->Signal(fence.Get(), fenceValue);
if (fence->GetCompletedValue() < fenceValue) {
HANDLE eventHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (eventHandle) {
fence->SetEventOnCompletion(fenceValue, eventHandle);
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
}
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -1,8 +1,6 @@
#pragma once
#include <gtest/gtest.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <wrl/client.h>
#include "XCEngine/RHI/D3D12/D3D12Device.h"
@@ -11,7 +9,8 @@
#include "XCEngine/RHI/D3D12/D3D12CommandList.h"
#include "XCEngine/RHI/D3D12/D3D12Fence.h"
using namespace Microsoft::WRL;
namespace XCEngine {
namespace RHI {
class D3D12TestFixture : public ::testing::Test {
protected:
@@ -21,16 +20,19 @@ protected:
void SetUp() override;
void TearDown() override;
ID3D12Device* GetDevice() { return mDevice.Get(); }
ID3D12CommandQueue* GetCommandQueue() { return mCommandQueue.Get(); }
ID3D12GraphicsCommandList* GetCommandList() { return mCommandList.Get(); }
ID3D12CommandAllocator* GetCommandAllocator() { return mCommandAllocator.Get(); }
D3D12Device* GetDevice() { return m_device.get(); }
D3D12CommandQueue* GetCommandQueue() { return m_commandQueue.get(); }
D3D12CommandList* GetCommandList() { return m_commandList.get(); }
D3D12CommandAllocator* GetCommandAllocator() { return m_commandAllocator.get(); }
void WaitForGPU();
private:
ComPtr<ID3D12Device> mDevice;
ComPtr<ID3D12CommandQueue> mCommandQueue;
ComPtr<ID3D12CommandAllocator> mCommandAllocator;
ComPtr<ID3D12GraphicsCommandList> mCommandList;
std::unique_ptr<D3D12Device> m_device;
std::unique_ptr<D3D12CommandQueue> m_commandQueue;
std::unique_ptr<D3D12CommandAllocator> m_commandAllocator;
std::unique_ptr<D3D12CommandList> m_commandList;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -1,146 +1,78 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12Buffer.h"
#include "XCEngine/RHI/RHIEnums.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, Buffer_Create_DefaultHeap) {
const uint64_t bufferSize = 1024;
BufferDesc desc = {};
desc.size = 1024;
desc.stride = 0;
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDesc.Width = bufferSize;
resourceDesc.Height = 1;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
auto* d3d12Buffer = static_cast<D3D12Buffer*>(buffer);
EXPECT_EQ(d3d12Buffer->GetSize(), 1024);
D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON;
ComPtr<ID3D12Resource> buffer;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
initialState,
nullptr,
IID_PPV_ARGS(&buffer)
);
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(buffer.Get(), nullptr);
D3D12_RESOURCE_DESC desc = buffer->GetDesc();
EXPECT_EQ(desc.Dimension, D3D12_RESOURCE_DIMENSION_BUFFER);
EXPECT_EQ(desc.Width, bufferSize);
buffer->Shutdown();
delete buffer;
}
TEST_F(D3D12TestFixture, Buffer_Create_UploadHeap) {
const uint64_t bufferSize = 2048;
BufferDesc desc = {};
desc.size = 2048;
desc.stride = 0;
desc.bufferType = static_cast<uint32_t>(BufferType::Constant);
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDesc.Width = bufferSize;
resourceDesc.Height = 1;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ComPtr<ID3D12Resource> buffer;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&buffer)
);
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(buffer.Get(), nullptr);
buffer->Shutdown();
delete buffer;
}
TEST_F(D3D12TestFixture, Buffer_Get_GPUVirtualAddress) {
const uint64_t bufferSize = 512;
BufferDesc desc = {};
desc.size = 512;
desc.stride = 0;
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDesc.Width = bufferSize;
resourceDesc.Height = 1;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
auto* d3d12Buffer = static_cast<D3D12Buffer*>(buffer);
EXPECT_NE(d3d12Buffer->GetGPUAddress(), 0);
ComPtr<ID3D12Resource> buffer;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&buffer)
);
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = buffer->GetGPUVirtualAddress();
EXPECT_NE(gpuAddress, 0);
buffer->Shutdown();
delete buffer;
}
TEST_F(D3D12TestFixture, Buffer_Map_Unmap) {
const uint64_t bufferSize = 256;
BufferDesc desc = {};
desc.size = 256;
desc.stride = 0;
desc.bufferType = static_cast<uint32_t>(BufferType::Constant);
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDesc.Width = bufferSize;
resourceDesc.Height = 1;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ComPtr<ID3D12Resource> buffer;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&buffer)
);
ASSERT_HRESULT_SUCCEEDED(hr);
void* mappedData = nullptr;
hr = buffer->Map(0, nullptr, &mappedData);
ASSERT_HRESULT_SUCCEEDED(hr);
void* mappedData = buffer->Map();
ASSERT_NE(mappedData, nullptr);
memset(mappedData, 0xAB, static_cast<size_t>(bufferSize));
memset(mappedData, 0xAB, 256);
buffer->Unmap(0, nullptr);
buffer->Unmap();
buffer->Shutdown();
delete buffer;
}
TEST_F(D3D12TestFixture, Buffer_Get_AlignmentRequirements) {
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = 256;
cbvDesc.SizeInBytes = 256;
EXPECT_GE(cbvDesc.SizeInBytes, 256);
EXPECT_EQ(cbvDesc.BufferLocation % 256, 0);
EXPECT_GE(256 % 256, 0);
}

View File

@@ -1,41 +1,35 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, CommandAllocator_Reset_Basic) {
ComPtr<ID3D12CommandAllocator> allocator;
HRESULT hr = GetDevice()->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&allocator)
);
ASSERT_HRESULT_SUCCEEDED(hr);
auto allocator = std::make_unique<D3D12CommandAllocator>();
ASSERT_TRUE(allocator->Initialize(GetDevice()->GetDevice(), CommandQueueType::Direct));
hr = allocator->Reset();
ASSERT_HRESULT_SUCCEEDED(hr);
allocator->Reset();
EXPECT_TRUE(allocator->IsReady());
}
TEST_F(D3D12TestFixture, CommandAllocator_Reset_Multiple) {
ComPtr<ID3D12CommandAllocator> allocator;
HRESULT hr = GetDevice()->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&allocator)
);
ASSERT_HRESULT_SUCCEEDED(hr);
auto allocator = std::make_unique<D3D12CommandAllocator>();
ASSERT_TRUE(allocator->Initialize(GetDevice()->GetDevice(), CommandQueueType::Direct));
for (int i = 0; i < 10; ++i) {
hr = allocator->Reset();
ASSERT_HRESULT_SUCCEEDED(hr);
allocator->Reset();
EXPECT_TRUE(allocator->IsReady());
}
}
TEST_F(D3D12TestFixture, CommandAllocator_Create_DifferentTypes) {
D3D12_COMMAND_LIST_TYPE types[] = {
D3D12_COMMAND_LIST_TYPE_DIRECT,
D3D12_COMMAND_LIST_TYPE_COMPUTE,
D3D12_COMMAND_LIST_TYPE_COPY
CommandQueueType types[] = {
CommandQueueType::Direct,
CommandQueueType::Compute,
CommandQueueType::Copy
};
for (auto type : types) {
ComPtr<ID3D12CommandAllocator> allocator;
HRESULT hr = GetDevice()->CreateCommandAllocator(type, IID_PPV_ARGS(&allocator));
ASSERT_HRESULT_SUCCEEDED(hr);
auto allocator = std::make_unique<D3D12CommandAllocator>();
ASSERT_TRUE(allocator->Initialize(GetDevice()->GetDevice(), type));
}
}

View File

@@ -1,11 +1,14 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12Enums.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, CommandList_Close_Basic) {
HRESULT hr = GetCommandList()->Close();
ASSERT_HRESULT_SUCCEEDED(hr);
GetCommandList()->Reset();
GetCommandList()->Close();
}
TEST_F(D3D12TestFixture, CommandList_Get_Desc) {
D3D12_COMMAND_LIST_TYPE type = GetCommandList()->GetType();
auto type = GetCommandList()->GetCommandList()->GetType();
EXPECT_EQ(type, D3D12_COMMAND_LIST_TYPE_DIRECT);
}

View File

@@ -1,22 +1,17 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12CommandQueue.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, CommandQueue_Get_TimestampFrequency) {
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
ComPtr<ID3D12CommandQueue> queue;
HRESULT hr = GetDevice()->CreateCommandQueue(&desc, IID_PPV_ARGS(&queue));
ASSERT_HRESULT_SUCCEEDED(hr);
UINT64 frequency;
hr = queue->GetTimestampFrequency(&frequency);
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(GetCommandQueue(), nullptr);
UINT64 frequency = GetCommandQueue()->GetTimestampFrequency();
EXPECT_GT(frequency, 0);
}
TEST_F(D3D12TestFixture, CommandQueue_Execute_EmptyCommandLists) {
GetCommandList()->Reset(GetCommandAllocator(), nullptr);
ID3D12CommandList* commandLists[] = { GetCommandList() };
GetCommandList()->Reset();
void* commandLists[] = { GetCommandList()->GetCommandList() };
GetCommandQueue()->ExecuteCommandLists(1, commandLists);
GetCommandList()->Close();
WaitForGPU();

View File

@@ -5,7 +5,7 @@ using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_Initialize_RTV) {
D3D12DescriptorHeap heap;
bool result = heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4);
bool result = heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 4);
ASSERT_TRUE(result);
EXPECT_EQ(heap.GetDescriptorCount(), 4u);
EXPECT_EQ(heap.GetType(), DescriptorHeapType::RTV);
@@ -14,7 +14,7 @@ TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_Initialize_RTV) {
TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetCPUDescriptorHandle) {
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4));
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 4));
uint32_t descriptorSize = heap.GetDescriptorSize();
CPUDescriptorHandle handle0 = heap.GetCPUDescriptorHandle(0);
@@ -29,7 +29,7 @@ TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetCPUDescriptorHandle) {
TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetGPUDescriptorHandle) {
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 4, true));
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 4, true));
uint32_t descriptorSize = heap.GetDescriptorSize();
GPUDescriptorHandle handle0 = heap.GetGPUDescriptorHandle(0);
@@ -42,83 +42,57 @@ TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetGPUDescriptorHandle) {
TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetDescriptorSize) {
D3D12DescriptorHeap rtvHeap;
ASSERT_TRUE(rtvHeap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4));
ASSERT_TRUE(rtvHeap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 4));
EXPECT_GT(rtvHeap.GetDescriptorSize(), 0u);
D3D12DescriptorHeap cbvHeap;
ASSERT_TRUE(cbvHeap.Initialize(GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 4));
ASSERT_TRUE(cbvHeap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 4));
EXPECT_GT(cbvHeap.GetDescriptorSize(), 0u);
}
TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_Shutdown) {
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 2));
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 2));
heap.Shutdown();
}
TEST_F(D3D12TestFixture, DescriptorHeap_Create_CBV_SRV_UAV) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
heapDesc.NumDescriptors = 10;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(descriptorHeap.Get(), nullptr);
D3D12_DESCRIPTOR_HEAP_DESC desc = descriptorHeap->GetDesc();
EXPECT_EQ(desc.Type, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
EXPECT_EQ(desc.NumDescriptors, 10);
D3D12DescriptorHeap heap;
bool result = heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 10, true);
ASSERT_TRUE(result);
EXPECT_EQ(heap.GetDescriptorCount(), 10u);
heap.Shutdown();
}
TEST_F(D3D12TestFixture, DescriptorHeap_Create_Sampler) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
heapDesc.NumDescriptors = 5;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_DESCRIPTOR_HEAP_DESC desc = descriptorHeap->GetDesc();
EXPECT_EQ(desc.Type, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
D3D12DescriptorHeap heap;
bool result = heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::Sampler, 5, true);
ASSERT_TRUE(result);
EXPECT_EQ(heap.GetType(), DescriptorHeapType::Sampler);
heap.Shutdown();
}
TEST_F(D3D12TestFixture, DescriptorHeap_Create_RTV) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heapDesc.NumDescriptors = 4;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_DESCRIPTOR_HEAP_DESC desc = descriptorHeap->GetDesc();
EXPECT_EQ(desc.Type, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12DescriptorHeap heap;
bool result = heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 4, false);
ASSERT_TRUE(result);
EXPECT_EQ(heap.GetType(), DescriptorHeapType::RTV);
heap.Shutdown();
}
TEST_F(D3D12TestFixture, DescriptorHeap_Create_DSV) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
heapDesc.NumDescriptors = 2;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_DESCRIPTOR_HEAP_DESC desc = descriptorHeap->GetDesc();
EXPECT_EQ(desc.Type, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
D3D12DescriptorHeap heap;
bool result = heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::DSV, 2, false);
ASSERT_TRUE(result);
EXPECT_EQ(heap.GetType(), DescriptorHeapType::DSV);
heap.Shutdown();
}
TEST_F(D3D12TestFixture, DescriptorHeap_Get_HandleIncrementSize) {
UINT cbvSrvUavSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
UINT samplerSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
UINT rtvSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
UINT dsvSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
UINT cbvSrvUavSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::CBV_SRV_UAV);
UINT samplerSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::Sampler);
UINT rtvSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::RTV);
UINT dsvSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV);
EXPECT_GT(cbvSrvUavSize, 0);
EXPECT_GT(samplerSize, 0);

View File

@@ -1,38 +1,40 @@
#include "fixtures/D3D12TestFixture.h"
#include <d3d12.h>
#include <dxgi1_4.h>
#include <windows.h>
#include "XCEngine/RHI/RHIEnums.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, Device_Create_Success) {
ASSERT_NE(GetDevice(), nullptr);
ASSERT_NE(GetDevice()->GetDevice(), nullptr);
}
TEST_F(D3D12TestFixture, Device_Get_CommandQueue) {
ASSERT_NE(GetCommandQueue(), nullptr);
ASSERT_NE(GetCommandQueue()->GetCommandQueue(), nullptr);
}
TEST_F(D3D12TestFixture, Device_Get_FeatureLevel) {
static const D3D_FEATURE_LEVEL requestedLevels[] = { D3D_FEATURE_LEVEL_12_0 };
D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevels = {};
static const D3D_FEATURE_LEVEL requestedLevels[] = { D3D_FEATURE_LEVEL_12_0 };
featureLevels.NumFeatureLevels = 1;
featureLevels.pFeatureLevelsRequested = requestedLevels;
HRESULT hr = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featureLevels, sizeof(featureLevels));
ASSERT_HRESULT_SUCCEEDED(hr);
bool result = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featureLevels, sizeof(featureLevels));
ASSERT_TRUE(result);
EXPECT_EQ(featureLevels.MaxSupportedFeatureLevel, D3D_FEATURE_LEVEL_12_0);
}
TEST_F(D3D12TestFixture, Device_Get_DescriptorHandleIncrementSize) {
UINT cbvSrvUavSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
UINT cbvSrvUavSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::CBV_SRV_UAV);
EXPECT_GT(cbvSrvUavSize, 0);
UINT samplerSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
UINT samplerSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::Sampler);
EXPECT_GT(samplerSize, 0);
UINT rtvSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
UINT rtvSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::RTV);
EXPECT_GT(rtvSize, 0);
UINT dsvSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
UINT dsvSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV);
EXPECT_GT(dsvSize, 0);
}
@@ -40,23 +42,23 @@ TEST_F(D3D12TestFixture, Device_Get_ShaderModelSupport) {
D3D12_FEATURE_DATA_SHADER_MODEL shaderModel = {};
shaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_0;
HRESULT hr = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel));
ASSERT_HRESULT_SUCCEEDED(hr);
bool result = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel));
ASSERT_TRUE(result);
EXPECT_GE(shaderModel.HighestShaderModel, D3D_SHADER_MODEL_6_0);
}
TEST_F(D3D12TestFixture, Device_Get_ResourceBindingTier) {
D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
HRESULT hr = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
ASSERT_HRESULT_SUCCEEDED(hr);
bool result = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
ASSERT_TRUE(result);
EXPECT_GE(options.ResourceBindingTier, D3D12_RESOURCE_BINDING_TIER_1);
}
TEST_F(D3D12TestFixture, Device_Get_TiledResourcesTier) {
D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
HRESULT hr = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
ASSERT_HRESULT_SUCCEEDED(hr);
bool result = GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
ASSERT_TRUE(result);
EXPECT_GE(options.TiledResourcesTier, D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED);
}

View File

@@ -1,60 +1,70 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12Fence.h"
#include <Windows.h>
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, Fence_Create_Success) {
ASSERT_NE(GetDevice(), nullptr);
ComPtr<ID3D12Fence> fence;
HRESULT hr = GetDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(fence.Get(), nullptr);
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
fence->Shutdown();
delete fence;
}
TEST_F(D3D12TestFixture, Fence_Get_CompletedValue_Initial) {
ComPtr<ID3D12Fence> fence;
HRESULT hr = GetDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
ASSERT_HRESULT_SUCCEEDED(hr);
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
EXPECT_EQ(fence->GetCompletedValue(), 0);
fence->Shutdown();
delete fence;
}
TEST_F(D3D12TestFixture, Fence_Signal_Wait) {
ComPtr<ID3D12Fence> fence;
HRESULT hr = GetDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
ASSERT_HRESULT_SUCCEEDED(hr);
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
const UINT64 fenceValue = 100;
hr = GetCommandQueue()->Signal(fence.Get(), fenceValue);
ASSERT_HRESULT_SUCCEEDED(hr);
HANDLE eventHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
ASSERT_NE(eventHandle, nullptr);
hr = fence->SetEventOnCompletion(fenceValue, eventHandle);
ASSERT_HRESULT_SUCCEEDED(hr);
DWORD waitResult = WaitForSingleObject(eventHandle, 1000);
EXPECT_EQ(waitResult, WAIT_OBJECT_0);
GetCommandQueue()->Signal(fence, fenceValue);
fence->Wait(fenceValue);
EXPECT_EQ(fence->GetCompletedValue(), fenceValue);
CloseHandle(eventHandle);
fence->Shutdown();
delete fence;
}
TEST_F(D3D12TestFixture, Fence_Set_EventOnCompletion) {
ComPtr<ID3D12Fence> fence;
HRESULT hr = GetDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
ASSERT_HRESULT_SUCCEEDED(hr);
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
auto* fence = new D3D12Fence();
ASSERT_TRUE(fence->Initialize(GetDevice()->GetDevice(), 200));
const UINT64 fenceValue = 200;
hr = GetCommandQueue()->Signal(fence.Get(), fenceValue);
ASSERT_HRESULT_SUCCEEDED(hr);
GetCommandQueue()->Signal(fence, fenceValue);
HANDLE eventHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
ASSERT_NE(eventHandle, nullptr);
hr = fence->SetEventOnCompletion(fenceValue, eventHandle);
ASSERT_HRESULT_SUCCEEDED(hr);
fence->GetFence()->SetEventOnCompletion(fenceValue, eventHandle);
DWORD waitResult = WaitForSingleObject(eventHandle, 1000);
EXPECT_EQ(waitResult, WAIT_OBJECT_0);
@@ -62,31 +72,32 @@ TEST_F(D3D12TestFixture, Fence_Set_EventOnCompletion) {
EXPECT_EQ(fence->GetCompletedValue(), fenceValue);
CloseHandle(eventHandle);
fence->Shutdown();
delete fence;
}
TEST_F(D3D12TestFixture, Fence_Signal_Multiple) {
ComPtr<ID3D12Fence> fence;
HRESULT hr = GetDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
ASSERT_HRESULT_SUCCEEDED(hr);
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
const UINT64 value1 = 1;
const UINT64 value2 = 2;
const UINT64 value3 = 3;
hr = GetCommandQueue()->Signal(fence.Get(), value1);
ASSERT_HRESULT_SUCCEEDED(hr);
GetCommandQueue()->Signal(fence, value1);
GetCommandQueue()->Signal(fence, value2);
GetCommandQueue()->Signal(fence, value3);
hr = GetCommandQueue()->Signal(fence.Get(), value2);
ASSERT_HRESULT_SUCCEEDED(hr);
hr = GetCommandQueue()->Signal(fence.Get(), value3);
ASSERT_HRESULT_SUCCEEDED(hr);
auto* d3d12fence = static_cast<D3D12Fence*>(fence);
HANDLE eventHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
ASSERT_NE(eventHandle, nullptr);
hr = fence->SetEventOnCompletion(value3, eventHandle);
ASSERT_HRESULT_SUCCEEDED(hr);
d3d12fence->GetFence()->SetEventOnCompletion(value3, eventHandle);
DWORD waitResult = WaitForSingleObject(eventHandle, 1000);
EXPECT_EQ(waitResult, WAIT_OBJECT_0);
@@ -94,4 +105,6 @@ TEST_F(D3D12TestFixture, Fence_Signal_Multiple) {
EXPECT_EQ(fence->GetCompletedValue(), value3);
CloseHandle(eventHandle);
fence->Shutdown();
delete fence;
}

View File

@@ -1,4 +1,7 @@
#include "fixtures/D3D12TestFixture.h"
#include <cstring>
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, PipelineState_Get_GraphicsPipelineDescDefaults) {
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};

View File

@@ -1,4 +1,7 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12RootSignature.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, RootSignature_Create_EmptyRootSignature) {
D3D12_ROOT_SIGNATURE_DESC rootSigDesc = {};
@@ -6,19 +9,13 @@ TEST_F(D3D12TestFixture, RootSignature_Create_EmptyRootSignature) {
rootSigDesc.NumStaticSamplers = 0;
rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
ComPtr<ID3DBlob> blob;
ComPtr<ID3DBlob> errorBlob;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &errorBlob);
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(blob.Get(), nullptr);
D3D12RootSignature rootSig;
bool result = rootSig.Initialize(GetDevice()->GetDevice(), rootSigDesc);
ASSERT_TRUE(result);
}
TEST_F(D3D12TestFixture, RootSignature_Create_WithCBV) {
D3D12_ROOT_PARAMETER param = {};
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
param.Descriptor.ShaderRegister = 0;
param.Descriptor.RegisterSpace = 0;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
D3D12_ROOT_PARAMETER param = D3D12RootSignature::CreateCBV(0);
D3D12_ROOT_SIGNATURE_DESC rootSigDesc = {};
rootSigDesc.NumParameters = 1;
@@ -26,13 +23,8 @@ TEST_F(D3D12TestFixture, RootSignature_Create_WithCBV) {
rootSigDesc.NumStaticSamplers = 0;
rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
ComPtr<ID3DBlob> blob;
ComPtr<ID3DBlob> errorBlob;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &errorBlob);
ASSERT_HRESULT_SUCCEEDED(hr);
ComPtr<ID3D12RootSignature> rootSig;
hr = GetDevice()->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&rootSig));
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(rootSig.Get(), nullptr);
D3D12RootSignature rootSig;
bool result = rootSig.Initialize(GetDevice()->GetDevice(), rootSigDesc);
ASSERT_TRUE(result);
ASSERT_NE(rootSig.GetRootSignature(), nullptr);
}

View File

@@ -1,5 +1,7 @@
#include "fixtures/D3D12TestFixture.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, Shader_Get_VertexShaderProfile) {
const char* profile = "vs_6_0";
EXPECT_STREQ(profile, "vs_6_0");

View File

@@ -1,140 +1,93 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/RHIEnums.h"
#include "XCEngine/RHI/RHITexture.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, Texture_Create_2D) {
const uint32_t width = 256;
const uint32_t height = 256;
const DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
TextureDesc desc = {};
desc.width = 256;
desc.height = 256;
desc.depth = 1;
desc.mipLevels = 1;
desc.arraySize = 1;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
desc.sampleCount = 1;
desc.sampleQuality = 0;
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
resourceDesc.Width = width;
resourceDesc.Height = height;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = format;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.SampleDesc.Quality = 0;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
EXPECT_EQ(texture->GetWidth(), 256);
EXPECT_EQ(texture->GetHeight(), 256);
EXPECT_EQ(texture->GetFormat(), Format::R8G8B8A8_UNorm);
ComPtr<ID3D12Resource> texture;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&texture)
);
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(texture.Get(), nullptr);
D3D12_RESOURCE_DESC desc = texture->GetDesc();
EXPECT_EQ(desc.Dimension, D3D12_RESOURCE_DIMENSION_TEXTURE2D);
EXPECT_EQ(desc.Width, width);
EXPECT_EQ(desc.Height, height);
EXPECT_EQ(desc.Format, format);
texture->Shutdown();
delete texture;
}
TEST_F(D3D12TestFixture, Texture_Create_3D) {
const uint32_t width = 64;
const uint32_t height = 64;
const uint32_t depth = 64;
TextureDesc desc = {};
desc.width = 64;
desc.height = 64;
desc.depth = 64;
desc.mipLevels = 1;
desc.arraySize = 1;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture3D);
desc.sampleCount = 1;
desc.sampleQuality = 0;
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
resourceDesc.Width = width;
resourceDesc.Height = height;
resourceDesc.DepthOrArraySize = depth;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
EXPECT_EQ(texture->GetDepth(), 64);
ComPtr<ID3D12Resource> texture;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&texture)
);
ASSERT_HRESULT_SUCCEEDED(hr);
ASSERT_NE(texture.Get(), nullptr);
D3D12_RESOURCE_DESC desc = texture->GetDesc();
EXPECT_EQ(desc.Dimension, D3D12_RESOURCE_DIMENSION_TEXTURE3D);
EXPECT_EQ(desc.DepthOrArraySize, depth);
texture->Shutdown();
delete texture;
}
TEST_F(D3D12TestFixture, Texture_Create_MultipleMips) {
const uint32_t width = 512;
const uint32_t height = 512;
const uint32_t mipLevels = 5;
TextureDesc desc = {};
desc.width = 512;
desc.height = 512;
desc.depth = 1;
desc.mipLevels = 5;
desc.arraySize = 1;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
desc.sampleCount = 1;
desc.sampleQuality = 0;
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
resourceDesc.Width = width;
resourceDesc.Height = height;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = mipLevels;
resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
EXPECT_EQ(texture->GetMipLevels(), 5);
ComPtr<ID3D12Resource> texture;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&texture)
);
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_RESOURCE_DESC desc = texture->GetDesc();
EXPECT_EQ(desc.MipLevels, mipLevels);
texture->Shutdown();
delete texture;
}
TEST_F(D3D12TestFixture, Texture_Create_Array) {
const uint32_t width = 128;
const uint32_t height = 128;
const uint32_t arraySize = 4;
TextureDesc desc = {};
desc.width = 128;
desc.height = 128;
desc.depth = 1;
desc.mipLevels = 1;
desc.arraySize = 4;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2DArray);
desc.sampleCount = 1;
desc.sampleQuality = 0;
desc.flags = 0;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
resourceDesc.Width = width;
resourceDesc.Height = height;
resourceDesc.DepthOrArraySize = arraySize;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
ComPtr<ID3D12Resource> texture;
HRESULT hr = GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&texture)
);
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_RESOURCE_DESC desc = texture->GetDesc();
EXPECT_EQ(desc.DepthOrArraySize, arraySize);
texture->Shutdown();
delete texture;
}

View File

@@ -1,62 +1,40 @@
#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
using namespace XCEngine::RHI;
TEST_F(D3D12TestFixture, Views_Create_RTVDescriptorHeap) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heapDesc.NumDescriptors = 1;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 1, false));
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_CPU_DESCRIPTOR_HANDLE handle = descriptorHeap->GetCPUDescriptorHandleForHeapStart();
CPUDescriptorHandle handle = heap.GetCPUDescriptorHandle(0);
EXPECT_NE(handle.ptr, 0);
}
TEST_F(D3D12TestFixture, Views_Create_DSVDescriptorHeap) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
heapDesc.NumDescriptors = 1;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::DSV, 1, false));
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_CPU_DESCRIPTOR_HANDLE handle = descriptorHeap->GetCPUDescriptorHandleForHeapStart();
CPUDescriptorHandle handle = heap.GetCPUDescriptorHandle(0);
EXPECT_NE(handle.ptr, 0);
}
TEST_F(D3D12TestFixture, Views_Create_CBVDescriptorHeap) {
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
heapDesc.NumDescriptors = 1;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 1, true));
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
D3D12_CPU_DESCRIPTOR_HANDLE handle = descriptorHeap->GetCPUDescriptorHandleForHeapStart();
CPUDescriptorHandle handle = heap.GetCPUDescriptorHandle(0);
EXPECT_NE(handle.ptr, 0);
}
TEST_F(D3D12TestFixture, Views_Get_RTVDescriptorHandleIncrement) {
UINT rtvSize = GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
UINT rtvSize = GetDevice()->GetDescriptorHandleIncrementSize(DescriptorHeapType::RTV);
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heapDesc.NumDescriptors = 4;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
D3D12DescriptorHeap heap;
ASSERT_TRUE(heap.Initialize(GetDevice()->GetDevice(), DescriptorHeapType::RTV, 4, false));
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
HRESULT hr = GetDevice()->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&descriptorHeap));
ASSERT_HRESULT_SUCCEEDED(hr);
CPUDescriptorHandle handle1 = heap.GetCPUDescriptorHandle(0);
CPUDescriptorHandle handle2 = heap.GetCPUDescriptorHandle(1);
D3D12_CPU_DESCRIPTOR_HANDLE handle1 = descriptorHeap->GetCPUDescriptorHandleForHeapStart();
D3D12_CPU_DESCRIPTOR_HANDLE handle2 = handle1;
handle2.ptr += rtvSize;
EXPECT_NE(handle1.ptr, handle2.ptr);
EXPECT_EQ(handle2.ptr - handle1.ptr, rtvSize);
}

View File

@@ -5,7 +5,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
project(OpenGL_Minimal)
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/OpenGL/package)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
add_executable(OpenGL_Minimal
WIN32
@@ -31,10 +31,10 @@ target_compile_definitions(OpenGL_Minimal PRIVATE
add_custom_command(TARGET OpenGL_Minimal POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/OpenGL/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/run_integration_test.py
$<TARGET_FILE_DIR:OpenGL_Minimal>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/OpenGL/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/compare_ppm.py
$<TARGET_FILE_DIR:OpenGL_Minimal>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -5,7 +5,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
project(OpenGL_Quad)
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/OpenGL/package)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
add_executable(OpenGL_Quad
WIN32
@@ -37,10 +37,10 @@ add_custom_command(TARGET OpenGL_Quad POST_BUILD
add_custom_command(TARGET OpenGL_Quad POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/OpenGL/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/run_integration_test.py
$<TARGET_FILE_DIR:OpenGL_Quad>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/OpenGL/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/compare_ppm.py
$<TARGET_FILE_DIR:OpenGL_Quad>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -0,0 +1,57 @@
cmake_minimum_required(VERSION 3.15)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
project(OpenGL_Sphere)
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
add_executable(OpenGL_Sphere
WIN32
main.cpp
${PACKAGE_DIR}/src/glad.c
)
target_include_directories(OpenGL_Sphere PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${ENGINE_ROOT_DIR}/include
${PACKAGE_DIR}/include
)
target_link_libraries(OpenGL_Sphere PRIVATE
opengl32
XCEngine
)
target_compile_definitions(OpenGL_Sphere PRIVATE
UNICODE
_UNICODE
)
add_custom_command(TARGET OpenGL_Sphere POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/Res
$<TARGET_FILE_DIR:OpenGL_Sphere>/Res
)
add_custom_command(TARGET OpenGL_Sphere POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/run_integration_test.py
$<TARGET_FILE_DIR:OpenGL_Sphere>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/compare_ppm.py
$<TARGET_FILE_DIR:OpenGL_Sphere>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
$<TARGET_FILE_DIR:OpenGL_Sphere>/
)
add_test(NAME OpenGL_Sphere_Integration
COMMAND ${Python3_EXECUTABLE} $<TARGET_FILE_DIR:OpenGL_Sphere>/run_integration_test.py
$<TARGET_FILE:OpenGL_Sphere>
sphere.ppm
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
5
WORKING_DIRECTORY $<TARGET_FILE_DIR:OpenGL_Sphere>
)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

View File

@@ -0,0 +1,10 @@
#version 460
in vec2 vTexcoord;
out vec4 fragColor;
uniform sampler2D uTexture;
void main() {
fragColor = texture(uTexture, vTexcoord);
}

View File

@@ -0,0 +1,17 @@
#version 460
layout(location = 0) in vec4 aPosition;
layout(location = 1) in vec2 aTexcoord;
out vec2 vTexcoord;
uniform mat4 gModelMatrix;
uniform mat4 gViewMatrix;
uniform mat4 gProjectionMatrix;
void main() {
vec4 positionWS = gModelMatrix * aPosition;
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexcoord = aTexcoord;
}

View File

@@ -0,0 +1,322 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <stdint.h>
#include <windows.h>
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
#include "XCEngine/RHI/OpenGL/OpenGLSwapChain.h"
#include "XCEngine/RHI/OpenGL/OpenGLCommandList.h"
#include "XCEngine/RHI/OpenGL/OpenGLBuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLVertexArray.h"
#include "XCEngine/RHI/OpenGL/OpenGLShader.h"
#include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h"
#include "XCEngine/RHI/OpenGL/OpenGLTexture.h"
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
#include "XCEngine/RHI/OpenGL/OpenGLScreenshot.h"
#include "XCEngine/Debug/Logger.h"
#include "XCEngine/Debug/ConsoleLogSink.h"
#include "XCEngine/Containers/String.h"
#pragma comment(lib, "opengl32.lib")
using namespace XCEngine::RHI;
using namespace XCEngine::Debug;
using namespace XCEngine::Containers;
static const int gWidth = 1280;
static const int gHeight = 720;
void Log(const char* format, ...) {
char buffer[1024];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
Logger::Get().Debug(LogCategory::Rendering, String(buffer));
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void IdentityMatrix(float* m) {
memset(m, 0, 16 * sizeof(float));
m[0] = m[5] = m[10] = m[15] = 1.0f;
}
void TranslationMatrix(float* m, float x, float y, float z) {
memset(m, 0, 16 * sizeof(float));
m[0] = 1.0f; m[12] = x;
m[5] = 1.0f; m[13] = y;
m[10] = 1.0f; m[14] = z;
m[15] = 1.0f;
}
void PerspectiveMatrix(float* m, float fov, float aspect, float nearZ, float farZ) {
memset(m, 0, 16 * sizeof(float));
float tanHalfFov = tanf(fov / 2.0f);
m[0] = 1.0f / (aspect * tanHalfFov);
m[5] = 1.0f / tanHalfFov;
m[10] = -(farZ + nearZ) / (farZ - nearZ);
m[11] = -1.0f;
m[14] = -(2.0f * farZ * nearZ) / (farZ - nearZ);
m[15] = 0.0f;
}
struct Vertex {
float pos[4];
float texcoord[2];
};
void GenerateSphere(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices, float radius, int segments) {
vertices.clear();
indices.clear();
segments = (segments < 3) ? 3 : segments;
for (int lat = 0; lat <= segments; ++lat) {
float phi = static_cast<float>(3.14159265f) * lat / segments;
float sinPhi = sinf(phi);
float cosPhi = cosf(phi);
for (int lon = 0; lon <= segments; ++lon) {
float theta = static_cast<float>(2 * 3.14159265f) * lon / segments;
float sinTheta = sinf(theta);
float cosTheta = cosf(theta);
Vertex v;
v.pos[0] = radius * sinPhi * cosTheta;
v.pos[1] = radius * cosPhi;
v.pos[2] = radius * sinPhi * sinTheta;
v.pos[3] = 1.0f;
v.texcoord[0] = static_cast<float>(lon) / segments;
v.texcoord[1] = static_cast<float>(lat) / segments;
vertices.push_back(v);
}
}
for (int lat = 0; lat < segments; ++lat) {
for (int lon = 0; lon < segments; ++lon) {
uint32_t topLeft = lat * (segments + 1) + lon;
uint32_t topRight = topLeft + 1;
uint32_t bottomLeft = (lat + 1) * (segments + 1) + lon;
uint32_t bottomRight = bottomLeft + 1;
indices.push_back(topLeft);
indices.push_back(bottomLeft);
indices.push_back(topRight);
indices.push_back(topRight);
indices.push_back(bottomLeft);
indices.push_back(bottomRight);
}
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
Logger::Get().Initialize();
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
Logger::Get().SetMinimumLevel(LogLevel::Debug);
Log("[INFO] OpenGL Sphere Test Starting");
WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"XCEngine_OpenGL_Sphere";
if (!RegisterClassExW(&wc)) {
Log("[ERROR] Failed to register window class");
return -1;
}
RECT rect = { 0, 0, gWidth, gHeight };
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowExW(
0,
L"XCEngine_OpenGL_Sphere",
L"OpenGL Sphere Test",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
NULL, NULL, hInstance, NULL
);
if (!hwnd) {
Log("[ERROR] Failed to create window");
return -1;
}
OpenGLDevice device;
if (!device.InitializeWithExistingWindow(hwnd)) {
Log("[ERROR] Failed to initialize OpenGL device");
return -1;
}
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
Log("[INFO] OpenGL Device: %S", device.GetDeviceInfo().renderer.c_str());
Log("[INFO] OpenGL Version: %S", device.GetDeviceInfo().version.c_str());
OpenGLSwapChain swapChain;
swapChain.Initialize(hwnd, gWidth, gHeight);
OpenGLCommandList commandList;
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
GenerateSphere(vertices, indices, 1.0f, 32);
Log("[INFO] Generated %d vertices, %d indices", (int)vertices.size(), (int)indices.size());
OpenGLBuffer vertexBuffer;
if (!vertexBuffer.InitializeVertexBuffer(vertices.data(), (uint32_t)(sizeof(Vertex) * vertices.size()))) {
Log("[ERROR] Failed to initialize vertex buffer");
return -1;
}
vertexBuffer.SetStride(sizeof(Vertex));
OpenGLBuffer indexBuffer;
if (!indexBuffer.InitializeIndexBuffer(indices.data(), (uint32_t)(sizeof(uint32_t) * indices.size()))) {
Log("[ERROR] Failed to initialize index buffer");
return -1;
}
OpenGLVertexArray vertexArray;
vertexArray.Initialize();
VertexAttribute posAttr = {};
posAttr.index = 0;
posAttr.count = 4;
posAttr.type = VertexAttributeType::Float;
posAttr.normalized = VertexAttributeNormalized::False;
posAttr.stride = sizeof(Vertex);
posAttr.offset = 0;
vertexArray.AddVertexBuffer(vertexBuffer.GetID(), posAttr);
VertexAttribute texAttr = {};
texAttr.index = 1;
texAttr.count = 2;
texAttr.type = VertexAttributeType::Float;
texAttr.normalized = VertexAttributeNormalized::False;
texAttr.stride = sizeof(Vertex);
texAttr.offset = sizeof(float) * 4;
vertexArray.AddVertexBuffer(vertexBuffer.GetID(), texAttr);
vertexArray.SetIndexBuffer(indexBuffer.GetID(), 0);
OpenGLShader shader;
if (!shader.CompileFromFile("Res/Shader/sphere.vert", "Res/Shader/sphere.frag")) {
Log("[ERROR] Failed to compile shaders");
return -1;
}
Log("[INFO] Shaders compiled successfully");
float modelMatrix[16];
float viewMatrix[16];
float projectionMatrix[16];
IdentityMatrix(viewMatrix);
TranslationMatrix(modelMatrix, 0.0f, 0.0f, -5.0f);
float aspect = 1280.0f / 720.0f;
PerspectiveMatrix(projectionMatrix, 45.0f * 3.14159265f / 180.0f, aspect, 0.1f, 1000.0f);
shader.SetMat4("gModelMatrix", modelMatrix);
shader.SetMat4("gViewMatrix", viewMatrix);
shader.SetMat4("gProjectionMatrix", projectionMatrix);
shader.SetInt("uTexture", 0);
OpenGLPipelineState pipelineState;
OpenGLRasterizerState rasterizerState;
rasterizerState.cullFaceEnable = false;
pipelineState.SetRasterizerState(rasterizerState);
OpenGLDepthStencilState depthStencilState;
depthStencilState.depthTestEnable = false;
depthStencilState.depthWriteEnable = false;
pipelineState.SetDepthStencilState(depthStencilState);
ViewportState viewportState = { 0.0f, 0.0f, (float)gWidth, (float)gHeight, 0.0f, 1.0f };
pipelineState.SetViewport(viewportState);
pipelineState.AttachShader(shader.GetID());
pipelineState.Apply();
OpenGLTexture texture;
if (!texture.LoadFromFile("Res/Image/earth.png", true)) {
Log("[ERROR] Failed to load texture");
return -1;
}
Log("[INFO] Texture loaded successfully");
OpenGLSampler sampler;
OpenGLSamplerDesc samplerDesc = {};
samplerDesc.minFilter = SamplerFilter::Linear;
samplerDesc.magFilter = SamplerFilter::Linear;
samplerDesc.wrapS = SamplerWrapMode::ClampToEdge;
samplerDesc.wrapT = SamplerWrapMode::ClampToEdge;
sampler.Initialize(samplerDesc);
MSG msg = {};
int frameCount = 0;
const int targetFrameCount = 30;
while (frameCount < targetFrameCount) {
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
} else {
commandList.SetViewport(0, 0, gWidth, gHeight);
commandList.Clear(0.0f, 0.0f, 1.0f, 1.0f, 1);
pipelineState.Bind();
indexBuffer.Bind();
vertexArray.Bind();
texture.Bind(0);
sampler.Bind(0);
commandList.DrawIndexed(PrimitiveType::Triangles, (uint32_t)indices.size(), 0, 0);
swapChain.Present(0, 0);
frameCount++;
}
}
Log("[INFO] Reached target frame count %d - taking screenshot!", targetFrameCount);
OpenGLScreenshot::Capture(device, swapChain, "sphere.ppm");
sampler.Shutdown();
texture.Shutdown();
indexBuffer.Shutdown();
vertexArray.Shutdown();
vertexBuffer.Shutdown();
shader.Shutdown();
pipelineState.Shutdown();
swapChain.Shutdown();
device.Shutdown();
Logger::Get().Shutdown();
Log("[INFO] OpenGL Sphere Test Finished");
return 0;
}

View File

@@ -5,7 +5,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
project(OpenGL_Triangle)
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/OpenGL/package)
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
add_executable(OpenGL_Triangle
WIN32
@@ -37,10 +37,10 @@ add_custom_command(TARGET OpenGL_Triangle POST_BUILD
add_custom_command(TARGET OpenGL_Triangle POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/OpenGL/integration/run_integration_test.py
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/run_integration_test.py
$<TARGET_FILE_DIR:OpenGL_Triangle>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/OpenGL/integration/compare_ppm.py
${CMAKE_SOURCE_DIR}/tests/rhi/opengl/integration/compare_ppm.py
$<TARGET_FILE_DIR:OpenGL_Triangle>/
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm

View File

@@ -4,14 +4,14 @@ get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../..
find_package(OpenGL REQUIRED)
include_directories(${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include/)
include_directories(${CMAKE_SOURCE_DIR}/tests/opengl/package/include/)
include_directories(${PROJECT_ROOT_DIR}/engine/include)
include_directories(${PROJECT_ROOT_DIR}/engine/src)
find_package(GTest REQUIRED)
set(TEST_SOURCES
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/src/glad.c
${CMAKE_SOURCE_DIR}/tests/opengl/package/src/glad.c
fixtures/OpenGLTestFixture.cpp
test_device.cpp
test_buffer.cpp
@@ -42,15 +42,6 @@ target_include_directories(opengl_engine_tests PRIVATE
${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)
include(GoogleTest)
gtest_discover_tests(opengl_engine_tests)

View File

@@ -40,7 +40,7 @@ void OpenGLTestFixture::SetUp() {
}
ShowWindow(m_hwnd, SW_HIDE);
m_hdc = GetDC(m_hwnd);
m_hdc = ::GetDC(m_hwnd);
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
@@ -88,7 +88,7 @@ void OpenGLTestFixture::SetUp() {
return;
}
if (!gladLoadGLLoader((GLADloadproc)wglGetProcAddress)) {
if (!gladLoadGL()) {
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(m_hglrc);
ReleaseDC(m_hwnd, m_hdc);

View File

@@ -5,7 +5,7 @@ using namespace XCEngine::RHI;
TEST_F(OpenGLTestFixture, SwapChain_Initialize_Window) {
OpenGLSwapChain swapChain;
GLFWwindow* window = GetWindow();
HWND window = GetWindow();
bool result = swapChain.Initialize(window, 800, 600);
@@ -16,7 +16,7 @@ TEST_F(OpenGLTestFixture, SwapChain_Initialize_Window) {
TEST_F(OpenGLTestFixture, SwapChain_Present) {
OpenGLSwapChain swapChain;
GLFWwindow* window = GetWindow();
HWND window = GetWindow();
swapChain.Initialize(window, 800, 600);
swapChain.Present();
@@ -29,7 +29,7 @@ TEST_F(OpenGLTestFixture, SwapChain_Present) {
TEST_F(OpenGLTestFixture, SwapChain_Resize_ChangesSize) {
OpenGLSwapChain swapChain;
GLFWwindow* window = GetWindow();
HWND window = GetWindow();
swapChain.Initialize(window, 800, 600);
swapChain.Resize(1024, 768);

View File

@@ -1,260 +0,0 @@
# RHI 抽象层测试遗留问题报告
## 测试概述
RHI 抽象层单元测试通过 `RHI_BACKEND` 环境变量选择后端D3D12/OpenGL一次编译后可测试两个后端的抽象接口一致性。
```bash
# 运行测试
RHI_BACKEND=D3D12 ./rhi_unit_tests.exe
RHI_BACKEND=OpenGL ./rhi_unit_tests.exe
```
## 测试结果汇总
| 后端 | 通过 | 失败 | 总计 |
|------|------|------|------|
| D3D12 | 21 | 48 | 69 |
| OpenGL | 未测试 | - | - |
---
## 问题清单
### 1. D3D12CommandQueue::CreateCommandQueue 未实现
**严重程度**: 高
**接口**: `RHIDevice::CreateCommandQueue`
**现象**: 返回 `nullptr`
**位置**: `engine/src/RHI/D3D12/D3D12Device.cpp:288`
```cpp
RHICommandQueue* D3D12Device::CreateCommandQueue(const CommandQueueDesc& desc) {
return nullptr; // 未实现
}
```
**影响测试**:
- `CommandQueue_ExecuteCommandLists`
- `CommandQueue_SignalWaitFence`
- `CommandQueue_GetCompletedValue`
- `CommandQueue_WaitForIdle`
- `CommandQueue_GetType`
- `CommandQueue_GetTimestampFrequency`
- `Device_CreateCommandQueue_ReturnsValid`
---
### 2. D3D12CommandList::CreateCommandList 未实现
**严重程度**: 高
**接口**: `RHIDevice::CreateCommandList`
**现象**: 返回 `nullptr`
**位置**: `engine/src/RHI/D3D12/D3D12Device.cpp:300`
```cpp
D3D12CommandList* D3D12Device::CreateCommandListImpl(const CommandListDesc& desc) {
return nullptr; // 未实现
}
```
**影响测试**:
- `CommandList_Reset_Close`
- `CommandList_SetPrimitiveTopology`
- `CommandList_SetViewport`
- `CommandList_SetViewports`
- `CommandList_SetScissorRect`
- `CommandList_Draw`
- `CommandList_DrawIndexed`
- `CommandList_ClearRenderTarget`
- `CommandList_SetDepthStencilState`
- `CommandList_SetBlendState`
- `CommandList_SetStencilRef`
- `CommandList_TransitionBarrier`
- `Device_CreateCommandList_ReturnsValid`
---
### 3. Texture 枚举值不匹配
**严重程度**: 高
**接口**: `RHIDevice::CreateTexture`
**现象**: 返回 `nullptr`
**位置**: `engine/src/RHI/D3D12/D3D12Device.cpp:220`
**原因**: RHI `TextureType` 枚举值与 D3D12 `D3D12_RESOURCE_DIMENSION` 枚举值不匹配
| RHI TextureType | 值 | D3D12 D3D12_RESOURCE_DIMENSION | 值 |
|-----------------|-----|----------------------------------|-----|
| Texture1D | 0 | D3D12_RESOURCE_DIMENSION_BUFFER | 0 |
| Texture2D | 1 | D3D12_RESOURCE_DIMENSION_TEXTURE1D | 1 |
| Texture2DArray | 2 | D3D12_RESOURCE_DIMENSION_TEXTURE2D | 2 |
| Texture3D | 3 | D3D12_RESOURCE_DIMENSION_TEXTURE3D | 3 |
| TextureCube | 4 | D3D12_RESOURCE_DIMENSION_TEXTURECUBE | 4 |
| TextureCubeArray | 5 | (无对应) | - |
**影响测试**:
- `Texture_Create_Texture2D`
- `Texture_Create_Texture3D`
- `Texture_StateManagement`
- `Texture_Naming`
- `Texture_GetNativeHandle`
- `Device_CreateTexture_ReturnsValid`
---
### 4. Shader 编译返回 nullptr
**严重程度**: 中
**接口**: `RHIDevice::CompileShader`
**现象**: 返回 `nullptr`
**可能原因**:
- Shader 编译参数不正确
- 需要有效的 shader 源码或文件路径
**影响测试**:
- `Shader_Compile_FromSource`
- `Shader_GetType`
- `Shader_IsValid`
- `Shader_Bind_Unbind`
- `Shader_SetInt`
- `Shader_SetFloat`
- `Shader_SetVec3`
- `Shader_SetVec4`
- `Shader_SetMat4`
- `Shader_GetNativeHandle`
---
### 5. SwapChain 需要窗口句柄
**严重程度**: 中
**接口**: `RHIDevice::CreateSwapChain`
**现象**: 返回 `nullptr`
**原因**: `RHIDeviceDesc` 需要有效的 `windowHandle`
**影响测试**:
- `SwapChain_Create`
- `SwapChain_GetCurrentBackBufferIndex`
- `SwapChain_GetCurrentBackBuffer`
- `SwapChain_Resize`
- `SwapChain_FullscreenState`
- `SwapChain_ShouldClose`
---
### 6. Buffer::Map 对某些类型返回 nullptr
**严重程度**: 中
**接口**: `RHIBuffer::Map`
**现象**: D3D12 Constant Buffer 类型可能无法 Map
**位置**: `tests/RHI/unit/test_buffer.cpp:16`
```cpp
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc); // desc.bufferType = Vertex
void* data = buffer->Map(); // 返回 nullptr
```
**影响测试**:
- `Buffer_Map_Unmap`
- `Buffer_SetData`
---
### 7. RHICapabilities 未填充
**严重程度**: 低
**接口**: `RHIDevice::GetCapabilities`
**现象**: 所有 capability 值为 0
**位置**: `engine/src/RHI/D3D12/D3D12Device.cpp`
**影响测试**:
- `Device_GetCapabilities_ReturnsValid` - 断言 `caps.maxRenderTargets >= 1`
---
### 8. RHIDeviceInfo 未填充
**严重程度**: 低
**接口**: `RHIDevice::GetDeviceInfo`
**现象**: `vendor``renderer` 字符串为空
**位置**: `engine/src/RHI/D3D12/D3D12Device.cpp:QueryAdapterInfo`
**影响测试**:
- `Device_GetDeviceInfo_ReturnsValid`
---
### 9. Fence::IsSignaled 逻辑问题
**严重程度**: 低
**接口**: `RHIFence::IsSignaled`
**现象**: 初始值应该为 `false`,但返回 `true`
**位置**: `tests/RHI/unit/test_fence.cpp:80`
**影响测试**:
- `Fence_IsSignaled`
---
### 10. Sampler::GetNativeHandle 返回 nullptr
**严重程度**: 低
**接口**: `RHISampler::GetNativeHandle`
**现象**: 返回 nullptr 但 Sampler 创建成功
**位置**: `engine/src/RHI/D3D12/D3D12Sampler.cpp``engine/src/RHI/OpenGL/OpenGLSampler.cpp`
**影响测试**:
- `Sampler_GetNativeHandle`
---
## 优先级建议
### P0 - 必须修复
1. D3D12CommandQueue::CreateCommandQueue 实现
2. D3D12CommandList::CreateCommandList 实现
3. Texture 枚举值对齐
### P1 - 应该修复
4. Shader 编译逻辑
5. SwapChain 窗口支持
### P2 - 可以修复
6. Buffer Map 行为确认
7. Capabilities/DeviceInfo 填充
8. Fence::IsSignaled 逻辑
9. Sampler::GetNativeHandle
---
## 测试文件位置
```
tests/RHI/unit/
├── fixtures/RHITestFixture.h/.cpp # 测试框架
├── test_device.cpp # 设备相关测试
├── test_buffer.cpp # Buffer 相关测试
├── test_texture.cpp # Texture 相关测试
├── test_swap_chain.cpp # SwapChain 相关测试
├── test_command_list.cpp # CommandList 相关测试
├── test_command_queue.cpp # CommandQueue 相关测试
├── test_shader.cpp # Shader 相关测试
├── test_fence.cpp # Fence 相关测试
└── test_sampler.cpp # Sampler 相关测试
```
---
## 运行测试
```bash
# 编译
cmake --build build --config Debug
# D3D12 后端测试
RHI_BACKEND=D3D12 ./build/tests/RHI/unit/Debug/rhi_unit_tests.exe
# OpenGL 后端测试
RHI_BACKEND=OpenGL ./build/tests/RHI/unit/Debug/rhi_unit_tests.exe
```

View File

@@ -34,8 +34,8 @@ target_include_directories(rhi_unit_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${PROJECT_ROOT_DIR}/engine/include
${PROJECT_ROOT_DIR}/engine/src
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include/
${CMAKE_SOURCE_DIR}/tests/opengl/package/include/
)
enable_testing()
add_test(NAME RHIUnitTests COMMAND rhi_unit_tests)
include(GoogleTest)
gtest_discover_tests(rhi_unit_tests)

View File

@@ -28,24 +28,25 @@ set(RESOURCES_TEST_SOURCES
test_resource_path.cpp
)
add_executable(xcengine_resources_tests ${RESOURCES_TEST_SOURCES})
add_executable(resources_tests ${RESOURCES_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_resources_tests PROPERTIES
set_target_properties(resources_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_resources_tests
target_link_libraries(resources_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_resources_tests PRIVATE
target_include_directories(resources_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME ResourcesTests COMMAND xcengine_resources_tests)
include(GoogleTest)
gtest_discover_tests(resources_tests)

View File

@@ -7,22 +7,23 @@ set(SCENE_TEST_SOURCES
test_scene_manager.cpp
)
add_executable(xcengine_scene_tests ${SCENE_TEST_SOURCES})
add_executable(scene_tests ${SCENE_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_scene_tests PROPERTIES
set_target_properties(scene_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_scene_tests PRIVATE
target_link_libraries(scene_tests PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_scene_tests PRIVATE
target_include_directories(scene_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
)
add_test(NAME SceneTests COMMAND xcengine_scene_tests)
include(GoogleTest)
gtest_discover_tests(scene_tests)

View File

@@ -2,239 +2,208 @@
## 1. 测试框架
### 1.1 框架组成
| 组件 | 用途 | 文档位置 |
|------|------|---------|
| Google Test | 单元测试框架 | https://google.github.io/googletest/ |
| CTest | CMake 测试发现和执行 | 内置 CMake 模块 |
| Python | 集成测试辅助 | `scripts/run_tests.py` |
### 1.2 测试分类
| 类型 | 框架 | 特点 |
|------|------|------|
| 单元测试 | Google Test | 快速、独立、无副作用 |
| 集成测试 | CTest + Python | 端到端验证、需要 GUI |
| 组件 | 用途 |
|------|------|
| Google Test | 单元测试框架 |
| CTest | CMake 测试发现和执行 |
---
## 2. 测试目录结构
### 2.1 整体结构
```
tests/
├── CMakeLists.txt # 主 CMake 配置
├── TEST_SPEC.md # 本规范文档
├── run_tests.bat # Windows 测试启动脚本
├── CMakeLists.txt # 主 CMake 配置 (包含 enable_testing)
├── fixtures/ # 共享测试夹具
├── scripts/
│ └── run_tests.py # 统一测试运行脚本
├── math/ # 数学库测试
├── core/ # 核心库测试
├── containers/ # 容器测试
├── memory/ # 内存管理测试
├── threading/ # 线程测试
├── debug/ # 调试系统测试
├── Resources/ # 资源系统测试
── RHI/
├── D3D12/ # D3D12 RHI 测试
├── resources/ # 资源系统测试
── input/ # 输入模块测试
├── scene/ # 场景测试
├── components/ # 组件测试
└── rhi/
├── d3d12/ # D3D12 RHI 测试
│ ├── unit/ # 单元测试
── integration/ # 集成测试
└── OpenGL/ # OpenGL RHI 测试 (暂不维护)
── integration/ # 集成测试
│ └── CMakeLists.txt
└── opengl/ # OpenGL RHI 测试
├── unit/ # 单元测试
├── integration/ # 集成测试
└── CMakeLists.txt
```
### 2.2 模块命名规范
| 模块 | 测试可执行文件 | CTest 名称 |
|------|--------------|-----------|
| math | xcengine_math_tests | MathTests |
| core | xcengine_core_tests | CoreTests |
| containers | xcengine_containers_tests | ContainersTests |
| memory | xcengine_memory_tests | MemoryTests |
| threading | xcengine_threading_tests | ThreadingTests |
| debug | xcengine_debug_tests | DebugTests |
| Resources | xcengine_resources_tests | ResourcesTests |
| RHI/D3D12/unit | d3d12_engine_tests | D3D12EngineTests |
| RHI/D3D12/integration | D3D12_Minimal | D3D12_Minimal_Integration |
---
## 3. 测试命名规范
## 3. 模块命名
### 3.1 单元测试命名
| 模块 | 可执行文件 | CTest 名称前缀 |
|------|----------|---------------|
| math | math_tests | Math_* |
| core | core_tests | Core_* |
| containers | containers_tests | Containers_* |
| memory | memory_tests | MemoryTest_* |
| threading | threading_tests | Threading_* |
| debug | debug_tests | Debug_* |
| resources | resources_tests | Resource*/Texture*/Mesh* 等 |
| input | input_tests | Input*/WindowsInput* |
| scene | scene_tests | Scene*/SceneManager_* |
| components | components_tests | Component_*|TransformComponent_* |
| RHI/D3D12 | rhi_d3d12_tests | D3D12*Fixture/SwapChain*Fixture |
| RHI/OpenGL | opengl_engine_tests | OpenGLTestFixture.* |
---
## 4. 测试用例命名
**格式**: `Component_Category_SubBehavior`
| 部分 | 说明 | 示例 |
|------|------|------|
| Component | 被测组件 | Memory, ResourceHandle, Buffer |
| Component | 被测组件 | Memory, Buffer, Texture |
| Category | 操作类别 | Create, Get, Set, Map, Reset |
| SubBehavior | 具体行为 | DefaultHeap, GPUAddress, ValidPointer |
**示例**:
```
MemoryTest_GetSystemAllocator_ReturnsValidPointer
ResourceHandle_DefaultConstructor
Buffer_Create_DefaultHeap
Texture_Create_2D
Fence_Signal_Wait
```
### 3.2 禁止模式
❌ 不允许:
**禁止模式**:
- `*_Placeholder` - 无意义的占位测试
- 驼峰+下划线混用
- 空操作测试
### 3.3 大小写规则
- 全小写,单词间用下划线分隔
- Component 名称与类名一致
---
## 4. CMakeLists.txt 规范
## 5. CMake 规范
### 4.1 单元测试模板
### 5.1 单元测试模板
```cmake
cmake_minimum_required(VERSION 3.15)
project(XCEngine_ModuleTests)
find_package(GTest REQUIRED)
set(TEST_SOURCES
test_module.cpp
# ... 其他测试文件
)
add_executable(xcengine_module_tests ${TEST_SOURCES})
add_executable(module_tests ${TEST_SOURCES})
# MSVC 运行时库排除
if(MSVC)
set_target_properties(xcengine_module_tests PROPERTIES
set_target_properties(module_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_module_tests PRIVATE
target_link_libraries(module_tests PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_module_tests PRIVATE
target_include_directories(module_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME ModuleTests COMMAND xcengine_module_tests)
include(GoogleTest)
gtest_discover_tests(module_tests)
```
### 4.2 CTest 注册
每个模块的 CMakeLists.txt 必须包含:
### 5.2 RHI 测试 CMakeLists 模板
```cmake
add_test(NAME <TestName> COMMAND <executable>)
```
cmake_minimum_required(VERSION 3.15)
### 4.3 测试资源复制
project(rhi_backend_tests)
如需复制测试资源:
find_package(GTest REQUIRED)
find_package(Python3 REQUIRED) # 集成测试需要
```cmake
add_custom_command(TARGET <test> POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/Res
$<TARGET_FILE_DIR:<test>>/Res
# 单元测试
set(UNIT_TEST_SOURCES
fixtures/TestFixture.cpp
test_*.cpp
)
add_executable(rhi_backend_tests ${UNIT_TEST_SOURCES})
target_link_libraries(rhi_backend_tests PRIVATE
d3d12
dxgi
d3dcompiler
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(rhi_backend_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/engine/src
)
# 集成测试
add_subdirectory(integration)
include(GoogleTest)
gtest_discover_tests(rhi_backend_tests)
```
---
## 5. 测试执行
## 6. 测试执行
### 5.1 统一测试脚本
### 6.1 增量构建(推荐)
**位置**: `scripts/run_tests.py`
**何时需要重新运行 `cmake ..`**
- 新增/删除/重命名源文件
- 修改 CMakeLists.txt添加 subdirectory、target 等)
- 修改编译器选项或宏定义
**功能**:
- 跨平台支持 (Windows/Linux)
- 自动检测测试目录
- 单元测试 + 集成测试
- 测试结果汇总
**增量构建命令**
```bash
# 1. 增量配置(检测 CMakeLists.txt 变化)
cd build
cmake ..
### 5.2 使用方式
# 2. 只构建特定 target不重新配置整个项目
cmake --build . --target math_tests --config Debug
| 命令 | 说明 |
|------|------|
| `python scripts/run_tests.py` | 运行所有测试 |
| `python scripts/run_tests.py --unit-only` | 仅单元测试 |
| `python scripts/run_tests.py --integration` | 含集成测试 |
| `python scripts/run_tests.py --ci` | CI 模式 (跳过 GUI 测试) |
| `python scripts/run_tests.py --build` | 构建并测试 |
| `python scripts/run_tests.py --dir <path>` | 指定测试目录 |
| `python scripts/run_tests.py --verbose` | 详细输出 |
# 3. 只构建特定模块
cmake --build . --target rhi_d3d12_tests --config Debug
```
### 5.3 直接使用 CTest
### 6.2 完整构建
```bash
# 运行特定模块测试
cd build/tests/<module>
ctest -C Debug --output-on-failure
cmake --build build --config Debug
cd build && ctest -C Debug --output-on-failure
```
### 6.3 测试运行
```bash
# 列出所有测试
ctest -N
# 运行所有测试
ctest -C Debug --output-on-failure
```
---
# 运行特定模块测试
ctest -R "Math_" -C Debug --output-on-failure
## 6. CI 集成
# 运行 D3D12 所有测试
ctest -R "D3D12" -C Debug --output-on-failure
### 6.1 GitHub Actions 配置
```yaml
name: XCEngine 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
run: cmake --build build --config Debug
- name: Run Tests
run: python scripts/run_tests.py --ci
```
### 6.2 CI 模式说明
`--ci` 模式会:
- 运行所有单元测试
- 跳过需要 GUI 窗口的集成测试
- 输出机器可读的测试结果
### 6.3 本地验证
```bash
# 完整构建和测试
python scripts/run_tests.py --build
# 仅验证单元测试
python scripts/run_tests.py --unit-only
# 运行特定测试用例
ctest -R "Buffer_Create" -C Debug --output-on-failure
```
---
@@ -247,37 +216,19 @@ python scripts/run_tests.py --unit-only
#include <gtest/gtest.h>
#include <XCEngine/Module/Component.h>
using namespace XCEngine::Module;
class ComponentTest : public ::testing::Test {
protected:
void SetUp() override {
// 初始化测试环境
}
void TearDown() override {
// 清理资源
}
void SetUp() override { }
void TearDown() override { }
};
TEST_F(ComponentTest, Create_ReturnsValidPointer) {
auto component = Component::Create();
ASSERT_NE(component, nullptr);
}
TEST_F(ComponentTest, GetProperty_ReturnsExpectedValue) {
Component component;
EXPECT_EQ(component.GetProperty(), expected_value);
}
```
### 7.2 Fixture 使用原则
- 每个测试类使用独立的 Fixture
- SetUp/TearDown 用于资源初始化/清理
- 避免测试间共享状态
### 7.3 断言选择
### 7.2 断言选择
| 断言 | 用途 |
|------|------|
@@ -286,43 +237,117 @@ TEST_F(ComponentTest, GetProperty_ReturnsExpectedValue) {
---
## 8. 文件结构
## 8. CI 配置
```
tests/
├── CMakeLists.txt
├── TEST_SPEC.md # 本规范
├── run_tests.bat # Windows 测试脚本
├── fixtures/ # 共享夹具头文件
├── scripts/
└── run_tests.py # 统一测试运行脚本
├── math/
├── CMakeLists.txt
│ └── test_*.cpp
├── core/
├── containers/
├── memory/
├── threading/
├── debug/
├── Resources/
└── RHI/
├── D3D12/
│ ├── unit/
│ ├── integration/
│ ├── TEST_SPEC.md # D3D12 专项规范
│ └── TEST_IMPROVEMENT_PLAN.md
└── OpenGL/
```yaml
name: XCEngine 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
run: cmake --build build --config Debug
- name: Run Tests
run: cd build && ctest -C Debug --output-on-failure
```
---
## 9. 规范更新记录
## 9. 性能参考
| 版本 | 日期 | 变更 |
|------|------|------|
| 1.0 | 2026-03-20 | 初始版本,统一测试规范体系 |
### 9.1 模块构建和运行时间
| 模块 | 构建时间 | 运行时间 | 测试数量 |
|------|---------|---------|----------|
| math | ~6s | ~26s | 140 |
| core | ~6s | ~4s | 18 |
| containers | ~3s | ~10s | 51 |
| memory | ~3s | ~4s | 19 |
| threading | ~5s | ~4s | 13 |
| debug | ~3s | ~2s | 8 |
| components | ~3s | ~8s | 39 |
| scene | ~4s | ~2s | 14 |
| resources | ~4s | ~31s | 131 |
| input | ~4s | ~9s | 40 |
| D3D12 unit | ~3s | ~55s | 49 |
| OpenGL unit | ~46s | ~11s | 61 |
| **总计** | - | - | **~622** |
### 9.2 分模块构建命令(推荐)
```bash
# ==================== 快速开发工作流 ====================
# 1. 修改某个模块代码后,增量构建该模块
cmake --build . --target math_tests --config Debug
cmake --build . --target core_tests --config Debug
cmake --build . --target containers_tests --config Debug
cmake --build . --target memory_tests --config Debug
cmake --build . --target threading_tests --config Debug
cmake --build . --target debug_tests --config Debug
cmake --build . --target components_tests --config Debug
cmake --build . --target scene_tests --config Debug
cmake --build . --target resources_tests --config Debug
cmake --build . --target input_tests --config Debug
cmake --build . --target rhi_d3d12_tests --config Debug
cmake --build . --target opengl_engine_tests --config Debug
# 2. 只运行该模块测试
ctest -R "^Math_" -C Debug --output-on-failure
ctest -R "^Core_" -C Debug --output-on-failure
ctest -R "^Containers_" -C Debug --output-on-failure
ctest -R "MemoryTest_|LinearAllocator|PoolAllocator" -C Debug --output-on-failure
ctest -R "^Threading_" -C Debug --output-on-failure
ctest -R "^Debug_" -C Debug --output-on-failure
ctest -R "Component_Test|TransformComponent_" -C Debug --output-on-failure
ctest -R "^Scene_" -C Debug --output-on-failure
ctest -R "Resource[A-Z]|Texture[A-Z]|Mesh[A-Z]|Shader[A-Z]|Audio[A-Z]|Material[A-Z]" -C Debug --output-on-failure
ctest -R "Input|WindowsInput" -C Debug --output-on-failure
ctest -R "D3D12.*Fixture|SwapChain.*Fixture" -C Debug --output-on-failure
build/tests/RHI/OpenGL/unit/Debug/opengl_engine_tests.exe --gtest_filter=*
```
### 9.3 按功能组构建和运行
```bash
# ==================== 基础设施模块 ====================
cmake --build . --target math_tests --config Debug
cmake --build . --target core_tests --config Debug
cmake --build . --target containers_tests --config Debug
ctest -R "^Math_|^Core_|^Containers_" -C Debug --output-on-failure
# ==================== 核心系统模块 ====================
cmake --build . --target memory_tests --config Debug
cmake --build . --target threading_tests --config Debug
cmake --build . --target debug_tests --config Debug
ctest -R "MemoryTest_|LinearAllocator|PoolAllocator|^Threading_|^Debug_" -C Debug --output-on-failure
# ==================== 游戏引擎模块 ====================
cmake --build . --target components_tests --config Debug
cmake --build . --target scene_tests --config Debug
cmake --build . --target resources_tests --config Debug
cmake --build . --target input_tests --config Debug
ctest -R "Component_Test|TransformComponent_|^Scene_|Resource[A-Z]|Texture[A-Z]|Mesh[A-Z]|Input|WindowsInput" -C Debug --output-on-failure
# ==================== RHI 模块 ====================
cmake --build . --target rhi_d3d12_tests --config Debug
cmake --build . --target opengl_engine_tests --config Debug
ctest -R "D3D12.*Fixture|SwapChain.*Fixture" -C Debug --output-on-failure
build/tests/RHI/OpenGL/unit/Debug/opengl_engine_tests.exe --gtest_filter=*
```
### 9.4 注意事项
- **OpenGL 单元测试**:构建时间较长(~46s),因为需要编译 glad.c。建议单独构建
- **D3D12 单元测试**:运行时间较长(~55s),适合 CI 自动化
- **Resources 模块**:运行时间~31s包含大量资源加载测试
- **Math 模块**:运行时间~26s包含140个数学运算测试
---
**规范版本**: 1.0
**最后更新**: 2026-03-20
**最后更新**: 2026-03-23

View File

@@ -8,24 +8,25 @@ set(CONTAINERS_TEST_SOURCES
test_hashmap.cpp
)
add_executable(xcengine_containers_tests ${CONTAINERS_TEST_SOURCES})
add_executable(containers_tests ${CONTAINERS_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_containers_tests PROPERTIES
set_target_properties(containers_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_containers_tests
target_link_libraries(containers_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_containers_tests PRIVATE
target_include_directories(containers_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME ContainersTests COMMAND xcengine_containers_tests)
include(GoogleTest)
gtest_discover_tests(containers_tests)

View File

@@ -6,25 +6,26 @@ set(CORE_TEST_SOURCES
test_core.cpp
)
add_executable(xcengine_core_tests ${CORE_TEST_SOURCES})
add_executable(core_tests ${CORE_TEST_SOURCES})
# Exclude all static runtime libraries to avoid conflicts
if(MSVC)
set_target_properties(xcengine_core_tests PROPERTIES
set_target_properties(core_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_core_tests
target_link_libraries(core_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_core_tests PRIVATE
target_include_directories(core_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME CoreTests COMMAND xcengine_core_tests)
include(GoogleTest)
gtest_discover_tests(core_tests)

View File

@@ -1,14 +1,16 @@
add_executable(xcengine_debug_tests
find_package(GTest REQUIRED)
add_executable(debug_tests
test_logger.cpp
test_profiler.cpp
)
target_link_libraries(xcengine_debug_tests
target_link_libraries(debug_tests
PRIVATE
XCEngine
gtest
gtest_main
GTest::gtest
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(xcengine_debug_tests)
gtest_discover_tests(debug_tests)

View File

@@ -9,25 +9,26 @@ set(MATH_TEST_SOURCES
test_geometry.cpp
)
add_executable(xcengine_math_tests ${MATH_TEST_SOURCES})
add_executable(math_tests ${MATH_TEST_SOURCES})
# Exclude all static runtime libraries to avoid conflicts
if(MSVC)
set_target_properties(xcengine_math_tests PROPERTIES
set_target_properties(math_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_math_tests
target_link_libraries(math_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_math_tests PRIVATE
target_include_directories(math_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME MathTests COMMAND xcengine_math_tests)
include(GoogleTest)
gtest_discover_tests(math_tests)

View File

@@ -8,24 +8,25 @@ set(MEMORY_TEST_SOURCES
test_pool_allocator.cpp
)
add_executable(xcengine_memory_tests ${MEMORY_TEST_SOURCES})
add_executable(memory_tests ${MEMORY_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_memory_tests PROPERTIES
set_target_properties(memory_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_memory_tests
target_link_libraries(memory_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_memory_tests PRIVATE
target_include_directories(memory_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME MemoryTests COMMAND xcengine_memory_tests)
include(GoogleTest)
gtest_discover_tests(memory_tests)

View File

@@ -8,24 +8,25 @@ set(THREADING_TEST_SOURCES
test_task.cpp
)
add_executable(xcengine_threading_tests ${THREADING_TEST_SOURCES})
add_executable(threading_tests ${THREADING_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_threading_tests PROPERTIES
set_target_properties(threading_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_threading_tests
target_link_libraries(threading_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_threading_tests PRIVATE
target_include_directories(threading_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
)
add_test(NAME ThreadingTests COMMAND xcengine_threading_tests)
include(GoogleTest)
gtest_discover_tests(threading_tests)