feat: 添加截图保存Debug工具
- 添加SaveScreenshot函数使用D3D12 Readback方式读取渲染目标 - 保存为PPM格式(在第2帧自动保存到screenshot.ppm) - 程序可自行测试渲染结果
This commit is contained in:
@@ -721,6 +721,111 @@ void SwapD3D12Buffers() {
|
|||||||
gSwapChain->Present(0, 0);
|
gSwapChain->Present(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=================================================================================
|
||||||
|
// 截图保存 Debug 工具
|
||||||
|
// 使用 Readback 方式读取渲染目标并保存为 PPM 格式
|
||||||
|
//=================================================================================
|
||||||
|
bool SaveScreenshot(const char* filename, int width, int height) {
|
||||||
|
ID3D12Device* device = gDevice.GetDevice();
|
||||||
|
ID3D12CommandQueue* queue = gCommandQueue.GetCommandQueue();
|
||||||
|
int currentRTIndex = gSwapChain->GetCurrentBackBufferIndex();
|
||||||
|
|
||||||
|
ID3D12Resource* renderTarget = gColorRTs[currentRTIndex];
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC desc = renderTarget->GetDesc();
|
||||||
|
|
||||||
|
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.Height = 1;
|
||||||
|
readbackDesc.DepthOrArraySize = 1;
|
||||||
|
readbackDesc.MipLevels = 1;
|
||||||
|
readbackDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||||
|
readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||||
|
readbackDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||||
|
|
||||||
|
ID3D12Resource* readbackBuffer = nullptr;
|
||||||
|
HRESULT hr = device->CreateCommittedResource(
|
||||||
|
&heapProps,
|
||||||
|
D3D12_HEAP_FLAG_NONE,
|
||||||
|
&readbackDesc,
|
||||||
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&readbackBuffer));
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
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));
|
||||||
|
|
||||||
|
D3D12_RESOURCE_BARRIER barrier = {};
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
|
barrier.Transition.pResource = renderTarget;
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
cmdList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
cmdList->CopyResource(readbackBuffer, renderTarget);
|
||||||
|
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||||
|
cmdList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
cmdList->Close();
|
||||||
|
ID3D12CommandList* ppCmdLists[] = { cmdList };
|
||||||
|
queue->ExecuteCommandLists(1, ppCmdLists);
|
||||||
|
|
||||||
|
gFence.Initialize(device, 0);
|
||||||
|
gFence.Signal(1);
|
||||||
|
gFence.Wait(1);
|
||||||
|
|
||||||
|
D3D12_RANGE readRange = { 0, width * height * 4 };
|
||||||
|
unsigned char* mappedData = nullptr;
|
||||||
|
readbackBuffer->Map(0, &readRange, (void**)&mappedData);
|
||||||
|
|
||||||
|
FILE* fp = fopen(filename, "wb");
|
||||||
|
if (!fp) {
|
||||||
|
readbackBuffer->Release();
|
||||||
|
cmdList->Release();
|
||||||
|
cmdAlloc->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;
|
||||||
|
unsigned char r = mappedData[idx + 2];
|
||||||
|
unsigned char g = mappedData[idx + 1];
|
||||||
|
unsigned char b = mappedData[idx + 0];
|
||||||
|
fwrite(&r, 1, 1, fp);
|
||||||
|
fwrite(&g, 1, 1, fp);
|
||||||
|
fwrite(&b, 1, 1, fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
D3D12_RANGE writeRange = { 0, 0 };
|
||||||
|
readbackBuffer->Unmap(0, &writeRange);
|
||||||
|
|
||||||
|
readbackBuffer->Release();
|
||||||
|
cmdList->Release();
|
||||||
|
cmdAlloc->Release();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
// 获取D3D12设备
|
// 获取D3D12设备
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
@@ -874,6 +979,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
MSG msg;
|
MSG msg;
|
||||||
DWORD last_time = timeGetTime();
|
DWORD last_time = timeGetTime();
|
||||||
DWORD appStartTime = last_time;
|
DWORD appStartTime = last_time;
|
||||||
|
int frameCount = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
ZeroMemory(&msg, sizeof(MSG));
|
ZeroMemory(&msg, sizeof(MSG));
|
||||||
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||||
@@ -883,6 +989,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
} else {
|
} else {
|
||||||
|
frameCount++;
|
||||||
|
if (frameCount == 2) {
|
||||||
|
SaveScreenshot("screenshot.png", 1280, 720);
|
||||||
|
}
|
||||||
WaitForCompletionOfCommandList();
|
WaitForCompletionOfCommandList();
|
||||||
DWORD current_time = timeGetTime();
|
DWORD current_time = timeGetTime();
|
||||||
DWORD frameTime = current_time - last_time;
|
DWORD frameTime = current_time - last_time;
|
||||||
|
|||||||
Reference in New Issue
Block a user