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
This commit is contained in:
@@ -6,7 +6,9 @@
|
|||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace RHI {
|
namespace RHI {
|
||||||
|
|
||||||
|
class D3D12Device;
|
||||||
class D3D12CommandQueue;
|
class D3D12CommandQueue;
|
||||||
|
class D3D12Texture;
|
||||||
|
|
||||||
class D3D12Screenshot {
|
class D3D12Screenshot {
|
||||||
public:
|
public:
|
||||||
@@ -17,6 +19,11 @@ public:
|
|||||||
uint32_t width,
|
uint32_t width,
|
||||||
uint32_t height);
|
uint32_t height);
|
||||||
|
|
||||||
|
static bool Capture(D3D12Device& device,
|
||||||
|
D3D12CommandQueue& commandQueue,
|
||||||
|
D3D12Texture& texture,
|
||||||
|
const char* filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool CopyToReadbackAndSave(ID3D12Device* device,
|
static bool CopyToReadbackAndSave(ID3D12Device* device,
|
||||||
ID3D12CommandQueue* commandQueue,
|
ID3D12CommandQueue* commandQueue,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#include "RHI/D3D12/D3D12Screenshot.h"
|
#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 "Debug/Logger.h"
|
||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -15,6 +18,20 @@ bool D3D12Screenshot::Capture(ID3D12Device* device,
|
|||||||
return CopyToReadbackAndSave(device, commandQueue, renderTarget, filename, width, height);
|
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,
|
bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device,
|
||||||
ID3D12CommandQueue* commandQueue,
|
ID3D12CommandQueue* commandQueue,
|
||||||
ID3D12Resource* renderTarget,
|
ID3D12Resource* renderTarget,
|
||||||
|
|||||||
@@ -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. 规范更新记录
|
## 8. 规范更新记录
|
||||||
|
|||||||
@@ -105,26 +105,8 @@ bool InitD3D12() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create swap chain
|
// Create swap chain using encapsulated interface
|
||||||
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
|
if (!gSwapChain.Initialize(factory, gCommandQueue.GetCommandQueue(), gHWND, gWidth, gHeight, 2)) {
|
||||||
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)) {
|
|
||||||
Log("[ERROR] Failed to initialize swap chain");
|
Log("[ERROR] Failed to initialize swap chain");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -140,21 +122,22 @@ bool InitD3D12() {
|
|||||||
gDSVHeap.Initialize(device, DescriptorHeapType::DSV, 1);
|
gDSVHeap.Initialize(device, DescriptorHeapType::DSV, 1);
|
||||||
gDSVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV);
|
gDSVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV);
|
||||||
|
|
||||||
// Create RTVs for back buffers
|
// Create RTVs for back buffers using encapsulated interface
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapStart = gRTVHeap.GetCPUDescriptorHandleForHeapStart();
|
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
ID3D12Resource* buffer = nullptr;
|
ID3D12Resource* buffer = nullptr;
|
||||||
gSwapChain.GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&buffer));
|
gSwapChain.GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&buffer));
|
||||||
gColorRTs[i].InitializeFromExisting(buffer);
|
gColorRTs[i].InitializeFromExisting(buffer);
|
||||||
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle;
|
CPUDescriptorHandle rtvCpuHandle = gRTVHeap.GetCPUDescriptorHandle(i);
|
||||||
rtvHandle.ptr = rtvHeapStart.ptr + i * gRTVDescriptorSize;
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr };
|
||||||
gRTVs[i].InitializeAt(device, gColorRTs[i].GetResource(), rtvHandle, nullptr);
|
gRTVs[i].InitializeAt(device, gColorRTs[i].GetResource(), rtvHandle, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create DSV
|
// Create DSV
|
||||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12DepthStencilView::CreateDesc(Format::D24_UNorm_S8_UInt);
|
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
|
// Create command allocator and list
|
||||||
gCommandAllocator.Initialize(device, CommandQueueType::Direct);
|
gCommandAllocator.Initialize(device, CommandQueueType::Direct);
|
||||||
@@ -184,10 +167,11 @@ void BeginRender() {
|
|||||||
gCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(),
|
gCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(),
|
||||||
ResourceStates::Present, ResourceStates::RenderTarget);
|
ResourceStates::Present, ResourceStates::RenderTarget);
|
||||||
|
|
||||||
// Set render targets
|
// Set render targets using encapsulated interface
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle;
|
CPUDescriptorHandle rtvCpuHandle = gRTVHeap.GetCPUDescriptorHandle(gCurrentRTIndex);
|
||||||
rtvHandle.ptr = gRTVHeap.GetCPUDescriptorHandleForHeapStart().ptr + gCurrentRTIndex * gRTVDescriptorSize;
|
CPUDescriptorHandle dsvCpuHandle = gDSVHeap.GetCPUDescriptorHandle(0);
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = gDSVHeap.GetCPUDescriptorHandleForHeapStart();
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr };
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { dsvCpuHandle.ptr };
|
||||||
|
|
||||||
gCommandList.SetRenderTargetsHandle(1, &rtvHandle, &dsvHandle);
|
gCommandList.SetRenderTargetsHandle(1, &rtvHandle, &dsvHandle);
|
||||||
|
|
||||||
@@ -289,12 +273,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
WaitForGPU();
|
WaitForGPU();
|
||||||
Log("[INFO] GPU idle, taking screenshot...");
|
Log("[INFO] GPU idle, taking screenshot...");
|
||||||
bool screenshotResult = D3D12Screenshot::Capture(
|
bool screenshotResult = D3D12Screenshot::Capture(
|
||||||
gDevice.GetDevice(),
|
gDevice,
|
||||||
gCommandQueue.GetCommandQueue(),
|
gCommandQueue,
|
||||||
gColorRTs[gCurrentRTIndex].GetResource(),
|
gColorRTs[gCurrentRTIndex],
|
||||||
"minimal.ppm",
|
"minimal.ppm"
|
||||||
gWidth,
|
|
||||||
gHeight
|
|
||||||
);
|
);
|
||||||
if (screenshotResult) {
|
if (screenshotResult) {
|
||||||
Log("[INFO] Screenshot saved to minimal.ppm");
|
Log("[INFO] Screenshot saved to minimal.ppm");
|
||||||
|
|||||||
@@ -1,4 +1,60 @@
|
|||||||
#include "fixtures/D3D12TestFixture.h"
|
#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) {
|
TEST_F(D3D12TestFixture, DescriptorHeap_Create_CBV_SRV_UAV) {
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
|
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user