D3D12: Fix Quad test screenshot and update texture

- Fix SetGraphicsRootDescriptorTable call order (SetRootSignature before SetPipelineState)
- Rename texture earth_d.jpg -> earth.png
- Fix vertex order for TriangleStrip quad rendering
- Add integration test README
- Add debug logging to D3D12Screenshot
- Remove obsolete run.bat
This commit is contained in:
2026-03-21 15:54:15 +08:00
parent c563db3123
commit a6c6482125
7 changed files with 66 additions and 21 deletions

View File

@@ -4,6 +4,7 @@
#include "RHI/D3D12/D3D12Texture.h" #include "RHI/D3D12/D3D12Texture.h"
#include "Debug/Logger.h" #include "Debug/Logger.h"
#include <d3d12.h> #include <d3d12.h>
#include <dxgi.h>
#include <stdio.h> #include <stdio.h>
namespace XCEngine { namespace XCEngine {
@@ -65,6 +66,13 @@ bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device,
ID3D12CommandAllocator* cmdAlloc = nullptr; ID3D12CommandAllocator* cmdAlloc = nullptr;
HRESULT hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); HRESULT hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
if (FAILED(hr)) { if (FAILED(hr)) {
XCEngine::Debug::Logger::Get().Error(XCEngine::Debug::LogCategory::Rendering, "Screenshot: CreateCommandAllocator failed");
if (hr == DXGI_ERROR_DEVICE_REMOVED) {
HRESULT removedReason = device->GetDeviceRemovedReason();
char reasonMsg[256];
sprintf_s(reasonMsg, sizeof(reasonMsg), "Screenshot: Device removed reason: 0x%X", removedReason);
XCEngine::Debug::Logger::Get().Error(XCEngine::Debug::LogCategory::Rendering, reasonMsg);
}
return false; return false;
} }
@@ -93,6 +101,7 @@ bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device,
IID_PPV_ARGS(&readbackBuffer)); IID_PPV_ARGS(&readbackBuffer));
if (FAILED(hr)) { if (FAILED(hr)) {
XCEngine::Debug::Logger::Get().Error(XCEngine::Debug::LogCategory::Rendering, "Screenshot: CreateCommittedResource failed");
cmdAlloc->Release(); cmdAlloc->Release();
return false; return false;
} }
@@ -100,6 +109,7 @@ bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device,
ID3D12GraphicsCommandList* cmdList = nullptr; ID3D12GraphicsCommandList* cmdList = nullptr;
hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList)); hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList));
if (FAILED(hr)) { if (FAILED(hr)) {
XCEngine::Debug::Logger::Get().Error(XCEngine::Debug::LogCategory::Rendering, "Screenshot: CreateCommandList failed");
cmdAlloc->Release(); cmdAlloc->Release();
readbackBuffer->Release(); readbackBuffer->Release();
return false; return false;
@@ -140,11 +150,21 @@ bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device,
cmdList->Close(); cmdList->Close();
ID3D12CommandList* ppCmdLists[] = { cmdList }; ID3D12CommandList* ppCmdLists[] = { cmdList };
commandQueue->ExecuteCommandLists(1, ppCmdLists); commandQueue->ExecuteCommandLists(1, ppCmdLists);
XCEngine::Debug::Logger::Get().Info(XCEngine::Debug::LogCategory::Rendering, "Screenshot: ExecuteCommandLists done, waiting for fence...");
HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
ID3D12Fence* fence = nullptr; ID3D12Fence* fence = nullptr;
hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
if (SUCCEEDED(hr)) { if (FAILED(hr)) {
XCEngine::Debug::Logger::Get().Error(XCEngine::Debug::LogCategory::Rendering, "Screenshot: CreateFence failed");
cmdList->Release();
cmdAlloc->Release();
readbackBuffer->Release();
CloseHandle(fenceEvent);
return false;
}
XCEngine::Debug::Logger::Get().Info(XCEngine::Debug::LogCategory::Rendering, "Screenshot: Fence created, waiting...");
{
UINT64 fenceValue = 1; UINT64 fenceValue = 1;
commandQueue->Signal(fence, fenceValue); commandQueue->Signal(fence, fenceValue);
if (fence->GetCompletedValue() < fenceValue) { if (fence->GetCompletedValue() < fenceValue) {

View File

@@ -8,4 +8,5 @@ enable_testing()
add_subdirectory(minimal) add_subdirectory(minimal)
add_subdirectory(triangle) add_subdirectory(triangle)
add_subdirectory(quad)
add_subdirectory(render_model) add_subdirectory(render_model)

View File

@@ -0,0 +1,36 @@
# D3D12 Integration Tests
## minimal
**后端**: D3D12Device, DXGIFactory, CommandQueue, CommandList, SwapChain
**内容**: 最小 D3D12 初始化流程,创建窗口和交换链,无渲染输出
---
## triangle
**后端**: D3D12Device, DXGIFactory, CommandQueue, CommandList, SwapChain, Buffer, Shader, RootSignature, PipelineState, RenderTargetView, DepthStencilView, Screenshot
**内容**:
- 基础三角形渲染
- 顶点 buffer 上传
- HLSL shader 编译 (vs_5_1, ps_5_1)
- Root signature + PSO 创建
- RenderTarget 切换 (Present ↔ RenderTarget)
- 截图功能验证
---
## quad
**后端**: triangle 的全部 + Texture, DescriptorHeap, ShaderResourceView
**内容**:
- 四边形纹理采样渲染
- Texture 加载与初始化 (stb_image)
- DescriptorHeap (CBV_SRV_UAV) + SRV 创建
- 静态采样器配置
- Root signature descriptor table
- SetDescriptorHeaps + SetGraphicsRootDescriptorTable
- 截图功能验证
---
## render_model
**后端**: (TODO)
**内容**: 模型渲染 (待实现)

View File

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 189 KiB

View File

@@ -95,8 +95,6 @@ bool LoadTexture(const char* filename, D3D12Texture& texture, D3D12ShaderResourc
return false; return false;
} }
Log("[INFO] Loaded texture %s: %dx%d", filename, width, height);
allocator.Reset(); allocator.Reset();
commandList->Reset(allocator.GetCommandAllocator(), nullptr); commandList->Reset(allocator.GetCommandAllocator(), nullptr);
@@ -112,9 +110,8 @@ bool LoadTexture(const char* filename, D3D12Texture& texture, D3D12ShaderResourc
queue.WaitForIdle(); queue.WaitForIdle();
texture.SetName(filename); texture.SetName(filename);
stbi_image_free(pixels);
srvHeap.Initialize(device, DescriptorHeapType::CBV_SRV_UAV, 1); srvHeap.Initialize(device, DescriptorHeapType::CBV_SRV_UAV, 1, true);
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = D3D12ShaderResourceView::CreateDesc(Format::R8G8B8A8_UNorm, D3D12_SRV_DIMENSION_TEXTURE2D); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = D3D12ShaderResourceView::CreateDesc(Format::R8G8B8A8_UNorm, D3D12_SRV_DIMENSION_TEXTURE2D);
srv.InitializeAt(device, texture.GetResource(), srvHeap.GetCPUDescriptorHandleForHeapStart(), &srvDesc); srv.InitializeAt(device, texture.GetResource(), srvHeap.GetCPUDescriptorHandleForHeapStart(), &srvDesc);
@@ -263,8 +260,8 @@ bool InitD3D12() {
Vertex vertices[] = { Vertex vertices[] = {
{ { -0.5f, -0.5f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 0.0f } }, { { -0.5f, -0.5f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 0.0f } },
{ { -0.5f, 0.5f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f, 0.0f } }, { { -0.5f, 0.5f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f, 0.0f } },
{ { 0.5f, 0.5f, 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 0.0f } },
{ { 0.5f, -0.5f, 0.0f, 1.0f }, { 1.0f, 1.0f, 0.0f, 0.0f } }, { { 0.5f, -0.5f, 0.0f, 1.0f }, { 1.0f, 1.0f, 0.0f, 0.0f } },
{ { 0.5f, 0.5f, 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 0.0f } },
}; };
if (!gVertexBuffer.InitializeWithData(device, gCommandList.GetCommandList(), vertices, sizeof(vertices), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)) { if (!gVertexBuffer.InitializeWithData(device, gCommandList.GetCommandList(), vertices, sizeof(vertices), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)) {
@@ -274,7 +271,7 @@ bool InitD3D12() {
gVertexBuffer.SetStride(sizeof(Vertex)); gVertexBuffer.SetStride(sizeof(Vertex));
gVertexBuffer.SetBufferType(BufferType::Vertex); gVertexBuffer.SetBufferType(BufferType::Vertex);
if (!LoadTexture("Res/Image/earth_d.jpg", gDiffuseTexture, gDiffuseSRV, device, gSRVHeap, gCommandList.GetCommandList(), gCommandAllocator, gCommandQueue)) { if (!LoadTexture("Res/Image/earth.png", gDiffuseTexture, gDiffuseSRV, device, gSRVHeap, gCommandList.GetCommandList(), gCommandAllocator, gCommandQueue)) {
Log("[ERROR] Failed to load texture"); Log("[ERROR] Failed to load texture");
return false; return false;
} }
@@ -384,24 +381,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
BeginRender(); BeginRender();
gCommandList.SetRootSignature(gRootSignature.GetRootSignature());
gCommandList.SetPipelineState(gPipelineState.GetPipelineState());
ID3D12DescriptorHeap* heaps[] = { gSRVHeap.GetDescriptorHeap() }; ID3D12DescriptorHeap* heaps[] = { gSRVHeap.GetDescriptorHeap() };
gCommandList.SetDescriptorHeaps(1, heaps); gCommandList.SetDescriptorHeaps(1, heaps);
gCommandList.SetGraphicsRootDescriptorTable(0, gSRVHeap.GetGPUDescriptorHandleForHeapStart()); gCommandList.SetGraphicsRootDescriptorTable(0, gSRVHeap.GetGPUDescriptorHandleForHeapStart());
gCommandList.SetPrimitiveTopology(PrimitiveTopology::TriangleStrip);
gCommandList.SetPipelineState(gPipelineState.GetPipelineState());
gCommandList.SetRootSignature(gRootSignature.GetRootSignature());
gCommandList.SetPrimitiveTopology(PrimitiveTopology::TriangleList);
gCommandList.SetVertexBuffer(0, gVertexBuffer.GetResource(), 0, gVertexBuffer.GetStride()); gCommandList.SetVertexBuffer(0, gVertexBuffer.GetResource(), 0, gVertexBuffer.GetStride());
gCommandList.Draw(4, 1, 0, 0); gCommandList.Draw(4, 1, 0, 0);
frameCount++; frameCount++;
if (frameCount >= targetFrameCount) { if (frameCount >= targetFrameCount) {
Log("[INFO] Reached target frame count %d - taking screenshot!", targetFrameCount);
ExecuteCommandList(); ExecuteCommandList();
WaitForGPU(); WaitForGPU();
Log("[INFO] GPU idle, taking screenshot...");
bool screenshotResult = D3D12Screenshot::Capture( bool screenshotResult = D3D12Screenshot::Capture(
gDevice, gDevice,
gCommandQueue, gCommandQueue,

View File

@@ -1,7 +0,0 @@
@echo off
cd /d "%~dp0..\..\..\..\build\tests\RHI\D3D12\integration\Debug"
if exist "D3D12_engine_log.txt" del "D3D12_engine_log.txt"
if exist "screenshot.ppm" del "screenshot.ppm"
D3D12.exe
python "%~dp0compare_ppm.py" "screenshot.ppm" "GT.ppm" 5
pause