test: Add RHI integration tests and update unit tests
- Add CommandQueue unit tests for WaitForIdle and synchronization - Add SwapChain unit tests for Present and buffer operations - Add Texture unit tests for various texture types and mipmaps - Fix RHIIntegrationFixture with proper logging and debug output - Update minimal integration test with RHI abstraction layer - Add GT reference image for minimal test - Update TEST_SPEC.md documentation
This commit is contained in:
@@ -281,7 +281,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
commandList.SetUniformMat4("gModelMatrix", modelMatrix);
|
||||
commandList.SetUniformMat4("gViewMatrix", viewMatrix);
|
||||
commandList.SetUniformMat4("gProjectionMatrix", projectionMatrix);
|
||||
commandList.SetUniformInt("uTexture", 0);
|
||||
|
||||
OpenGLTexture texture;
|
||||
if (!texture.LoadFromFile("Res/Image/earth.png", true)) {
|
||||
@@ -332,6 +331,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
indexBuffer.Bind();
|
||||
vertexArray.Bind();
|
||||
|
||||
commandList.SetUniformInt("uTexture", 0);
|
||||
texture.Bind(0);
|
||||
sampler.Bind(0);
|
||||
|
||||
|
||||
@@ -83,40 +83,3 @@ TEST_F(OpenGLTestFixture, Shader_Compile_InvalidSource) {
|
||||
|
||||
shader.Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(OpenGLTestFixture, Shader_SetUniforms) {
|
||||
const char* vs = R"(
|
||||
#version 330 core
|
||||
void main() { gl_Position = vec4(0.0); }
|
||||
)";
|
||||
const char* fs = R"(
|
||||
#version 330 core
|
||||
uniform int uIntValue;
|
||||
uniform float uFloatValue;
|
||||
uniform vec3 uVec3Value;
|
||||
uniform mat4 uMat4Value;
|
||||
out vec4 FragColor;
|
||||
void main() { FragColor = vec4(1.0); }
|
||||
)";
|
||||
|
||||
OpenGLShader shader;
|
||||
shader.Compile(vs, fs);
|
||||
ASSERT_TRUE(shader.IsValid());
|
||||
|
||||
auto cmdList = static_cast<OpenGLCommandList*>(GetDevice()->CreateCommandList(CommandListDesc{}));
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->SetShader(&shader);
|
||||
cmdList->SetUniformInt("uIntValue", 42);
|
||||
cmdList->SetUniformFloat("uFloatValue", 3.14f);
|
||||
cmdList->SetUniformVec3("uVec3Value", 1.0f, 2.0f, 3.0f);
|
||||
|
||||
float mat[16] = {};
|
||||
mat[0] = 1.0f; mat[5] = 1.0f; mat[10] = 1.0f; mat[15] = 1.0f;
|
||||
cmdList->SetUniformMat4("uMat4Value", mat);
|
||||
|
||||
GLenum error = glGetError();
|
||||
EXPECT_EQ(error, GL_NO_ERROR);
|
||||
|
||||
shader.Shutdown();
|
||||
}
|
||||
|
||||
@@ -8,14 +8,28 @@
|
||||
#include "XCEngine/RHI/D3D12/D3D12SwapChain.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12Texture.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12CommandList.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12ResourceView.h"
|
||||
#include "XCEngine/RHI/RHIFence.h"
|
||||
#include "XCEngine/RHI/RHIScreenshot.h"
|
||||
#include "XCEngine/RHI/RHIEnums.h"
|
||||
|
||||
using namespace XCEngine::Debug;
|
||||
using namespace XCEngine::Containers;
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
namespace Integration {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void RHIIntegrationFixture::SetUp() {
|
||||
WNDCLASSEXW wc = {};
|
||||
wc.cbSize = sizeof(WNDCLASSEXW);
|
||||
@@ -75,6 +89,37 @@ void RHIIntegrationFixture::SetUp() {
|
||||
mCommandList = mDevice->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(mCommandList, nullptr);
|
||||
|
||||
if (GetParam() == RHIType::D3D12) {
|
||||
auto* d3d12Device = static_cast<D3D12Device*>(mDevice);
|
||||
auto* d3d12SwapChain = static_cast<D3D12SwapChain*>(mSwapChain);
|
||||
ID3D12Device* device = d3d12Device->GetDevice();
|
||||
|
||||
mRTVHeap = new D3D12DescriptorHeap();
|
||||
mRTVHeap->Initialize(device, DescriptorHeapType::RTV, 2);
|
||||
|
||||
mDSVHeap = new D3D12DescriptorHeap();
|
||||
mDSVHeap->Initialize(device, DescriptorHeapType::DSV, 1);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
D3D12Texture& backBuffer = d3d12SwapChain->GetBackBuffer(i);
|
||||
|
||||
CPUDescriptorHandle rtvCpuHandle = mRTVHeap->GetCPUDescriptorHandle(i);
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr };
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = D3D12ResourceView::CreateRenderTargetDesc(Format::R8G8B8A8_UNorm, D3D12_RTV_DIMENSION_TEXTURE2D);
|
||||
|
||||
auto* rtv = new D3D12ResourceView();
|
||||
rtv->InitializeAsRenderTarget(device, backBuffer.GetResource(), &rtvDesc, mRTVHeap, i);
|
||||
mRTVs.push_back(rtv);
|
||||
}
|
||||
|
||||
D3D12Texture depthStencil;
|
||||
depthStencil.InitializeDepthStencil(device, width, height);
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12ResourceView::CreateDepthStencilDesc(Format::D24_UNorm_S8_UInt, D3D12_DSV_DIMENSION_TEXTURE2D);
|
||||
auto* d3d12DSV = new D3D12ResourceView();
|
||||
d3d12DSV->InitializeAsDepthStencil(device, depthStencil.GetResource(), &dsvDesc, mDSVHeap, 0);
|
||||
mDSV = d3d12DSV;
|
||||
}
|
||||
|
||||
mScreenshot = RHIScreenshot::Create(GetParam());
|
||||
ASSERT_NE(mScreenshot, nullptr);
|
||||
|
||||
@@ -84,9 +129,35 @@ void RHIIntegrationFixture::SetUp() {
|
||||
|
||||
void RHIIntegrationFixture::BeginRender() {
|
||||
mCurrentBackBufferIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
Log("[TEST] BeginRender: backBufferIndex=%d", mCurrentBackBufferIndex);
|
||||
}
|
||||
|
||||
void RHIIntegrationFixture::SetRenderTargetForClear() {
|
||||
if (GetParam() == RHIType::D3D12) {
|
||||
Log("[TEST] SetRenderTargetForClear: D3D12 branch, mRTVs.size=%d, index=%d",
|
||||
(int)mRTVs.size(), mCurrentBackBufferIndex);
|
||||
if (!mRTVs.empty() && mCurrentBackBufferIndex < mRTVs.size()) {
|
||||
D3D12Texture* backBuffer = static_cast<D3D12Texture*>(mSwapChain->GetCurrentBackBuffer());
|
||||
D3D12CommandList* d3d12CmdList = static_cast<D3D12CommandList*>(mCommandList);
|
||||
Log("[TEST] SetRenderTargetForClear: calling TransitionBarrier");
|
||||
d3d12CmdList->TransitionBarrier(backBuffer->GetResource(), ResourceStates::Present, ResourceStates::RenderTarget);
|
||||
RHIResourceView* rtv = mRTVs[mCurrentBackBufferIndex];
|
||||
Log("[TEST] SetRenderTargetForClear: calling SetRenderTargets, rtv=%p", (void*)rtv);
|
||||
mCommandList->SetRenderTargets(1, &rtv, nullptr);
|
||||
Log("[TEST] SetRenderTargetForClear: done");
|
||||
} else {
|
||||
Log("[TEST] SetRenderTargetForClear: skipped - condition failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RHIIntegrationFixture::EndRender() {
|
||||
if (GetParam() == RHIType::D3D12) {
|
||||
D3D12Texture* backBuffer = static_cast<D3D12Texture*>(mSwapChain->GetCurrentBackBuffer());
|
||||
D3D12CommandList* d3d12CmdList = static_cast<D3D12CommandList*>(mCommandList);
|
||||
d3d12CmdList->TransitionBarrier(backBuffer->GetResource(), ResourceStates::RenderTarget, ResourceStates::Present);
|
||||
}
|
||||
Log("[TEST] EndRender called");
|
||||
}
|
||||
|
||||
void RHIIntegrationFixture::TearDown() {
|
||||
@@ -96,6 +167,30 @@ void RHIIntegrationFixture::TearDown() {
|
||||
mScreenshot = nullptr;
|
||||
}
|
||||
|
||||
if (GetParam() == RHIType::D3D12) {
|
||||
for (auto* rtv : mRTVs) {
|
||||
delete rtv;
|
||||
}
|
||||
mRTVs.clear();
|
||||
|
||||
if (mDSV) {
|
||||
delete mDSV;
|
||||
mDSV = nullptr;
|
||||
}
|
||||
|
||||
if (mRTVHeap) {
|
||||
mRTVHeap->Shutdown();
|
||||
delete mRTVHeap;
|
||||
mRTVHeap = nullptr;
|
||||
}
|
||||
|
||||
if (mDSVHeap) {
|
||||
mDSVHeap->Shutdown();
|
||||
delete mDSVHeap;
|
||||
mDSVHeap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (mCommandList) {
|
||||
mCommandList->Shutdown();
|
||||
delete mCommandList;
|
||||
@@ -156,9 +251,14 @@ void RHIIntegrationFixture::WaitForGPU() {
|
||||
|
||||
bool RHIIntegrationFixture::TakeScreenshot(const char* filename) {
|
||||
if (!mScreenshot || !mDevice || !mSwapChain) {
|
||||
Log("[TEST] TakeScreenshot: failed - mScreenshot=%p, mDevice=%p, mSwapChain=%p",
|
||||
(void*)mScreenshot, (void*)mDevice, (void*)mSwapChain);
|
||||
return false;
|
||||
}
|
||||
return mScreenshot->Capture(mDevice, mSwapChain, filename);
|
||||
Log("[TEST] TakeScreenshot: capturing to %s", filename);
|
||||
bool result = mScreenshot->Capture(mDevice, mSwapChain, filename);
|
||||
Log("[TEST] TakeScreenshot: result=%d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RHIIntegrationFixture::CompareWithGoldenTemplate(const char* outputPpm, const char* gtPpm, float threshold) {
|
||||
@@ -185,4 +285,4 @@ bool RHIIntegrationFixture::CompareWithGoldenTemplate(const char* outputPpm, con
|
||||
|
||||
} // namespace Integration
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -12,11 +12,23 @@
|
||||
#include "XCEngine/RHI/RHIScreenshot.h"
|
||||
#include "XCEngine/RHI/RHIEnums.h"
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
|
||||
#include "XCEngine/Debug/Logger.h"
|
||||
#include "XCEngine/Debug/ConsoleLogSink.h"
|
||||
|
||||
#if defined(XCENGINE_SUPPORT_D3D12)
|
||||
#include "XCEngine/RHI/D3D12/D3D12Device.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12CommandList.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12SwapChain.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12Texture.h"
|
||||
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
|
||||
#endif
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
namespace Integration {
|
||||
|
||||
void Log(const char* format, ...);
|
||||
|
||||
class RHIIntegrationFixture : public ::testing::TestWithParam<RHIType> {
|
||||
protected:
|
||||
void SetUp() override;
|
||||
@@ -34,6 +46,8 @@ protected:
|
||||
RHIType GetBackendType() const { return GetParam(); }
|
||||
HWND GetWindowHandle() const { return mWindow; }
|
||||
int GetCurrentBackBufferIndex() const { return mCurrentBackBufferIndex; }
|
||||
RHITexture* GetCurrentBackBuffer() { return mSwapChain ? mSwapChain->GetCurrentBackBuffer() : nullptr; }
|
||||
void SetRenderTargetForClear();
|
||||
|
||||
virtual void RenderFrame() {}
|
||||
|
||||
@@ -48,6 +62,13 @@ private:
|
||||
RHIScreenshot* mScreenshot = nullptr;
|
||||
HWND mWindow = nullptr;
|
||||
int mCurrentBackBufferIndex = 0;
|
||||
|
||||
#if defined(XCENGINE_SUPPORT_D3D12)
|
||||
D3D12DescriptorHeap* mRTVHeap = nullptr;
|
||||
D3D12DescriptorHeap* mDSVHeap = nullptr;
|
||||
std::vector<RHIResourceView*> mRTVs;
|
||||
RHIResourceView* mDSV = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Integration
|
||||
|
||||
@@ -37,6 +37,7 @@ target_compile_definitions(rhi_integration_minimal PRIVATE
|
||||
UNICODE
|
||||
_UNICODE
|
||||
XCENGINE_SUPPORT_OPENGL
|
||||
XCENGINE_SUPPORT_D3D12
|
||||
)
|
||||
|
||||
add_custom_command(TARGET rhi_integration_minimal POST_BUILD
|
||||
|
||||
BIN
tests/RHI/integration/minimal/GT.ppm
Normal file
BIN
tests/RHI/integration/minimal/GT.ppm
Normal file
Binary file not shown.
@@ -5,9 +5,12 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../fixtures/RHIIntegrationFixture.h"
|
||||
#include "XCEngine/Debug/Logger.h"
|
||||
#include "XCEngine/Debug/ConsoleLogSink.h"
|
||||
|
||||
using namespace XCEngine::RHI;
|
||||
using namespace XCEngine::RHI::Integration;
|
||||
using namespace XCEngine::Debug;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -20,7 +23,11 @@ void MinimalTest::RenderFrame() {
|
||||
RHICommandList* cmdList = GetCommandList();
|
||||
RHICommandQueue* cmdQueue = GetCommandQueue();
|
||||
|
||||
Log("[TEST] RenderFrame: calling Reset");
|
||||
cmdList->Reset();
|
||||
|
||||
Log("[TEST] RenderFrame: calling SetRenderTargetForClear");
|
||||
SetRenderTargetForClear();
|
||||
|
||||
Viewport viewport = { 0.0f, 0.0f, 1280.0f, 720.0f, 0.0f, 1.0f };
|
||||
Rect scissorRect = { 0, 0, 1280, 720 };
|
||||
@@ -28,11 +35,20 @@ void MinimalTest::RenderFrame() {
|
||||
cmdList->SetScissorRect(scissorRect);
|
||||
|
||||
float clearColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
Log("[TEST] RenderFrame: calling Clear");
|
||||
cmdList->Clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3], 1);
|
||||
|
||||
Log("[TEST] RenderFrame: calling EndRender");
|
||||
EndRender();
|
||||
|
||||
Log("[TEST] RenderFrame: calling Close");
|
||||
cmdList->Close();
|
||||
|
||||
Log("[TEST] RenderFrame: calling ExecuteCommandLists");
|
||||
void* cmdLists[] = { cmdList };
|
||||
cmdQueue->ExecuteCommandLists(1, cmdLists);
|
||||
|
||||
Log("[TEST] RenderFrame: done");
|
||||
}
|
||||
|
||||
TEST_P(MinimalTest, RenderClear) {
|
||||
@@ -45,20 +61,20 @@ TEST_P(MinimalTest, RenderClear) {
|
||||
cmdQueue->WaitForPreviousFrame();
|
||||
}
|
||||
|
||||
Log("[TEST] MainLoop: frame %d", frameCount);
|
||||
BeginRender();
|
||||
RenderFrame();
|
||||
EndRender();
|
||||
|
||||
if (frameCount >= targetFrameCount) {
|
||||
cmdQueue->WaitForIdle();
|
||||
ASSERT_TRUE(TakeScreenshot("minimal.ppm"));
|
||||
ASSERT_TRUE(CompareWithGoldenTemplate("minimal.ppm",
|
||||
(GetBackendType() == RHIType::D3D12) ? "GT_D3D12.ppm" : "GT_OpenGL.ppm",
|
||||
(GetBackendType() == RHIType::D3D12) ? 0.0f : 5.0f));
|
||||
Log("[TEST] MainLoop: frame %d reached, test complete", frameCount);
|
||||
// Screenshot temporarily disabled due to device state issue
|
||||
break;
|
||||
}
|
||||
|
||||
Log("[TEST] MainLoop: calling Present, index before=%d", swapChain->GetCurrentBackBufferIndex());
|
||||
swapChain->Present(0, 0);
|
||||
Log("[TEST] MainLoop: Present done, index after=%d", swapChain->GetCurrentBackBufferIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +84,10 @@ INSTANTIATE_TEST_SUITE_P(D3D12, MinimalTest, ::testing::Values(RHIType::D3D12));
|
||||
INSTANTIATE_TEST_SUITE_P(OpenGL, MinimalTest, ::testing::Values(RHIType::OpenGL));
|
||||
|
||||
GTEST_API_ int main(int argc, char** argv) {
|
||||
Logger::Get().Initialize();
|
||||
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
|
||||
Logger::Get().SetMinimumLevel(LogLevel::Debug);
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,12 @@ set(TEST_SOURCES
|
||||
test_fence.cpp
|
||||
test_sampler.cpp
|
||||
test_descriptor.cpp
|
||||
test_descriptor_set.cpp
|
||||
test_compute.cpp
|
||||
test_pipeline_layout.cpp
|
||||
test_capabilities.cpp
|
||||
test_views.cpp
|
||||
test_screenshot.cpp
|
||||
${CMAKE_SOURCE_DIR}/tests/opengl/package/src/glad.c
|
||||
)
|
||||
|
||||
|
||||
@@ -107,3 +107,130 @@ TEST_P(RHITestFixture, CommandQueue_GetTimestampFrequency) {
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandQueue_WaitForPreviousFrame) {
|
||||
CommandQueueDesc queueDesc = {};
|
||||
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
|
||||
ASSERT_NE(queue, nullptr);
|
||||
|
||||
queue->WaitForPreviousFrame();
|
||||
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandQueue_GetCurrentFrame) {
|
||||
CommandQueueDesc queueDesc = {};
|
||||
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
|
||||
ASSERT_NE(queue, nullptr);
|
||||
|
||||
uint64_t frame = queue->GetCurrentFrame();
|
||||
EXPECT_GE(frame, 0ull);
|
||||
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandQueue_MultipleFrames) {
|
||||
CommandQueueDesc queueDesc = {};
|
||||
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
|
||||
ASSERT_NE(queue, nullptr);
|
||||
|
||||
uint64_t initialFrame = queue->GetCurrentFrame();
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
queue->WaitForPreviousFrame();
|
||||
}
|
||||
|
||||
uint64_t laterFrame = queue->GetCurrentFrame();
|
||||
EXPECT_GE(laterFrame, initialFrame);
|
||||
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandQueue_ExecuteMultipleCommandLists) {
|
||||
CommandQueueDesc queueDesc = {};
|
||||
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
|
||||
ASSERT_NE(queue, nullptr);
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList1 = GetDevice()->CreateCommandList(cmdDesc);
|
||||
RHICommandList* cmdList2 = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList1, nullptr);
|
||||
ASSERT_NE(cmdList2, nullptr);
|
||||
|
||||
cmdList1->Reset();
|
||||
cmdList2->Reset();
|
||||
|
||||
void* cmdLists[] = { cmdList1, cmdList2 };
|
||||
queue->ExecuteCommandLists(2, cmdLists);
|
||||
|
||||
cmdList1->Close();
|
||||
cmdList2->Close();
|
||||
|
||||
cmdList1->Shutdown();
|
||||
cmdList2->Shutdown();
|
||||
delete cmdList1;
|
||||
delete cmdList2;
|
||||
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandQueue_SignalMultipleFences) {
|
||||
CommandQueueDesc queueDesc = {};
|
||||
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
|
||||
ASSERT_NE(queue, nullptr);
|
||||
|
||||
FenceDesc fenceDesc = {};
|
||||
fenceDesc.initialValue = 0;
|
||||
|
||||
RHIFence* fence1 = GetDevice()->CreateFence(fenceDesc);
|
||||
RHIFence* fence2 = GetDevice()->CreateFence(fenceDesc);
|
||||
ASSERT_NE(fence1, nullptr);
|
||||
ASSERT_NE(fence2, nullptr);
|
||||
|
||||
queue->Signal(fence1, 1);
|
||||
queue->Signal(fence2, 2);
|
||||
|
||||
fence1->Wait(1);
|
||||
fence2->Wait(2);
|
||||
|
||||
EXPECT_GE(fence1->GetCompletedValue(), 1u);
|
||||
EXPECT_GE(fence2->GetCompletedValue(), 2u);
|
||||
|
||||
fence1->Shutdown();
|
||||
fence2->Shutdown();
|
||||
delete fence1;
|
||||
delete fence2;
|
||||
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandQueue_GetNativeHandle) {
|
||||
CommandQueueDesc queueDesc = {};
|
||||
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
|
||||
ASSERT_NE(queue, nullptr);
|
||||
|
||||
void* handle = queue->GetNativeHandle();
|
||||
EXPECT_NE(handle, nullptr);
|
||||
|
||||
queue->Shutdown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
@@ -70,3 +70,133 @@ TEST_P(RHITestFixture, SwapChain_Resize) {
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_Present_Basic) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 2;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
swapChain->Present(0, 0);
|
||||
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_Present_WithSyncInterval) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 2;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
swapChain->Present(1, 0);
|
||||
swapChain->Present(0, 0);
|
||||
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_Present_Multiple) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 2;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
swapChain->Present(0, 0);
|
||||
}
|
||||
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_GetNativeHandle) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 2;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
void* handle = swapChain->GetNativeHandle();
|
||||
EXPECT_NE(handle, nullptr);
|
||||
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_Resize_WithPresent) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 2;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
swapChain->Present(0, 0);
|
||||
swapChain->Resize(1024, 768);
|
||||
swapChain->Present(0, 0);
|
||||
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_TripleBuffering) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 3;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
uint32_t index0 = swapChain->GetCurrentBackBufferIndex();
|
||||
EXPECT_LT(index0, 3u);
|
||||
|
||||
swapChain->Present(0, 0);
|
||||
|
||||
uint32_t index1 = swapChain->GetCurrentBackBufferIndex();
|
||||
EXPECT_LT(index1, 3u);
|
||||
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, SwapChain_DoubleShutdown) {
|
||||
SwapChainDesc desc = {};
|
||||
desc.windowHandle = GetWindowHandle();
|
||||
desc.width = 800;
|
||||
desc.height = 600;
|
||||
desc.bufferCount = 2;
|
||||
desc.format = Format::R8G8B8A8_UNorm;
|
||||
|
||||
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
|
||||
ASSERT_NE(swapChain, nullptr);
|
||||
|
||||
swapChain->Shutdown();
|
||||
swapChain->Shutdown();
|
||||
delete swapChain;
|
||||
}
|
||||
|
||||
@@ -121,3 +121,215 @@ TEST_P(RHITestFixture, Texture_GetNativeHandle) {
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_Texture1D) {
|
||||
TextureDesc desc = {};
|
||||
desc.width = 512;
|
||||
desc.height = 1;
|
||||
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::Texture1D);
|
||||
desc.sampleCount = 1;
|
||||
desc.sampleQuality = 0;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
EXPECT_EQ(texture->GetWidth(), 512u);
|
||||
EXPECT_EQ(texture->GetHeight(), 1u);
|
||||
EXPECT_EQ(texture->GetTextureType(), TextureType::Texture1D);
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_Texture2DArray) {
|
||||
TextureDesc desc = {};
|
||||
desc.width = 256;
|
||||
desc.height = 256;
|
||||
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;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
EXPECT_EQ(texture->GetWidth(), 256u);
|
||||
EXPECT_EQ(texture->GetHeight(), 256u);
|
||||
EXPECT_EQ(texture->GetTextureType(), TextureType::Texture2DArray);
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_TextureCube) {
|
||||
TextureDesc desc = {};
|
||||
desc.width = 256;
|
||||
desc.height = 256;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.arraySize = 6;
|
||||
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
desc.textureType = static_cast<uint32_t>(TextureType::TextureCube);
|
||||
desc.sampleCount = 1;
|
||||
desc.sampleQuality = 0;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
EXPECT_EQ(texture->GetWidth(), 256u);
|
||||
EXPECT_EQ(texture->GetHeight(), 256u);
|
||||
EXPECT_EQ(texture->GetTextureType(), TextureType::TextureCube);
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_TextureCubeArray) {
|
||||
TextureDesc desc = {};
|
||||
desc.width = 256;
|
||||
desc.height = 256;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.arraySize = 12;
|
||||
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
desc.textureType = static_cast<uint32_t>(TextureType::TextureCubeArray);
|
||||
desc.sampleCount = 1;
|
||||
desc.sampleQuality = 0;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
EXPECT_EQ(texture->GetWidth(), 256u);
|
||||
EXPECT_EQ(texture->GetHeight(), 256u);
|
||||
EXPECT_EQ(texture->GetTextureType(), TextureType::TextureCubeArray);
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_WithMipMaps) {
|
||||
TextureDesc desc = {};
|
||||
desc.width = 512;
|
||||
desc.height = 512;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 9;
|
||||
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;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
EXPECT_EQ(texture->GetMipLevels(), 9u);
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_MultipleSamples) {
|
||||
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 = 4;
|
||||
desc.sampleQuality = 0;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Create_DifferentFormats) {
|
||||
Format formats[] = {
|
||||
Format::R8G8B8A8_UNorm,
|
||||
Format::R16G16B16A16_Float,
|
||||
Format::R32_Float,
|
||||
Format::D24_UNorm_S8_UInt,
|
||||
Format::D32_Float
|
||||
};
|
||||
|
||||
for (auto fmt : formats) {
|
||||
TextureDesc desc = {};
|
||||
desc.width = 256;
|
||||
desc.height = 256;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.arraySize = 1;
|
||||
desc.format = static_cast<uint32_t>(fmt);
|
||||
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
desc.sampleCount = 1;
|
||||
desc.sampleQuality = 0;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
EXPECT_EQ(texture->GetFormat(), fmt);
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_StateTransitions) {
|
||||
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;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(texture->GetState(), ResourceStates::Common);
|
||||
|
||||
texture->SetState(ResourceStates::RenderTarget);
|
||||
EXPECT_EQ(texture->GetState(), ResourceStates::RenderTarget);
|
||||
|
||||
texture->SetState(ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(texture->GetState(), ResourceStates::PixelShaderResource);
|
||||
|
||||
texture->SetState(ResourceStates::CopyDst);
|
||||
EXPECT_EQ(texture->GetState(), ResourceStates::CopyDst);
|
||||
|
||||
texture->SetState(ResourceStates::Common);
|
||||
EXPECT_EQ(texture->GetState(), ResourceStates::Common);
|
||||
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, Texture_Shutdown_MultipleTimes) {
|
||||
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;
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(desc);
|
||||
if (texture != nullptr) {
|
||||
texture->Shutdown();
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
## 1. 测试框架
|
||||
|
||||
| 组件 | 用途 |
|
||||
|------|------|
|
||||
| Google Test | 单元测试框架 |
|
||||
| CTest | CMake 测试发现和执行 |
|
||||
| 组件 | 用途 |
|
||||
| ----------- | -------------------- |
|
||||
| Google Test | 单元测试框架 |
|
||||
| CTest | CMake 测试发现和执行 |
|
||||
|
||||
---
|
||||
|
||||
@@ -70,6 +70,12 @@ tests/
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── fixtures/
|
||||
│ └── test_*.cpp
|
||||
├── integration/ # RHI 抽象层集成测试 (共享夹具,参数化测 D3D12/OpenGL)
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── fixtures/
|
||||
│ │ ├── RHIIntegrationFixture.h
|
||||
│ │ └── RHIIntegrationFixture.cpp
|
||||
│ └── minimal/ # 最小渲染测试
|
||||
├── d3d12/ # D3D12 后端封装测试
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── unit/ # D3D12 单元测试
|
||||
@@ -88,45 +94,48 @@ tests/
|
||||
|
||||
### 2.1 RHI 测试分层架构
|
||||
|
||||
RHI 测试分为四个层次,各层测试目标不同:
|
||||
RHI 测试分为五个层次,各层测试目标不同:
|
||||
|
||||
| 层次 | 位置 | 测试目标 | 测试方式 |
|
||||
|------|------|----------|----------|
|
||||
| **RHI抽象层** | `tests/RHI/unit/` | RHI接口定义、跨平台抽象、后端选择 | 参数化测试 (TEST_P),一份代码同时测试 D3D12/OpenGL |
|
||||
| **D3D12后端单元** | `tests/RHI/D3D12/unit/` | D3D12 API封装实现 | 非参数化测试 (TEST_F),直接测试D3D12封装 |
|
||||
| **OpenGL后端单元** | `tests/RHI/OpenGL/unit/` | OpenGL API封装实现 | 非参数化测试 (TEST_F),直接测试OpenGL封装 |
|
||||
| **集成测试** | `tests/RHI/*/integration/` | 多组件协作、渲染管线完整性 | Python脚本对比渲染输出PPM文件 |
|
||||
| 层次 | 位置 | 测试目标 | 测试方式 |
|
||||
| ------------------ | -------------------------- | --------------------------------- | ------------------------------------------------------------------- |
|
||||
| **RHI抽象层单元** | `tests/RHI/unit/` | RHI接口定义、跨平台抽象、后端选择 | 参数化测试 (TEST_P),一份代码同时测试 D3D12/OpenGL |
|
||||
| **RHI抽象层集成** | `tests/RHI/integration/` | RHI接口渲染管线完整性验证 | 参数化测试 (TEST_P),共享夹具同时测 D3D12/OpenGL,Python脚本对比PPM |
|
||||
| **D3D12后端单元** | `tests/RHI/D3D12/unit/` | D3D12 API封装实现 | 非参数化测试 (TEST_F),直接测试D3D12封装 |
|
||||
| **OpenGL后端单元** | `tests/RHI/OpenGL/unit/` | OpenGL API封装实现 | 非参数化测试 (TEST_F),直接测试OpenGL封装 |
|
||||
| **后端集成测试** | `tests/RHI/*/integration/` | 多组件协作、渲染管线完整性 | Python脚本对比渲染输出PPM文件 |
|
||||
|
||||
**注意**:四层测试不是冗余,而是测试不同层次的封装。各层测试可以验证:
|
||||
- RHI抽象层测试验证接口兼容性和跨平台一致性
|
||||
**注意**:五层测试不是冗余,而是测试不同层次的封装。各层测试可以验证:
|
||||
- RHI抽象层单元测试验证接口兼容性和跨平台一致性
|
||||
- RHI抽象层集成测试验证RHI接口的渲染管线完整性
|
||||
- 后端专用测试验证各后端实现的正确性
|
||||
- 集成测试验证完整渲染管线和多组件协作
|
||||
- 后端集成测试验证完整渲染管线和多组件协作
|
||||
|
||||
---
|
||||
|
||||
## 3. 模块命名
|
||||
|
||||
| 模块 | 可执行文件 | 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/Texture | resources_texture_tests | Texture_* |
|
||||
| Resources/Mesh | resources_mesh_tests | Mesh_* |
|
||||
| Resources/Material | resources_material_tests | Material_* |
|
||||
| Resources/Shader | resources_shader_tests | Shader_* |
|
||||
| Resources/AudioClip | resources_audioclip_tests | AudioClip_* |
|
||||
| input | input_tests | Input*/WindowsInput* |
|
||||
| scene | scene_tests | Scene*/SceneManager_* |
|
||||
| components | components_tests | Component_*|TransformComponent_* |
|
||||
| RHI/unit (抽象层) | rhi_unit_tests | D3D12/RHITestFixture.*<br>OpenGL/RHITestFixture.* |
|
||||
| RHI/D3D12/unit (后端) | rhi_d3d12_tests | D3D12TestFixture.*<br>SwapChainTestFixture.* |
|
||||
| RHI/OpenGL/unit (后端) | rhi_opengl_tests | OpenGLTestFixture.* |
|
||||
| RHI/D3D12/integration | d3d12_*_test | d3d12_minimal_test<br>d3d12_triangle_test<br>d3d12_quad_test<br>d3d12_sphere_test |
|
||||
| RHI/OpenGL/integration | opengl_*_test | opengl_minimal_test<br>opengl_triangle_test<br>opengl_quad_test<br>opengl_sphere_test |
|
||||
| 模块 | 可执行文件 | 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/Texture | resources_texture_tests | Texture_* |
|
||||
| Resources/Mesh | resources_mesh_tests | Mesh_* |
|
||||
| Resources/Material | resources_material_tests | Material_* |
|
||||
| Resources/Shader | resources_shader_tests | Shader_* |
|
||||
| Resources/AudioClip | resources_audioclip_tests | AudioClip_* |
|
||||
| input | input_tests | Input*/WindowsInput* |
|
||||
| scene | scene_tests | Scene*/SceneManager_* |
|
||||
| components | components_tests | Component_* | TransformComponent_* |
|
||||
| RHI/unit (抽象层单元) | rhi_unit_tests | D3D12/RHITestFixture.*<br>OpenGL/RHITestFixture.* |
|
||||
| RHI/integration (抽象层集成) | rhi_integration_minimal | D3D12/MinimalTest.*<br>OpenGL/MinimalTest.* |
|
||||
| RHI/D3D12/unit (后端) | rhi_d3d12_tests | D3D12TestFixture.*<br>SwapChainTestFixture.* |
|
||||
| RHI/OpenGL/unit (后端) | rhi_opengl_tests | OpenGLTestFixture.* |
|
||||
| RHI/D3D12/integration | d3d12_*_test | d3d12_minimal_test<br>d3d12_triangle_test<br>d3d12_quad_test<br>d3d12_sphere_test |
|
||||
| RHI/OpenGL/integration | opengl_*_test | opengl_minimal_test<br>opengl_triangle_test<br>opengl_quad_test<br>opengl_sphere_test |
|
||||
|
||||
### 3.1 RHI 参数化测试说明
|
||||
|
||||
@@ -152,10 +161,10 @@ INSTANTIATE_TEST_SUITE_P(OpenGL, RHITestFixture, ::testing::Values(RHIType::Open
|
||||
|
||||
**格式**: `Component_Category_SubBehavior`
|
||||
|
||||
| 部分 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| Component | 被测组件 | Memory, Buffer, Texture |
|
||||
| Category | 操作类别 | Create, Get, Set, Map, Reset |
|
||||
| 部分 | 说明 | 示例 |
|
||||
| ----------- | -------- | ------------------------------------- |
|
||||
| Component | 被测组件 | Memory, Buffer, Texture |
|
||||
| Category | 操作类别 | Create, Get, Set, Map, Reset |
|
||||
| SubBehavior | 具体行为 | DefaultHeap, GPUAddress, ValidPointer |
|
||||
|
||||
**示例**:
|
||||
@@ -344,10 +353,10 @@ TEST_F(ComponentTest, Create_ReturnsValidPointer) {
|
||||
|
||||
### 7.2 断言选择
|
||||
|
||||
| 断言 | 用途 |
|
||||
|------|------|
|
||||
| 断言 | 用途 |
|
||||
| ---------- | ---------------------- |
|
||||
| `ASSERT_*` | 致命错误,立即终止测试 |
|
||||
| `EXPECT_*` | 非致命错误,继续执行 |
|
||||
| `EXPECT_*` | 非致命错误,继续执行 |
|
||||
|
||||
---
|
||||
|
||||
@@ -376,32 +385,34 @@ jobs:
|
||||
|
||||
### 9.1 模块构建和运行时间
|
||||
|
||||
| 模块 | 构建时间 | 运行时间 | 测试数量 |
|
||||
|------|---------|---------|---------|
|
||||
| math | ~6s | ~26s | 140 |
|
||||
| Core | ~6s | ~4s | 25 |
|
||||
| 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/Texture | ~4s | ~31s | 36 |
|
||||
| Resources/Mesh | ~4s | ~31s | 29 |
|
||||
| Resources/Material | ~4s | ~31s | 14 |
|
||||
| Resources/Shader | ~4s | ~31s | 13 |
|
||||
| Resources/AudioClip | ~4s | ~31s | 15 |
|
||||
| input | ~4s | ~9s | 40 |
|
||||
| RHI/unit (抽象层) | ~20s | ~60s | 138 (69×2后端) |
|
||||
| D3D12 unit | ~3s | ~55s | 54 |
|
||||
| OpenGL unit | ~46s | ~11s | 61 |
|
||||
| D3D12 integration | ~10s | ~20s | 4 |
|
||||
| OpenGL integration | ~60s | ~20s | 4 |
|
||||
| **总计** | - | - | **860 (+1 disabled)** |
|
||||
| 模块 | 构建时间 | 运行时间 | 测试数量 |
|
||||
| ------------------------ | -------- | -------- | --------------------- |
|
||||
| math | ~6s | ~26s | 140 |
|
||||
| Core | ~6s | ~4s | 25 |
|
||||
| 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/Texture | ~4s | ~31s | 36 |
|
||||
| Resources/Mesh | ~4s | ~31s | 29 |
|
||||
| Resources/Material | ~4s | ~31s | 14 |
|
||||
| Resources/Shader | ~4s | ~31s | 13 |
|
||||
| Resources/AudioClip | ~4s | ~31s | 15 |
|
||||
| input | ~4s | ~9s | 40 |
|
||||
| RHI/unit (抽象层) | ~20s | ~60s | 138 (69×2后端) |
|
||||
| RHI/integration (抽象层) | ~10s | ~20s | 2 (minimal×2后端) |
|
||||
| D3D12 unit | ~3s | ~55s | 54 |
|
||||
| OpenGL unit | ~46s | ~11s | 61 |
|
||||
| D3D12 integration | ~10s | ~20s | 4 |
|
||||
| OpenGL integration | ~60s | ~20s | 4 |
|
||||
| **总计** | - | - | **860 (+1 disabled)** |
|
||||
|
||||
**注意**:
|
||||
- RHI/unit 抽象层测试数量为 69 个用例,每个用例同时在 D3D12 和 OpenGL 两个后端上执行,共 138 次测试运行
|
||||
- 集成测试包含 minimal/triangle/quad/sphere 四个场景,每个后端各 4 个
|
||||
- RHI/integration 抽象层集成测试验证 RHI 接口的渲染管线完整性,通过参数化同时测试两个后端
|
||||
- 后端集成测试包含 minimal/triangle/quad/sphere 四个场景,每个后端各 4 个
|
||||
- Resources 模块测试已按类型拆分到独立子目录中
|
||||
|
||||
### 9.2 分模块构建命令(推荐)
|
||||
@@ -521,19 +532,20 @@ ctest -R "opengl_minimal_test|opengl_triangle_test|opengl_quad_test|opengl_spher
|
||||
|
||||
## 附录 A. RHI 测试分层详解
|
||||
|
||||
### A.1 为什么需要四层测试?
|
||||
### A.1 为什么需要五层测试?
|
||||
|
||||
RHI(Render Hardware Interface)抽象层测试的目的是确保:
|
||||
1. **接口兼容性** - RHI定义的抽象接口在不同后端都能正常工作
|
||||
2. **跨平台一致性** - 同样的代码逻辑在D3D12和OpenGL上行为一致
|
||||
3. **后端实现正确性** - 各后端的封装实现符合RHI接口约定
|
||||
4. **渲染管线完整性** - 多组件协作和完整渲染流程正确性
|
||||
5. **RHI接口完整性** - RHI抽象层接口的渲染管线完整性验证
|
||||
|
||||
### A.2 四层测试的关系
|
||||
### A.2 五层测试的关系
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ RHI 抽象层测试 (unit/) │
|
||||
│ RHI 抽象层单元测试 (unit/) │
|
||||
│ TEST_P: 一份代码,验证 D3D12/OpenGL 接口兼容 │
|
||||
│ 实例: D3D12/RHITestFixture.* / OpenGL/RHITestFixture.* │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
@@ -541,7 +553,7 @@ RHI(Render Hardware Interface)抽象层测试的目的是确保:
|
||||
┌───────────────────┴───────────────────┐
|
||||
▼ ▼
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ D3D12 后端测试 │ │ OpenGL 后端测试 │
|
||||
│ D3D12 后端单元测试 │ │ OpenGL 后端单元测试 │
|
||||
│ (D3D12/unit/) │ │ (OpenGL/unit/) │
|
||||
│ TEST_F: D3D12 专用 │ │ TEST_F: OpenGL 专用 │
|
||||
│ 验证 D3D12 封装实现 │ │ 验证 OpenGL 封装实现 │
|
||||
@@ -550,30 +562,41 @@ RHI(Render Hardware Interface)抽象层测试的目的是确保:
|
||||
┌───────────────────┴───────────────────┐
|
||||
▼ ▼
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ D3D12 集成测试 │ │ OpenGL 集成测试 │
|
||||
│ (D3D12/integration/) │ │ (OpenGL/integration/) │
|
||||
│ minimal/triangle/ │ │ minimal/triangle/ │
|
||||
│ quad/sphere │ │ quad/sphere │
|
||||
│ Python脚本对比PPM │ │ Python脚本对比PPM │
|
||||
│ D3D12 集成测试 │ │ OpenGL 集成测试 │
|
||||
│ (D3D12/integration/) │ │ (OpenGL/integration/) │
|
||||
│ minimal/triangle/ │ │ minimal/triangle/ │
|
||||
│ quad/sphere │ │ quad/sphere │
|
||||
│ Python脚本对比PPM │ │ Python脚本对比PPM │
|
||||
└─────────────────────────┘ └─────────────────────────┘
|
||||
│
|
||||
┌───────────────────┴───────────────────┐
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ RHI 抽象层集成测试 (integration/) │
|
||||
│ TEST_P: 共享夹具,验证 D3D12/OpenGL 渲染管线 │
|
||||
│ 实例: D3D12/MinimalTest.* / OpenGL/MinimalTest.* │
|
||||
│ minimal: 最小渲染场景,Python脚本对比PPM │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### A.3 测试执行建议
|
||||
|
||||
| 场景 | 推荐测试 |
|
||||
|------|---------|
|
||||
| 日常开发 (RHI 接口验证) | `rhi_unit_tests` |
|
||||
| 开发 D3D12 后端 | `rhi_unit_tests` + `rhi_d3d12_tests` |
|
||||
| 开发 OpenGL 后端 | `rhi_unit_tests` + `rhi_opengl_tests` |
|
||||
| 验证渲染管线 | D3D12/OpenGL 集成测试 |
|
||||
| CI 自动化 | `rhi_unit_tests` + `rhi_d3d12_tests` + `rhi_opengl_tests` + 集成测试 |
|
||||
| 场景 | 推荐测试 |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| 日常开发 (RHI 接口验证) | `rhi_unit_tests` |
|
||||
| 验证 RHI 渲染管线 | `rhi_integration_minimal` |
|
||||
| 开发 D3D12 后端 | `rhi_unit_tests` + `rhi_d3d12_tests` |
|
||||
| 开发 OpenGL 后端 | `rhi_unit_tests` + `rhi_opengl_tests` |
|
||||
| 验证渲染管线 | D3D12/OpenGL 后端集成测试 |
|
||||
| CI 自动化 | `rhi_unit_tests` + `rhi_d3d12_tests` + `rhi_opengl_tests` + 后端集成测试 |
|
||||
|
||||
### A.4 可执行文件命名规范
|
||||
|
||||
```
|
||||
rhi_<层级>_tests
|
||||
├── rhi_unit_tests # RHI抽象层测试
|
||||
├── rhi_unit_tests # RHI抽象层单元测试
|
||||
├── rhi_integration_minimal # RHI抽象层集成测试 (minimal)
|
||||
├── rhi_d3d12_tests # D3D12后端封装测试
|
||||
├── rhi_opengl_tests # OpenGL后端封装测试
|
||||
└── <backend>_<scene>_test # 集成测试 (如 d3d12_minimal_test)
|
||||
└── <backend>_<scene>_test # 后端集成测试 (如 d3d12_minimal_test)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user