refactor: 将截图功能移到RHI模块的D3D12Screenshot类
This commit is contained in:
@@ -88,10 +88,12 @@ add_library(XCEngine STATIC
|
|||||||
include/XCEngine/RHI/D3D12/D3D12CommandQueue.h
|
include/XCEngine/RHI/D3D12/D3D12CommandQueue.h
|
||||||
include/XCEngine/RHI/D3D12/D3D12CommandAllocator.h
|
include/XCEngine/RHI/D3D12/D3D12CommandAllocator.h
|
||||||
include/XCEngine/RHI/D3D12/D3D12Fence.h
|
include/XCEngine/RHI/D3D12/D3D12Fence.h
|
||||||
|
include/XCEngine/RHI/D3D12/D3D12Screenshot.h
|
||||||
src/RHI/D3D12Device.cpp
|
src/RHI/D3D12Device.cpp
|
||||||
src/RHI/D3D12CommandQueue.cpp
|
src/RHI/D3D12CommandQueue.cpp
|
||||||
src/RHI/D3D12CommandAllocator.cpp
|
src/RHI/D3D12CommandAllocator.cpp
|
||||||
src/RHI/D3D12Fence.cpp
|
src/RHI/D3D12Fence.cpp
|
||||||
|
src/RHI/D3D12Screenshot.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(XCEngine PUBLIC
|
target_include_directories(XCEngine PUBLIC
|
||||||
|
|||||||
28
engine/include/XCEngine/RHI/D3D12/D3D12Screenshot.h
Normal file
28
engine/include/XCEngine/RHI/D3D12/D3D12Screenshot.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <d3d12.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace RHI {
|
||||||
|
|
||||||
|
class D3D12Screenshot {
|
||||||
|
public:
|
||||||
|
static bool Capture(ID3D12Device* device,
|
||||||
|
ID3D12CommandQueue* commandQueue,
|
||||||
|
ID3D12Resource* renderTarget,
|
||||||
|
const char* filename,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool CopyToReadbackAndSave(ID3D12Device* device,
|
||||||
|
ID3D12CommandQueue* commandQueue,
|
||||||
|
ID3D12Resource* renderTarget,
|
||||||
|
const char* filename,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace RHI
|
||||||
|
} // namespace XCEngine
|
||||||
168
engine/src/RHI/D3D12Screenshot.cpp
Normal file
168
engine/src/RHI/D3D12Screenshot.cpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#include "RHI/D3D12/D3D12Screenshot.h"
|
||||||
|
#include "Debug/Logger.h"
|
||||||
|
#include <d3d12.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace RHI {
|
||||||
|
|
||||||
|
bool D3D12Screenshot::Capture(ID3D12Device* device,
|
||||||
|
ID3D12CommandQueue* commandQueue,
|
||||||
|
ID3D12Resource* renderTarget,
|
||||||
|
const char* filename,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height) {
|
||||||
|
return CopyToReadbackAndSave(device, commandQueue, renderTarget, filename, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12Screenshot::CopyToReadbackAndSave(ID3D12Device* device,
|
||||||
|
ID3D12CommandQueue* commandQueue,
|
||||||
|
ID3D12Resource* renderTarget,
|
||||||
|
const char* filename,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height) {
|
||||||
|
D3D12_RESOURCE_DESC rtDesc = renderTarget->GetDesc();
|
||||||
|
|
||||||
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {};
|
||||||
|
UINT64 totalSize = 0;
|
||||||
|
device->GetCopyableFootprints(&rtDesc, 0, 1, 0, &layout, nullptr, nullptr, &totalSize);
|
||||||
|
UINT rowPitch = layout.Footprint.RowPitch;
|
||||||
|
|
||||||
|
ID3D12CommandAllocator* cmdAlloc = nullptr;
|
||||||
|
HRESULT hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||||
|
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
|
||||||
|
D3D12_RESOURCE_DESC readbackDesc = {};
|
||||||
|
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
|
readbackDesc.Alignment = 0;
|
||||||
|
readbackDesc.Width = totalSize;
|
||||||
|
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;
|
||||||
|
hr = device->CreateCommittedResource(
|
||||||
|
&heapProps,
|
||||||
|
D3D12_HEAP_FLAG_NONE,
|
||||||
|
&readbackDesc,
|
||||||
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&readbackBuffer));
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
cmdAlloc->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12GraphicsCommandList* cmdList = nullptr;
|
||||||
|
hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
cmdAlloc->Release();
|
||||||
|
readbackBuffer->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_RENDER_TARGET;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
cmdList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
||||||
|
srcLoc.pResource = renderTarget;
|
||||||
|
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||||
|
srcLoc.SubresourceIndex = 0;
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
||||||
|
dstLoc.pResource = readbackBuffer;
|
||||||
|
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||||
|
dstLoc.PlacedFootprint = layout;
|
||||||
|
|
||||||
|
D3D12_BOX srcBox = {};
|
||||||
|
srcBox.left = 0;
|
||||||
|
srcBox.top = 0;
|
||||||
|
srcBox.front = 0;
|
||||||
|
srcBox.right = rtDesc.Width;
|
||||||
|
srcBox.bottom = rtDesc.Height;
|
||||||
|
srcBox.back = 1;
|
||||||
|
cmdList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, &srcBox);
|
||||||
|
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||||
|
cmdList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
cmdList->Close();
|
||||||
|
ID3D12CommandList* ppCmdLists[] = { cmdList };
|
||||||
|
commandQueue->ExecuteCommandLists(1, ppCmdLists);
|
||||||
|
|
||||||
|
HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
ID3D12Fence* fence = nullptr;
|
||||||
|
hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
UINT64 fenceValue = 1;
|
||||||
|
commandQueue->Signal(fence, fenceValue);
|
||||||
|
if (fence->GetCompletedValue() < fenceValue) {
|
||||||
|
fence->SetEventOnCompletion(fenceValue, fenceEvent);
|
||||||
|
WaitForSingleObject(fenceEvent, INFINITE);
|
||||||
|
}
|
||||||
|
fence->Release();
|
||||||
|
}
|
||||||
|
CloseHandle(fenceEvent);
|
||||||
|
|
||||||
|
D3D12_RANGE readRange = { 0, 0 };
|
||||||
|
unsigned char* mappedData = nullptr;
|
||||||
|
hr = readbackBuffer->Map(0, &readRange, (void**)&mappedData);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
cmdList->Release();
|
||||||
|
cmdAlloc->Release();
|
||||||
|
readbackBuffer->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fp = fopen(filename, "wb");
|
||||||
|
if (!fp) {
|
||||||
|
readbackBuffer->Unmap(0, nullptr);
|
||||||
|
cmdList->Release();
|
||||||
|
cmdAlloc->Release();
|
||||||
|
readbackBuffer->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "P6\n%d %d\n255\n", width, height);
|
||||||
|
for (uint32_t y = 0; y < height; y++) {
|
||||||
|
for (uint32_t x = 0; x < width; x++) {
|
||||||
|
int idx = y * rowPitch + x * 4;
|
||||||
|
unsigned char r = mappedData[idx + 0];
|
||||||
|
unsigned char g = mappedData[idx + 1];
|
||||||
|
unsigned char b = mappedData[idx + 2];
|
||||||
|
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);
|
||||||
|
|
||||||
|
cmdList->Release();
|
||||||
|
cmdAlloc->Release();
|
||||||
|
readbackBuffer->Release();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RHI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "XCEngine/RHI/D3D12/D3D12CommandQueue.h"
|
#include "XCEngine/RHI/D3D12/D3D12CommandQueue.h"
|
||||||
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
|
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
|
||||||
#include "XCEngine/RHI/D3D12/D3D12Fence.h"
|
#include "XCEngine/RHI/D3D12/D3D12Fence.h"
|
||||||
|
#include "XCEngine/RHI/D3D12/D3D12Screenshot.h"
|
||||||
#include "XCEngine/Debug/Logger.h"
|
#include "XCEngine/Debug/Logger.h"
|
||||||
#include "XCEngine/Debug/ConsoleLogSink.h"
|
#include "XCEngine/Debug/ConsoleLogSink.h"
|
||||||
#include "XCEngine/Debug/FileLogSink.h"
|
#include "XCEngine/Debug/FileLogSink.h"
|
||||||
@@ -743,187 +744,21 @@ void SwapD3D12Buffers() {
|
|||||||
|
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
// 截图保存 Debug 工具
|
// 截图保存 Debug 工具
|
||||||
// 使用 Readback 方式读取渲染目标并保存为 PPM 格式
|
// 使用 RHI 模块的 D3D12Screenshot 类
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
bool SaveScreenshot(const char* filename, int width, int height) {
|
bool SaveScreenshot(const char* filename, int width, int height) {
|
||||||
Log("[DEBUG] SaveScreenshot: start\n");
|
Log("[DEBUG] SaveScreenshot: start\n");
|
||||||
|
|
||||||
XCEngine::RHI::D3D12Device& deviceWrapper = gDevice;
|
ID3D12Device* device = gDevice.GetDevice();
|
||||||
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();
|
|
||||||
|
|
||||||
// Don't call Present() - capture directly from RENDER_TARGET state
|
|
||||||
int captureRTIndex = gCurrentRTIndex;
|
|
||||||
Log("[DEBUG] SaveScreenshot: gCurrentRTIndex = %d\n", gCurrentRTIndex);
|
|
||||||
|
|
||||||
// Use existing render target
|
|
||||||
ID3D12Resource* renderTarget = gColorRTs[captureRTIndex];
|
|
||||||
Log("[DEBUG] SaveScreenshot: renderTarget = %p\n", renderTarget);
|
|
||||||
|
|
||||||
D3D12_RESOURCE_DESC rtDesc = renderTarget->GetDesc();
|
|
||||||
Log("[DEBUG] SaveScreenshot: rtDesc.Width=%llu, Height=%u, Format=%d\n", rtDesc.Width, rtDesc.Height, rtDesc.Format);
|
|
||||||
|
|
||||||
// Create readback buffer - size must include row pitch padding
|
|
||||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {};
|
|
||||||
UINT64 totalSize = 0;
|
|
||||||
device->GetCopyableFootprints(&rtDesc, 0, 1, 0, &layout, nullptr, nullptr, &totalSize);
|
|
||||||
UINT rowPitch = layout.Footprint.RowPitch;
|
|
||||||
Log("[DEBUG] SaveScreenshot: GPU rowPitch = %u, totalSize = %llu\n", rowPitch, totalSize);
|
|
||||||
|
|
||||||
// Create buffer with proper size
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
ID3D12CommandAllocator* cmdAlloc = nullptr;
|
|
||||||
hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
Log("[DEBUG] SaveScreenshot: CreateCommandAllocator failed! hr=%08X\n", hr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
|
||||||
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
|
|
||||||
D3D12_RESOURCE_DESC readbackDesc = {};
|
|
||||||
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
||||||
readbackDesc.Alignment = 0;
|
|
||||||
readbackDesc.Width = totalSize;
|
|
||||||
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;
|
|
||||||
hr = device->CreateCommittedResource(
|
|
||||||
&heapProps,
|
|
||||||
D3D12_HEAP_FLAG_NONE,
|
|
||||||
&readbackDesc,
|
|
||||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
||||||
nullptr,
|
|
||||||
IID_PPV_ARGS(&readbackBuffer));
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
Log("[DEBUG] SaveScreenshot: CreateCommittedResource failed! hr=%08X\n", hr);
|
|
||||||
cmdAlloc->Release();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Log("[DEBUG] SaveScreenshot: created readback buffer\n");
|
|
||||||
|
|
||||||
ID3D12GraphicsCommandList* cmdList = nullptr;
|
|
||||||
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_RENDER_TARGET;
|
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
|
||||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
||||||
cmdList->ResourceBarrier(1, &barrier);
|
|
||||||
|
|
||||||
// Use CopyTextureRegion to copy from texture to buffer
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
|
||||||
srcLoc.pResource = renderTarget;
|
|
||||||
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
||||||
srcLoc.SubresourceIndex = 0;
|
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
|
||||||
dstLoc.pResource = readbackBuffer;
|
|
||||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
||||||
dstLoc.PlacedFootprint = layout;
|
|
||||||
|
|
||||||
D3D12_BOX srcBox = {};
|
|
||||||
srcBox.left = 0;
|
|
||||||
srcBox.top = 0;
|
|
||||||
srcBox.front = 0;
|
|
||||||
srcBox.right = (UINT)rtDesc.Width;
|
|
||||||
srcBox.bottom = rtDesc.Height;
|
|
||||||
srcBox.back = 1;
|
|
||||||
cmdList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, &srcBox);
|
|
||||||
|
|
||||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
||||||
cmdList->ResourceBarrier(1, &barrier);
|
|
||||||
|
|
||||||
cmdList->Close();
|
|
||||||
ID3D12CommandList* ppCmdLists[] = { cmdList };
|
|
||||||
ID3D12CommandQueue* queue = gCommandQueue.GetCommandQueue();
|
ID3D12CommandQueue* queue = gCommandQueue.GetCommandQueue();
|
||||||
queue->ExecuteCommandLists(1, ppCmdLists);
|
ID3D12Resource* renderTarget = gColorRTs[gCurrentRTIndex];
|
||||||
|
|
||||||
Log("[DEBUG] SaveScreenshot: waiting for GPU copy\n");
|
Log("[DEBUG] SaveScreenshot: calling D3D12Screenshot::Capture\n");
|
||||||
gFenceValue += 1;
|
bool result = XCEngine::RHI::D3D12Screenshot::Capture(
|
||||||
queue->Signal(gFence.GetFence(), gFenceValue);
|
device, queue, renderTarget, filename, (uint32_t)width, (uint32_t)height);
|
||||||
gFence.Wait(gFenceValue);
|
|
||||||
|
Log("[DEBUG] SaveScreenshot: done, result=%d\n", result);
|
||||||
Log("[DEBUG] SaveScreenshot: mapping memory\n");
|
return result;
|
||||||
D3D12_RANGE readRange = { 0, 0 };
|
|
||||||
unsigned char* mappedData = nullptr;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log("[DEBUG] SaveScreenshot: rowPitch = %u\n", rowPitch);
|
|
||||||
|
|
||||||
// Debug: check some pixel values
|
|
||||||
int centerIdx = (height / 2) * rowPitch + (width / 2) * 4;
|
|
||||||
Log("[DEBUG] SaveScreenshot: center pixel RGBA = %02X %02X %02X %02X\n",
|
|
||||||
mappedData[centerIdx], mappedData[centerIdx+1], mappedData[centerIdx+2], mappedData[centerIdx+3]);
|
|
||||||
Log("[DEBUG] SaveScreenshot: first pixel RGBA = %02X %02X %02X %02X\n",
|
|
||||||
mappedData[0], mappedData[1], mappedData[2], mappedData[3]);
|
|
||||||
|
|
||||||
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 * rowPitch + x * 4;
|
|
||||||
unsigned char r = mappedData[idx + 0]; // R8G8B8A8: R is at offset 0
|
|
||||||
unsigned char g = mappedData[idx + 1]; // G is at offset 1
|
|
||||||
unsigned char b = mappedData[idx + 2]; // B is at offset 2
|
|
||||||
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);
|
|
||||||
|
|
||||||
cmdList->Release();
|
|
||||||
cmdAlloc->Release();
|
|
||||||
readbackBuffer->Release();
|
|
||||||
|
|
||||||
Log("[DEBUG] SaveScreenshot: done!\n");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user