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:
2026-03-25 20:50:49 +08:00
parent 04a80d10e7
commit a9b9a6ebfc
12 changed files with 731 additions and 129 deletions

View File

@@ -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