fix: 修复截图保存功能和CreateCommittedResource参数问题

- 修复SaveScreenshot函数中D3D12_RESOURCE_DESC的初始化
- 添加完整的SampleDesc和Layout字段
- 修复从PRESENT状态正确转换到COPY_SOURCE进行读取
- 启用D3D12 Debug Layer以获取更好的调试信息
- 添加日志输出到文件以便捕获调试信息
This commit is contained in:
2026-03-15 12:51:18 +08:00
parent 1a5bcd75d9
commit 3d285fa98a
2 changed files with 117 additions and 32 deletions

View File

@@ -1,4 +1,5 @@
#include "XCEngine/RHI/D3D12/D3D12Device.h"
#include <stdio.h>
#ifdef _DEBUG
#include <dxgidebug.h>
@@ -75,8 +76,7 @@ void D3D12Device::Shutdown() {
bool D3D12Device::CreateDXGIFactory(bool enableDebugLayer) {
UINT dxgiFactoryFlags = 0;
#ifdef _DEBUG
if (enableDebugLayer) {
{
ID3D12Debug* debugController = nullptr;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
debugController->EnableDebugLayer();
@@ -84,14 +84,19 @@ bool D3D12Device::CreateDXGIFactory(bool enableDebugLayer) {
debugController->Release();
}
}
#endif
HRESULT hr = CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&m_factory));
return SUCCEEDED(hr);
}
bool D3D12Device::CreateDevice(IDXGIAdapter1* adapter) {
OutputDebugStringA("[DEBUG] CreateDevice: start\n");
HRESULT hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr)) {
char buf[256];
sprintf(buf, "[DEBUG] CreateDevice: D3D12CreateDevice failed! hr=%08X\n", hr);
OutputDebugStringA(buf);
}
return SUCCEEDED(hr);
}

View File

