test: 添加 Input 模块单元测试
- 创建 tests/Input 目录和 CMakeLists.txt - 添加 28 个测试用例覆盖: - InputManager: Singleton, Initialize/Shutdown, 按键状态, 鼠标, 轴, 按钮 - InputAxis: 默认构造, 正负键, SetValue - InputEvent: KeyEvent, MouseButtonEvent, MouseMoveEvent, MouseWheelEvent, TextInputEvent
This commit is contained in:
29
tests/Input/CMakeLists.txt
Normal file
29
tests/Input/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
# ============================================================
|
||||
# Input Module Tests
|
||||
# ============================================================
|
||||
|
||||
set(INPUT_TEST_SOURCES
|
||||
test_input_manager.cpp
|
||||
)
|
||||
|
||||
add_executable(xcengine_input_tests ${INPUT_TEST_SOURCES})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(xcengine_input_tests PROPERTIES
|
||||
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(xcengine_input_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(xcengine_input_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/tests/fixtures
|
||||
)
|
||||
|
||||
add_test(NAME InputTests COMMAND xcengine_input_tests)
|
||||
423
tests/Input/test_input_manager.cpp
Normal file
423
tests/Input/test_input_manager.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Input/InputManager.h>
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
#include <XCEngine/Input/InputEvent.h>
|
||||
#include <XCEngine/Input/InputAxis.h>
|
||||
#include <XCEngine/Math/Vector2.h>
|
||||
#include <XCEngine/Containers/String.h>
|
||||
|
||||
using namespace XCEngine::Input;
|
||||
using namespace XCEngine::Math;
|
||||
using namespace XCEngine::Containers;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(InputManager, Singleton) {
|
||||
InputManager& mgr1 = InputManager::Get();
|
||||
InputManager& mgr2 = InputManager::Get();
|
||||
EXPECT_EQ(&mgr1, &mgr2);
|
||||
}
|
||||
|
||||
TEST(InputManager, InitializeShutdown) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, IsKeyDown) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_FALSE(mgr.IsKeyDown(KeyCode::A));
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::A, false, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.IsKeyDown(KeyCode::A));
|
||||
|
||||
mgr.ProcessKeyUp(KeyCode::A, false, false, false, false);
|
||||
EXPECT_FALSE(mgr.IsKeyDown(KeyCode::A));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, IsKeyPressed) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_FALSE(mgr.IsKeyPressed(KeyCode::A));
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::A, false, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.IsKeyPressed(KeyCode::A));
|
||||
|
||||
mgr.Update(0.016f);
|
||||
EXPECT_FALSE(mgr.IsKeyPressed(KeyCode::A));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, IsKeyUp) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_TRUE(mgr.IsKeyUp(KeyCode::A));
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::A, false, false, false, false, false);
|
||||
EXPECT_FALSE(mgr.IsKeyUp(KeyCode::A));
|
||||
|
||||
mgr.ProcessKeyUp(KeyCode::A, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.IsKeyUp(KeyCode::A));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, KeyEventModifiers) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
bool eventFired = false;
|
||||
KeyEvent capturedEvent{};
|
||||
|
||||
uint64_t id = mgr.OnKeyEvent().Subscribe([&eventFired, &capturedEvent](const KeyEvent& e) {
|
||||
eventFired = true;
|
||||
capturedEvent = e;
|
||||
});
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::A, false, true, true, false, false);
|
||||
EXPECT_TRUE(eventFired);
|
||||
EXPECT_EQ(capturedEvent.keyCode, KeyCode::A);
|
||||
EXPECT_TRUE(capturedEvent.alt);
|
||||
EXPECT_TRUE(capturedEvent.ctrl);
|
||||
EXPECT_FALSE(capturedEvent.shift);
|
||||
EXPECT_FALSE(capturedEvent.meta);
|
||||
EXPECT_EQ(capturedEvent.type, KeyEvent::Type::Down);
|
||||
|
||||
mgr.OnKeyEvent().Unsubscribe(id);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, KeyEventRepeat) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
bool eventFired = false;
|
||||
KeyEvent capturedEvent{};
|
||||
|
||||
uint64_t id = mgr.OnKeyEvent().Subscribe([&eventFired, &capturedEvent](const KeyEvent& e) {
|
||||
eventFired = true;
|
||||
capturedEvent = e;
|
||||
});
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::A, true, false, false, false, false);
|
||||
EXPECT_TRUE(eventFired);
|
||||
EXPECT_EQ(capturedEvent.type, KeyEvent::Type::Repeat);
|
||||
|
||||
mgr.OnKeyEvent().Unsubscribe(id);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, MousePosition) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
mgr.ProcessMouseMove(100, 200, 10, -5);
|
||||
|
||||
Vector2 pos = mgr.GetMousePosition();
|
||||
EXPECT_EQ(pos.x, 100.0f);
|
||||
EXPECT_EQ(pos.y, 200.0f);
|
||||
|
||||
Vector2 delta = mgr.GetMouseDelta();
|
||||
EXPECT_EQ(delta.x, 10.0f);
|
||||
EXPECT_EQ(delta.y, -5.0f);
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, MouseButton) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_FALSE(mgr.IsMouseButtonDown(MouseButton::Left));
|
||||
|
||||
mgr.ProcessMouseButton(MouseButton::Left, true, 100, 200);
|
||||
EXPECT_TRUE(mgr.IsMouseButtonDown(MouseButton::Left));
|
||||
|
||||
mgr.ProcessMouseButton(MouseButton::Left, false, 100, 200);
|
||||
EXPECT_FALSE(mgr.IsMouseButtonDown(MouseButton::Left));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, MouseButtonClicked) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_FALSE(mgr.IsMouseButtonClicked(MouseButton::Left));
|
||||
|
||||
mgr.ProcessMouseButton(MouseButton::Left, true, 100, 200);
|
||||
EXPECT_TRUE(mgr.IsMouseButtonClicked(MouseButton::Left));
|
||||
|
||||
mgr.Update(0.016f);
|
||||
EXPECT_FALSE(mgr.IsMouseButtonClicked(MouseButton::Left));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, MouseButtonEvent) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
bool eventFired = false;
|
||||
MouseButtonEvent capturedEvent{};
|
||||
|
||||
uint64_t id = mgr.OnMouseButton().Subscribe([&eventFired, &capturedEvent](const MouseButtonEvent& e) {
|
||||
eventFired = true;
|
||||
capturedEvent = e;
|
||||
});
|
||||
|
||||
mgr.ProcessMouseButton(MouseButton::Left, true, 100, 200);
|
||||
EXPECT_TRUE(eventFired);
|
||||
EXPECT_EQ(capturedEvent.button, MouseButton::Left);
|
||||
EXPECT_EQ(capturedEvent.position.x, 100.0f);
|
||||
EXPECT_EQ(capturedEvent.position.y, 200.0f);
|
||||
EXPECT_EQ(capturedEvent.type, MouseButtonEvent::Pressed);
|
||||
|
||||
mgr.OnMouseButton().Unsubscribe(id);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, MouseMoveEvent) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
bool eventFired = false;
|
||||
MouseMoveEvent capturedEvent{};
|
||||
|
||||
uint64_t id = mgr.OnMouseMove().Subscribe([&eventFired, &capturedEvent](const MouseMoveEvent& e) {
|
||||
eventFired = true;
|
||||
capturedEvent = e;
|
||||
});
|
||||
|
||||
mgr.ProcessMouseMove(100, 200, 10, -5);
|
||||
EXPECT_TRUE(eventFired);
|
||||
EXPECT_EQ(capturedEvent.position.x, 100.0f);
|
||||
EXPECT_EQ(capturedEvent.position.y, 200.0f);
|
||||
EXPECT_EQ(capturedEvent.delta.x, 10.0f);
|
||||
EXPECT_EQ(capturedEvent.delta.y, -5.0f);
|
||||
|
||||
mgr.OnMouseMove().Unsubscribe(id);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, MouseWheelEvent) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
bool eventFired = false;
|
||||
MouseWheelEvent capturedEvent{};
|
||||
|
||||
uint64_t id = mgr.OnMouseWheel().Subscribe([&eventFired, &capturedEvent](const MouseWheelEvent& e) {
|
||||
eventFired = true;
|
||||
capturedEvent = e;
|
||||
});
|
||||
|
||||
mgr.ProcessMouseWheel(1.0f, 100, 200);
|
||||
EXPECT_TRUE(eventFired);
|
||||
EXPECT_EQ(capturedEvent.delta, 1.0f);
|
||||
|
||||
mgr.OnMouseWheel().Unsubscribe(id);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, DefaultAxes) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
float h = mgr.GetAxis("Horizontal");
|
||||
EXPECT_GE(h, -1.0f);
|
||||
EXPECT_LE(h, 1.0f);
|
||||
|
||||
float v = mgr.GetAxis("Vertical");
|
||||
EXPECT_GE(v, -1.0f);
|
||||
EXPECT_LE(v, 1.0f);
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, DefaultButtons) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_FALSE(mgr.GetButton("Jump"));
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::Space, false, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.GetButton("Jump"));
|
||||
|
||||
mgr.ProcessKeyUp(KeyCode::Space, false, false, false, false);
|
||||
EXPECT_FALSE(mgr.GetButton("Jump"));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, GetButtonDown) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
EXPECT_FALSE(mgr.GetButtonDown("Fire1"));
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::LeftCtrl, false, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.GetButtonDown("Fire1"));
|
||||
|
||||
mgr.Update(0.016f);
|
||||
EXPECT_FALSE(mgr.GetButtonDown("Fire1"));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, GetButtonUp) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::LeftCtrl, false, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.GetButton("Fire1"));
|
||||
|
||||
mgr.ProcessKeyUp(KeyCode::LeftCtrl, false, false, false, false);
|
||||
EXPECT_TRUE(mgr.GetButtonUp("Fire1"));
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, RegisterAxis) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
mgr.ClearAxes();
|
||||
|
||||
InputAxis axis("TestAxis", KeyCode::W, KeyCode::S);
|
||||
mgr.RegisterAxis(axis);
|
||||
|
||||
EXPECT_EQ(mgr.GetAxis("TestAxis"), 0.0f);
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::W, false, false, false, false, false);
|
||||
EXPECT_EQ(mgr.GetAxis("TestAxis"), 1.0f);
|
||||
|
||||
mgr.ProcessKeyUp(KeyCode::W, false, false, false, false);
|
||||
EXPECT_EQ(mgr.GetAxis("TestAxis"), 0.0f);
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::S, false, false, false, false, false);
|
||||
EXPECT_EQ(mgr.GetAxis("TestAxis"), -1.0f);
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, GetAxisRaw) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
mgr.ClearAxes();
|
||||
InputAxis axis("RawTest", KeyCode::D, KeyCode::A);
|
||||
mgr.RegisterAxis(axis);
|
||||
|
||||
mgr.ProcessKeyDown(KeyCode::D, false, false, false, false, false);
|
||||
EXPECT_EQ(mgr.GetAxisRaw("RawTest"), 1.0f);
|
||||
|
||||
mgr.Update(0.016f);
|
||||
EXPECT_EQ(mgr.GetAxisRaw("RawTest"), 0.0f);
|
||||
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputManager, TextInputEvent) {
|
||||
InputManager& mgr = InputManager::Get();
|
||||
mgr.Initialize(nullptr);
|
||||
|
||||
bool eventFired = false;
|
||||
TextInputEvent capturedEvent{};
|
||||
|
||||
uint64_t id = mgr.OnTextInput().Subscribe([&eventFired, &capturedEvent](const TextInputEvent& e) {
|
||||
eventFired = true;
|
||||
capturedEvent = e;
|
||||
});
|
||||
|
||||
mgr.ProcessTextInput('A');
|
||||
EXPECT_TRUE(eventFired);
|
||||
EXPECT_EQ(capturedEvent.character, 'A');
|
||||
|
||||
mgr.OnTextInput().Unsubscribe(id);
|
||||
mgr.Shutdown();
|
||||
}
|
||||
|
||||
TEST(InputAxis, DefaultConstruction) {
|
||||
InputAxis axis;
|
||||
EXPECT_EQ(axis.GetValue(), 0.0f);
|
||||
EXPECT_EQ(axis.GetPositiveKey(), KeyCode::None);
|
||||
EXPECT_EQ(axis.GetNegativeKey(), KeyCode::None);
|
||||
}
|
||||
|
||||
TEST(InputAxis, PositiveNegativeKeys) {
|
||||
InputAxis axis("Test", KeyCode::W, KeyCode::S);
|
||||
EXPECT_EQ(axis.GetPositiveKey(), KeyCode::W);
|
||||
EXPECT_EQ(axis.GetNegativeKey(), KeyCode::S);
|
||||
}
|
||||
|
||||
TEST(InputAxis, SetValue) {
|
||||
InputAxis axis;
|
||||
axis.SetValue(0.5f);
|
||||
EXPECT_EQ(axis.GetValue(), 0.5f);
|
||||
}
|
||||
|
||||
TEST(InputEvent, KeyEventConstruction) {
|
||||
KeyEvent event;
|
||||
event.keyCode = KeyCode::A;
|
||||
event.alt = false;
|
||||
event.ctrl = false;
|
||||
event.shift = false;
|
||||
event.meta = false;
|
||||
event.type = KeyEvent::Type::Down;
|
||||
EXPECT_EQ(event.keyCode, KeyCode::A);
|
||||
EXPECT_FALSE(event.alt);
|
||||
EXPECT_FALSE(event.ctrl);
|
||||
EXPECT_FALSE(event.shift);
|
||||
EXPECT_FALSE(event.meta);
|
||||
EXPECT_EQ(event.type, KeyEvent::Type::Down);
|
||||
}
|
||||
|
||||
TEST(InputEvent, MouseButtonEventConstruction) {
|
||||
MouseButtonEvent event;
|
||||
event.button = MouseButton::Left;
|
||||
event.position.x = 100.0f;
|
||||
event.position.y = 200.0f;
|
||||
event.type = MouseButtonEvent::Pressed;
|
||||
EXPECT_EQ(event.button, MouseButton::Left);
|
||||
EXPECT_EQ(event.position.x, 100.0f);
|
||||
EXPECT_EQ(event.position.y, 200.0f);
|
||||
EXPECT_EQ(event.type, MouseButtonEvent::Pressed);
|
||||
}
|
||||
|
||||
TEST(InputEvent, MouseMoveEventConstruction) {
|
||||
MouseMoveEvent event;
|
||||
event.position.x = 100.0f;
|
||||
event.position.y = 200.0f;
|
||||
event.delta.x = 10.0f;
|
||||
event.delta.y = -5.0f;
|
||||
EXPECT_EQ(event.position.x, 100.0f);
|
||||
EXPECT_EQ(event.position.y, 200.0f);
|
||||
EXPECT_EQ(event.delta.x, 10.0f);
|
||||
EXPECT_EQ(event.delta.y, -5.0f);
|
||||
}
|
||||
|
||||
TEST(InputEvent, MouseWheelEventConstruction) {
|
||||
MouseWheelEvent event;
|
||||
event.position.x = 100.0f;
|
||||
event.position.y = 200.0f;
|
||||
event.delta = 1.5f;
|
||||
EXPECT_EQ(event.position.x, 100.0f);
|
||||
EXPECT_EQ(event.position.y, 200.0f);
|
||||
EXPECT_EQ(event.delta, 1.5f);
|
||||
}
|
||||
|
||||
TEST(InputEvent, TextInputEventConstruction) {
|
||||
TextInputEvent event;
|
||||
event.character = 'X';
|
||||
EXPECT_EQ(event.character, 'X');
|
||||
}
|
||||
|
||||
} // namespace
|
||||
260
tests/RHI/TEST_ISSUES.md
Normal file
260
tests/RHI/TEST_ISSUES.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user