From 3cd3b04c7e9e6f77bb82b1e40b21b5b661a34995 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 20 Mar 2026 17:36:51 +0800 Subject: [PATCH] D3D12: Add Screenshot wrapper overload and document known limitations - Add D3D12Screenshot::Capture(D3D12Device&, D3D12CommandQueue&, D3D12Texture&, const char*) wrapper overload - Update minimal integration test to use encapsulated APIs - Add DescriptorHeap wrapper unit tests - Document minimal GetBuffer native call as known limitation in TEST_SPEC.md --- .../XCEngine/RHI/D3D12/D3D12Screenshot.h | 7 +++ engine/src/RHI/D3D12/D3D12Screenshot.cpp | 17 ++++++ tests/RHI/D3D12/TEST_SPEC.md | 16 ++++++ tests/RHI/D3D12/integration/minimal/main.cpp | 52 ++++++----------- tests/RHI/D3D12/unit/test_descriptor_heap.cpp | 56 +++++++++++++++++++ 5 files changed, 113 insertions(+), 35 deletions(-) diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12Screenshot.h b/engine/include/XCEngine/RHI/D3D12/D3D12Screenshot.h index 449449f9..0f8fb103 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12Screenshot.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12Screenshot.h @@ -6,7 +6,9 @@ namespace XCEngine { namespace RHI { +class D3D12Device; class D3D12CommandQueue; +class D3D12Texture; class D3D12Screenshot { public: @@ -17,6 +19,11 @@ public: uint32_t width, uint32_t height); + static bool Capture(D3D12Device& device, + D3D12CommandQueue& commandQueue, + D3D12Texture& texture, + const char* filename); + private: static bool CopyToReadbackAndSave(ID3D12Device* device, ID3D12CommandQueue* commandQueue, diff --git a/engine/src/RHI/D3D12/D3D12Screenshot.cpp b/engine/src/RHI/D3D12/D3D12Screenshot.cpp index 2b260b58..eb8dd145 100644 --- a/engine/src/RHI/D3D12/D3D12Screenshot.cpp +++ b/engine/src/RHI/D3D12/D3D12Screenshot.cpp @@ -1,4 +1,7 @@ #include "RHI/D3D12/D3D12Screenshot.h" +#include "RHI/D3D12/D3D12Device.h" +#include "RHI/D3D12/D3D12CommandQueue.h" +#include "RHI/D3D12/D3D12Texture.h" #include "Debug/Logger.h" #include #include @@ -15,6 +18,20 @@ bool D3D12Screenshot::Capture(ID3D12Device* device, return CopyToReadbackAndSave(device, commandQueue, renderTarget, filename, width, height); } +bool D3D12Screenshot::Capture(D3D12Device& device, + D3D12CommandQueue& commandQueue, + D3D12Texture& texture, + const char* filename) { + return Capture( + device.GetDevice(), + commandQueue.GetCommandQueue(), + texture.GetResource(), + filename, + texture.GetWidth(), + texture.GetHeight() + ); +} + bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device, ID3D12CommandQueue* commandQueue, ID3D12Resource* renderTarget, diff --git a/tests/RHI/D3D12/TEST_SPEC.md b/tests/RHI/D3D12/TEST_SPEC.md index b5420e71..c41020fc 100644 --- a/tests/RHI/D3D12/TEST_SPEC.md +++ b/tests/RHI/D3D12/TEST_SPEC.md @@ -290,6 +290,22 @@ engine/ **状态**: 待修复 +### 7.2 minimal GetBuffer 原生调用 + +**问题**: `minimal/main.cpp` 第 127-129 行仍使用原生 D3D12 API: + +```cpp +ID3D12Resource* buffer = nullptr; +gSwapChain.GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&buffer)); +gColorRTs[i].InitializeFromExisting(buffer); +``` + +**原因**: SwapChain back buffer 获取后需要通过 `InitializeFromExisting()` 绑定到已有的 `D3D12Texture` 对象。当前没有优雅的封装方案保留此语义。 + +**影响**: minimal 集成测试仍包含少量原生 D3D12 代码 + +**状态**: 标记为已知限制,暂不修复 + --- ## 8. 规范更新记录 diff --git a/tests/RHI/D3D12/integration/minimal/main.cpp b/tests/RHI/D3D12/integration/minimal/main.cpp index b906277d..cde9552f 100644 --- a/tests/RHI/D3D12/integration/minimal/main.cpp +++ b/tests/RHI/D3D12/integration/minimal/main.cpp @@ -105,26 +105,8 @@ bool InitD3D12() { return false; } - // Create swap chain - DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; - swapChainDesc.BufferCount = 2; - swapChainDesc.BufferDesc.Width = gWidth; - swapChainDesc.BufferDesc.Height = gHeight; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.OutputWindow = gHWND; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.Windowed = true; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - - IDXGISwapChain* dxgiSwapChain = nullptr; - HRESULT hr = factory->CreateSwapChain(gCommandQueue.GetCommandQueue(), &swapChainDesc, &dxgiSwapChain); - if (FAILED(hr)) { - Log("[ERROR] Failed to create swap chain"); - return false; - } - - if (!gSwapChain.Initialize(dxgiSwapChain, (uint32_t)gWidth, (uint32_t)gHeight)) { + // Create swap chain using encapsulated interface + if (!gSwapChain.Initialize(factory, gCommandQueue.GetCommandQueue(), gHWND, gWidth, gHeight, 2)) { Log("[ERROR] Failed to initialize swap chain"); return false; } @@ -140,21 +122,22 @@ bool InitD3D12() { gDSVHeap.Initialize(device, DescriptorHeapType::DSV, 1); gDSVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV); - // Create RTVs for back buffers - D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapStart = gRTVHeap.GetCPUDescriptorHandleForHeapStart(); + // Create RTVs for back buffers using encapsulated interface for (int i = 0; i < 2; i++) { ID3D12Resource* buffer = nullptr; gSwapChain.GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&buffer)); gColorRTs[i].InitializeFromExisting(buffer); - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle; - rtvHandle.ptr = rtvHeapStart.ptr + i * gRTVDescriptorSize; + CPUDescriptorHandle rtvCpuHandle = gRTVHeap.GetCPUDescriptorHandle(i); + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr }; gRTVs[i].InitializeAt(device, gColorRTs[i].GetResource(), rtvHandle, nullptr); } // Create DSV D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12DepthStencilView::CreateDesc(Format::D24_UNorm_S8_UInt); - gDSV.InitializeAt(device, gDepthStencil.GetResource(), gDSVHeap.GetCPUDescriptorHandleForHeapStart(), &dsvDesc); + CPUDescriptorHandle dsvCpuHandle = gDSVHeap.GetCPUDescriptorHandle(0); + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { dsvCpuHandle.ptr }; + gDSV.InitializeAt(device, gDepthStencil.GetResource(), dsvHandle, &dsvDesc); // Create command allocator and list gCommandAllocator.Initialize(device, CommandQueueType::Direct); @@ -184,10 +167,11 @@ void BeginRender() { gCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(), ResourceStates::Present, ResourceStates::RenderTarget); - // Set render targets - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle; - rtvHandle.ptr = gRTVHeap.GetCPUDescriptorHandleForHeapStart().ptr + gCurrentRTIndex * gRTVDescriptorSize; - D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = gDSVHeap.GetCPUDescriptorHandleForHeapStart(); + // Set render targets using encapsulated interface + CPUDescriptorHandle rtvCpuHandle = gRTVHeap.GetCPUDescriptorHandle(gCurrentRTIndex); + CPUDescriptorHandle dsvCpuHandle = gDSVHeap.GetCPUDescriptorHandle(0); + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr }; + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { dsvCpuHandle.ptr }; gCommandList.SetRenderTargetsHandle(1, &rtvHandle, &dsvHandle); @@ -289,12 +273,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine WaitForGPU(); Log("[INFO] GPU idle, taking screenshot..."); bool screenshotResult = D3D12Screenshot::Capture( - gDevice.GetDevice(), - gCommandQueue.GetCommandQueue(), - gColorRTs[gCurrentRTIndex].GetResource(), - "minimal.ppm", - gWidth, - gHeight + gDevice, + gCommandQueue, + gColorRTs[gCurrentRTIndex], + "minimal.ppm" ); if (screenshotResult) { Log("[INFO] Screenshot saved to minimal.ppm"); diff --git a/tests/RHI/D3D12/unit/test_descriptor_heap.cpp b/tests/RHI/D3D12/unit/test_descriptor_heap.cpp index dbc6ddc3..48d353f0 100644 --- a/tests/RHI/D3D12/unit/test_descriptor_heap.cpp +++ b/tests/RHI/D3D12/unit/test_descriptor_heap.cpp @@ -1,4 +1,60 @@ #include "fixtures/D3D12TestFixture.h" +#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h" + +using namespace XCEngine::RHI; + +TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_Initialize_RTV) { + D3D12DescriptorHeap heap; + bool result = heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4); + ASSERT_TRUE(result); + EXPECT_EQ(heap.GetDescriptorCount(), 4u); + EXPECT_EQ(heap.GetType(), DescriptorHeapType::RTV); + heap.Shutdown(); +} + +TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetCPUDescriptorHandle) { + D3D12DescriptorHeap heap; + ASSERT_TRUE(heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4)); + + uint32_t descriptorSize = heap.GetDescriptorSize(); + CPUDescriptorHandle handle0 = heap.GetCPUDescriptorHandle(0); + CPUDescriptorHandle handle1 = heap.GetCPUDescriptorHandle(1); + CPUDescriptorHandle handle2 = heap.GetCPUDescriptorHandle(2); + + EXPECT_EQ(handle1.ptr - handle0.ptr, descriptorSize); + EXPECT_EQ(handle2.ptr - handle1.ptr, descriptorSize); + + heap.Shutdown(); +} + +TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetGPUDescriptorHandle) { + D3D12DescriptorHeap heap; + ASSERT_TRUE(heap.Initialize(GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 4, true)); + + uint32_t descriptorSize = heap.GetDescriptorSize(); + GPUDescriptorHandle handle0 = heap.GetGPUDescriptorHandle(0); + GPUDescriptorHandle handle1 = heap.GetGPUDescriptorHandle(1); + + EXPECT_EQ(handle1.ptr - handle0.ptr, descriptorSize); + + heap.Shutdown(); +} + +TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_GetDescriptorSize) { + D3D12DescriptorHeap rtvHeap; + ASSERT_TRUE(rtvHeap.Initialize(GetDevice(), DescriptorHeapType::RTV, 4)); + EXPECT_GT(rtvHeap.GetDescriptorSize(), 0u); + + D3D12DescriptorHeap cbvHeap; + ASSERT_TRUE(cbvHeap.Initialize(GetDevice(), DescriptorHeapType::CBV_SRV_UAV, 4)); + EXPECT_GT(cbvHeap.GetDescriptorSize(), 0u); +} + +TEST_F(D3D12TestFixture, DescriptorHeap_Wrapper_Shutdown) { + D3D12DescriptorHeap heap; + ASSERT_TRUE(heap.Initialize(GetDevice(), DescriptorHeapType::RTV, 2)); + heap.Shutdown(); +} TEST_F(D3D12TestFixture, DescriptorHeap_Create_CBV_SRV_UAV) { D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};