@@ -6,6 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdarg.h>
#include <algorithm>
#include <unordered_map>
#include <string>
@@ -25,6 +26,22 @@ using namespace XCEngine::RHI;
#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib,"winmm.lib")
static FILE* gLogFile = nullptr;
void Log(const char* format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
if (gLogFile) {
va_start(args, format);
vfprintf(gLogFile, format, args);
va_end(args);
fflush(gLogFile);
}
}
//=================================================================================
// D3D12 核心全局对象 (最小渲染所需)
//=================================================================================
@@ -285,7 +302,7 @@ void CreateShaderFromFile(
if (FAILED(hResult)) {
char szLog[1024] = { 0 };
strcpy_s(szLog, (char*)errorBuffer->GetBufferPointer());
printf("CreateShaderFromFile error : [%s][%s]:[%s]\n", inMainFunctionName, inTarget, szLog);
Log("CreateShaderFromFile error : [%s][%s]:[%s]\n", inMainFunctionName, inTarget, szLog);
errorBuffer->Release();
return;
}
@@ -726,31 +743,48 @@ void SwapD3D12Buffers() {
// 使用 Readback 方式读取渲染目标并保存为 PPM 格式
//=================================================================================
bool SaveScreenshot(const char* filename, int width, int height) {
ID3D12Device* device = gDevice.GetDevice();
ID3D12CommandQueue* queue = gCommandQueue.GetCommandQueue();
int currentRTIndex = gSwapChain->GetCurrentBackBufferIndex();
Log("[DEBUG] SaveScreenshot: start\n");
XCEngine::RHI::D3D12Device& deviceWrapper = gDevice;
ID3D12Device* device = deviceWrapper.GetDevice();
Log("[DEBUG] SaveScreenshot: device = %p\n", device);
IDXGISwapChain3* swapChain = gSwapChain;
// Wait for previous frame to finish
Log("[DEBUG] SaveScreenshot: waiting for GPU\n");
WaitForCompletionOfCommandList();
// Get current back buffer index
int currentRTIndex = swapChain->GetCurrentBackBufferIndex();
Log("[DEBUG] SaveScreenshot: currentRTIndex = %d\n", currentRTIndex);
// Use existing render target
ID3D12Resource* renderTarget = gColorRTs[currentRTIndex];
D3D12_RESOURCE_DESC desc = renderTarget->GetDesc();
Log("[DEBUG] SaveScreenshot: renderTarget = %p\n", renderTarget);
// Need to transition from PRESENT to COPY_SOURCE for reading
HRESULT hr = S_OK;
ID3D12CommandAllocator* cmdAlloc = nullptr;
hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
D3D12_RESOURCE_DESC readbackDesc = {};
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
readbackDesc.Width = width * height * 4;
readbackDesc.Alignment = 0;
readbackDesc.Width = (UINT64)width * height * 4;
readbackDesc.Height = 1;
readbackDesc.DepthOrArraySize = 1;
readbackDesc.MipLevels = 1;
readbackDesc.Format = DXGI_FORMAT_UNKNOWN;
readbackDesc.SampleDesc.Count = 1;
readbackDesc.SampleDesc.Quality = 0;
readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
readbackDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* readbackBuffer = nullptr;
HRESULT hr = device->CreateCommittedResource(
hr = device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&readbackDesc,
@@ -759,17 +793,31 @@ bool SaveScreenshot(const char* filename, int width, int height) {
IID_PPV_ARGS(&readbackBuffer));
if (FAILED(hr)) {
Log("[DEBUG] SaveScreenshot: CreateCommittedResource failed! hr=%08X\n", hr);
return false;
}
Log("[DEBUG] SaveScreenshot: created readback buffer\n");
hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
if (FAILED(hr)) {
Log("[DEBUG] SaveScreenshot: CreateCommandAllocator failed! hr=%08X\n", hr);
readbackBuffer->Release();
return false;
}
ID3D12CommandAllocator* cmdAlloc = nullptr;
device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
ID3D12GraphicsCommandList* cmdList = nullptr;
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)) {
Log("[DEBUG] SaveScreenshot: CreateCommandList failed! hr=%08X\n", hr);
cmdAlloc->Release();
readbackBuffer->Release();
return false;
}
Log("[DEBUG] SaveScreenshot: copying resource\n");
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = renderTarget;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
@@ -784,28 +832,45 @@ bool SaveScreenshot(const char* filename, int width, int height) {
cmdList->Close();
ID3D12CommandList* ppCmdLists[] = { cmdList };
ID3D12CommandQueue* queue = gCommandQueue.GetCommandQueue();
queue->ExecuteCommandLists(1, ppCmdLists);
gFence.Initialize(device, 0);
gFence.Signal(1);
gFence.Wait(1);
Log("[DEBUG] SaveScreenshot: waiting for GPU copy\n");
gFenceValue += 1;
queue->Signal(gFence.GetFence(), gFenceValue);
gFence.Wait(gFenceValue);
D3D12_RANGE readRange = { 0, width * height * 4 };
Log("[DEBUG] SaveScreenshot: mapping memory\n");
D3D12_RANGE readRange = { 0, 0 };
unsigned char* mappedData = nullptr;
readbackBuffer->Map(0, &readRange, (void**)&mappedData);
FILE* fp = fopen(filename, "wb");
if (!fp) {
readbackBuffer->Release();
hr = readbackBuffer->Map(0, &readRange, (void**)&mappedData);
if (FAILED(hr)) {
Log("[DEBUG] SaveScreenshot: Map failed! hr=%08X\n", hr);
cmdList->Release();
cmdAlloc->Release();
readbackBuffer->Release();
return false;
}
D3D12_RESOURCE_DESC desc = readbackBuffer->GetDesc();
UINT rowPitch = width * 4;
Log("[DEBUG] SaveScreenshot: rowPitch = %u\n", rowPitch);
Log("[DEBUG] SaveScreenshot: writing file\n");
FILE* fp = fopen(filename, "wb");
if (!fp) {
Log("[DEBUG] SaveScreenshot: fopen failed!\n");
readbackBuffer->Unmap(0, nullptr);
cmdList->Release();
cmdAlloc->Release();
readbackBuffer->Release();
return false;
}
fprintf(fp, "P6\n%d %d\n255\n", width, height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int idx = (y * width + x) * 4;
int idx = y * rowPitch + x * 4;
unsigned char r = mappedData[idx + 2];
unsigned char g = mappedData[idx + 1];
unsigned char b = mappedData[idx + 0];
@@ -819,10 +884,11 @@ bool SaveScreenshot(const char* filename, int width, int height) {
D3D12_RANGE writeRange = { 0, 0 };
readbackBuffer->Unmap(0, &writeRange);
readbackBuffer->Release();
cmdList->Release();
cmdAlloc->Release();
readbackBuffer->Release();
Log("[DEBUG] SaveScreenshot: done!\n");
return true;
}
@@ -855,6 +921,12 @@ LRESULT CALLBACK WindowProc(HWND inHWND, UINT inMSG, WPARAM inWParam, LPARAM inL
// 程序入口点
//=================================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int inShowCmd) {
fopen_s(&gLogFile, "D3D12_log.txt", "w");
AllocConsole();
freopen("CONOUT$", "w", stdout);
Log("[DEBUG] D3D12 Test Application Started\n");
WNDCLASSEX wndClassEx;
wndClassEx.cbSize = sizeof(WNDCLASSEX);
wndClassEx.style = CS_HREDRAW | CS_VREDRAW;
@@ -989,9 +1061,14 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
frameCount++;
frameCount++;
if (frameCount == 2) {
SaveScreenshot("screenshot.png", 1280, 720);
Log("[DEBUG] Saving screenshot at frame %d...\n", frameCount);
if (SaveScreenshot("screenshot.ppm", 1280, 720)) {
Log("[DEBUG] Screenshot saved to screenshot.ppm\n");
} else {
Log("[DEBUG] Failed to save screenshot!\n");
}
}
WaitForCompletionOfCommandList();
DWORD current_time = timeGetTime();
@@ -1018,5 +1095,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
SwapD3D12Buffers();
}
}
if (gLogFile) {
fclose(gLogFile);
}
return 0;
}