From 1a5bcd75d968f2aec39df9cc14272d99e27fbc39 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 15 Mar 2026 03:29:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E4=BF=9D=E5=AD=98Debug=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加SaveScreenshot函数使用D3D12 Readback方式读取渲染目标 - 保存为PPM格式(在第2帧自动保存到screenshot.ppm) - 程序可自行测试渲染结果 --- tests/D3D12/main.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/D3D12/main.cpp b/tests/D3D12/main.cpp index 03bb74c6..9a1bc69d 100644 --- a/tests/D3D12/main.cpp +++ b/tests/D3D12/main.cpp @@ -721,6 +721,111 @@ void SwapD3D12Buffers() { 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设备 //================================================================================= @@ -874,6 +979,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine MSG msg; DWORD last_time = timeGetTime(); DWORD appStartTime = last_time; + int frameCount = 0; while (true) { ZeroMemory(&msg, sizeof(MSG)); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { @@ -883,6 +989,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine TranslateMessage(&msg); DispatchMessage(&msg); } else { + frameCount++; + if (frameCount == 2) { + SaveScreenshot("screenshot.png", 1280, 720); + } WaitForCompletionOfCommandList(); DWORD current_time = timeGetTime(); DWORD frameTime = current_time - last_time;