Files
XCEngine/tests/RHI/D3D12/unit/test_swap_chain.cpp
ssdfasd 26fe3cd835 D3D12: Add bounds check to GetBackBuffer and update unit tests
- Add assert() bounds check to GetBackBuffer() to catch invalid indices
- Include <cassert> in D3D12SwapChain.cpp
- Update test_swap_chain.cpp to use reference return type
- Mark InvalidIndex test as DISABLED (assert aborts on invalid index)
- Update get-back-buffer.md documentation
2026-03-20 18:35:00 +08:00

190 lines
4.7 KiB
C++

#include "fixtures/D3D12TestFixture.h"
#include "XCEngine/RHI/D3D12/D3D12SwapChain.h"
#include <windows.h>
using namespace XCEngine::RHI;
class SwapChainTestFixture : public ::testing::Test {
protected:
void SetUp() override {
HRESULT hr = D3D12CreateDevice(
nullptr,
D3D_FEATURE_LEVEL_12_0,
IID_PPV_ARGS(&mDevice)
);
if (FAILED(hr)) {
GTEST_SKIP() << "Failed to create D3D12 device";
return;
}
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
hr = mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue));
if (FAILED(hr)) {
GTEST_SKIP() << "Failed to create command queue";
return;
}
hr = CreateDXGIFactory1(IID_PPV_ARGS(&mFactory));
if (FAILED(hr)) {
GTEST_SKIP() << "Failed to create DXGI factory";
return;
}
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = "SwapChainTest";
RegisterClassEx(&wc);
mHWND = CreateWindowEx(
0,
"SwapChainTest",
"SwapChain Test",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
wc.hInstance,
NULL
);
if (!mHWND) {
GTEST_SKIP() << "Failed to create window";
return;
}
}
void TearDown() override {
if (mCommandQueue) {
WaitForGPU();
}
if (mHWND) {
DestroyWindow(mHWND);
}
mSwapChain.Shutdown();
mCommandQueue.Reset();
mDevice.Reset();
mFactory.Reset();
}
void WaitForGPU() {
if (!mCommandQueue || !mDevice) return;
ComPtr<ID3D12Fence> fence;
UINT64 fenceValue = 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);
}
}
}
}
Microsoft::WRL::ComPtr<ID3D12Device> mDevice;
Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
Microsoft::WRL::ComPtr<IDXGIFactory4> mFactory;
HWND mHWND = nullptr;
D3D12SwapChain mSwapChain;
};
TEST_F(SwapChainTestFixture, SwapChain_Initialize_FromFactory) {
bool result = mSwapChain.Initialize(
mFactory.Get(),
mCommandQueue.Get(),
mHWND,
800,
600,
2
);
ASSERT_TRUE(result);
}
TEST_F(SwapChainTestFixture, SwapChain_GetBackBuffer_ValidIndex) {
ASSERT_TRUE(mSwapChain.Initialize(
mFactory.Get(),
mCommandQueue.Get(),
mHWND,
800,
600,
2
));
D3D12Texture& backBuffer0 = mSwapChain.GetBackBuffer(0);
ID3D12Resource* resource0 = backBuffer0.GetResource();
ASSERT_NE(resource0, nullptr);
D3D12Texture& backBuffer1 = mSwapChain.GetBackBuffer(1);
ID3D12Resource* resource1 = backBuffer1.GetResource();
ASSERT_NE(resource1, nullptr);
ASSERT_NE(resource0, resource1);
}
TEST_F(SwapChainTestFixture, DISABLED_SwapChain_GetBackBuffer_InvalidIndex) {
ASSERT_TRUE(mSwapChain.Initialize(
mFactory.Get(),
mCommandQueue.Get(),
mHWND,
800,
600,
2
));
ASSERT_DEATH(mSwapChain.GetBackBuffer(99), "BackBuffer index out of range");
}
TEST_F(SwapChainTestFixture, SwapChain_GetCurrentBackBufferIndex) {
ASSERT_TRUE(mSwapChain.Initialize(
mFactory.Get(),
mCommandQueue.Get(),
mHWND,
800,
600,
2
));
uint32_t index = mSwapChain.GetCurrentBackBufferIndex();
EXPECT_LT(index, 2u);
}
TEST_F(SwapChainTestFixture, SwapChain_Present_DoesNotCrash) {
ASSERT_TRUE(mSwapChain.Initialize(
mFactory.Get(),
mCommandQueue.Get(),
mHWND,
800,
600,
2
));
ASSERT_NO_FATAL_FAILURE(mSwapChain.Present(0, 0));
}
TEST_F(SwapChainTestFixture, SwapChain_Shutdown_Cleanup) {
ASSERT_TRUE(mSwapChain.Initialize(
mFactory.Get(),
mCommandQueue.Get(),
mHWND,
800,
600,
2
));
ASSERT_NO_FATAL_FAILURE(mSwapChain.Shutdown());
}