diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12SwapChain.h b/engine/include/XCEngine/RHI/D3D12/D3D12SwapChain.h index aab60bd5..d91ffd05 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12SwapChain.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12SwapChain.h @@ -33,6 +33,9 @@ public: void* GetNativeHandle() override; private: + bool RefreshBackBuffers(); + void ReleaseBackBuffers(); + ComPtr m_swapChain; ComPtr m_commandQueue; uint32_t m_width; diff --git a/engine/src/RHI/D3D12/D3D12SwapChain.cpp b/engine/src/RHI/D3D12/D3D12SwapChain.cpp index 0b50fb79..eae7b168 100644 --- a/engine/src/RHI/D3D12/D3D12SwapChain.cpp +++ b/engine/src/RHI/D3D12/D3D12SwapChain.cpp @@ -43,14 +43,7 @@ bool D3D12SwapChain::Initialize(IDXGIFactory4* factory, ID3D12CommandQueue* comm m_height = height; m_bufferCount = bufferCount; - m_backBuffers.resize(m_bufferCount); - for (uint32_t i = 0; i < m_bufferCount; ++i) { - ID3D12Resource* resource = nullptr; - m_swapChain->GetBuffer(i, IID_PPV_ARGS(&resource)); - m_backBuffers[i].InitializeFromExisting(resource, false); - } - - return true; + return RefreshBackBuffers(); } bool D3D12SwapChain::Initialize(IDXGISwapChain* swapChain, uint32_t width, uint32_t height) { @@ -59,21 +52,25 @@ bool D3D12SwapChain::Initialize(IDXGISwapChain* swapChain, uint32_t width, uint3 return false; } - m_width = width; - m_height = height; - - m_backBuffers.resize(m_bufferCount); - for (uint32_t i = 0; i < m_bufferCount; ++i) { - ID3D12Resource* resource = nullptr; - m_swapChain->GetBuffer(i, IID_PPV_ARGS(&resource)); - m_backBuffers[i].InitializeFromExisting(resource, false); + DXGI_SWAP_CHAIN_DESC desc = {}; + hResult = m_swapChain->GetDesc(&desc); + if (FAILED(hResult) || desc.BufferCount == 0) { + return false; } - return true; + m_width = width; + m_height = height; + m_bufferCount = desc.BufferCount; + + return RefreshBackBuffers(); } void D3D12SwapChain::Shutdown() { + ReleaseBackBuffers(); + m_commandQueue.Reset(); m_swapChain.Reset(); + m_width = 0; + m_height = 0; } uint32_t D3D12SwapChain::GetCurrentBackBufferIndex() const { @@ -95,9 +92,16 @@ void D3D12SwapChain::Present(uint32_t syncInterval, uint32_t flags) { } void D3D12SwapChain::Resize(uint32_t width, uint32_t height) { - m_swapChain->ResizeBuffers(m_bufferCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0); + ReleaseBackBuffers(); + + const HRESULT hResult = m_swapChain->ResizeBuffers(m_bufferCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0); + if (FAILED(hResult)) { + return; + } + m_width = width; m_height = height; + RefreshBackBuffers(); } void* D3D12SwapChain::GetNativeHandle() { @@ -108,5 +112,34 @@ RHITexture* D3D12SwapChain::GetCurrentBackBuffer() { return &GetBackBuffer(GetCurrentBackBufferIndex()); } +bool D3D12SwapChain::RefreshBackBuffers() { + if (!m_swapChain || m_bufferCount == 0) { + return false; + } + + m_backBuffers.clear(); + m_backBuffers.resize(m_bufferCount); + + for (uint32_t i = 0; i < m_bufferCount; ++i) { + ComPtr resource; + const HRESULT hResult = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&resource)); + if (FAILED(hResult) || resource == nullptr) { + ReleaseBackBuffers(); + return false; + } + + m_backBuffers[i].InitializeFromExisting(resource.Get(), false); + } + + return true; +} + +void D3D12SwapChain::ReleaseBackBuffers() { + for (D3D12Texture& backBuffer : m_backBuffers) { + backBuffer.Shutdown(); + } + m_backBuffers.clear(); +} + } // namespace RHI } // namespace XCEngine diff --git a/tests/RHI/D3D12/unit/test_swap_chain.cpp b/tests/RHI/D3D12/unit/test_swap_chain.cpp index be8dd84f..e7946f8e 100644 --- a/tests/RHI/D3D12/unit/test_swap_chain.cpp +++ b/tests/RHI/D3D12/unit/test_swap_chain.cpp @@ -175,6 +175,30 @@ TEST_F(SwapChainTestFixture, SwapChain_Present_DoesNotCrash) { ASSERT_NO_FATAL_FAILURE(mSwapChain.Present(0, 0)); } +TEST_F(SwapChainTestFixture, SwapChain_Resize_RecreatesBackBuffers) { + ASSERT_TRUE(mSwapChain.Initialize( + mFactory.Get(), + mCommandQueue.Get(), + mHWND, + 800, + 600, + 2 + )); + + ID3D12Resource* beforeResize = mSwapChain.GetBackBuffer(0).GetResource(); + ASSERT_NE(beforeResize, nullptr); + EXPECT_EQ(mSwapChain.GetBackBuffer(0).GetWidth(), 800u); + EXPECT_EQ(mSwapChain.GetBackBuffer(0).GetHeight(), 600u); + + ASSERT_NO_FATAL_FAILURE(mSwapChain.Resize(1024, 768)); + + ID3D12Resource* afterResize = mSwapChain.GetBackBuffer(0).GetResource(); + ASSERT_NE(afterResize, nullptr); + EXPECT_EQ(mSwapChain.GetBackBuffer(0).GetWidth(), 1024u); + EXPECT_EQ(mSwapChain.GetBackBuffer(0).GetHeight(), 768u); + EXPECT_NE(beforeResize, afterResize); +} + TEST_F(SwapChainTestFixture, SwapChain_Shutdown_Cleanup) { ASSERT_TRUE(mSwapChain.Initialize( mFactory.Get(),