fix: 修复D3D12截图功能 - 使用正确的buffer尺寸和row pitch获取
This commit is contained in:
@@ -680,8 +680,13 @@ ID3D12GraphicsCommandList* GetCommandList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaitForCompletionOfCommandList() {
|
void WaitForCompletionOfCommandList() {
|
||||||
if (gFence.GetCompletedValue() < gFenceValue) {
|
UINT64 completed = gFence.GetCompletedValue();
|
||||||
gFence.Wait(gFenceValue);
|
UINT64 current = gFenceValue;
|
||||||
|
Log("[DEBUG] WaitForCompletion: completed=%llu, waiting for=%llu\n", completed, current);
|
||||||
|
if (completed < current) {
|
||||||
|
Log("[DEBUG] WaitForCompletion: waiting...\n");
|
||||||
|
gFence.Wait(current);
|
||||||
|
Log("[DEBUG] WaitForCompletion: done\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -755,25 +760,39 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
|||||||
Log("[DEBUG] SaveScreenshot: waiting for GPU\n");
|
Log("[DEBUG] SaveScreenshot: waiting for GPU\n");
|
||||||
WaitForCompletionOfCommandList();
|
WaitForCompletionOfCommandList();
|
||||||
|
|
||||||
// Get current back buffer index
|
// Don't call Present() - capture directly from RENDER_TARGET state
|
||||||
int currentRTIndex = swapChain->GetCurrentBackBufferIndex();
|
int captureRTIndex = gCurrentRTIndex;
|
||||||
Log("[DEBUG] SaveScreenshot: currentRTIndex = %d\n", currentRTIndex);
|
Log("[DEBUG] SaveScreenshot: gCurrentRTIndex = %d\n", gCurrentRTIndex);
|
||||||
|
|
||||||
// Use existing render target
|
// Use existing render target
|
||||||
ID3D12Resource* renderTarget = gColorRTs[currentRTIndex];
|
ID3D12Resource* renderTarget = gColorRTs[captureRTIndex];
|
||||||
Log("[DEBUG] SaveScreenshot: renderTarget = %p\n", renderTarget);
|
Log("[DEBUG] SaveScreenshot: renderTarget = %p\n", renderTarget);
|
||||||
|
|
||||||
// Need to transition from PRESENT to COPY_SOURCE for reading
|
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;
|
HRESULT hr = S_OK;
|
||||||
ID3D12CommandAllocator* cmdAlloc = nullptr;
|
ID3D12CommandAllocator* cmdAlloc = nullptr;
|
||||||
hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
|
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 = {};
|
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||||
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
|
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
|
||||||
D3D12_RESOURCE_DESC readbackDesc = {};
|
D3D12_RESOURCE_DESC readbackDesc = {};
|
||||||
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
readbackDesc.Alignment = 0;
|
readbackDesc.Alignment = 0;
|
||||||
readbackDesc.Width = (UINT64)width * height * 4;
|
readbackDesc.Width = totalSize;
|
||||||
readbackDesc.Height = 1;
|
readbackDesc.Height = 1;
|
||||||
readbackDesc.DepthOrArraySize = 1;
|
readbackDesc.DepthOrArraySize = 1;
|
||||||
readbackDesc.MipLevels = 1;
|
readbackDesc.MipLevels = 1;
|
||||||
@@ -794,17 +813,11 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
|||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
Log("[DEBUG] SaveScreenshot: CreateCommittedResource failed! hr=%08X\n", hr);
|
Log("[DEBUG] SaveScreenshot: CreateCommittedResource failed! hr=%08X\n", hr);
|
||||||
|
cmdAlloc->Release();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Log("[DEBUG] SaveScreenshot: created readback buffer\n");
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
||||||
@@ -819,15 +832,33 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
|||||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||||
barrier.Transition.pResource = renderTarget;
|
barrier.Transition.pResource = renderTarget;
|
||||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
cmdList->ResourceBarrier(1, &barrier);
|
cmdList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
cmdList->CopyResource(readbackBuffer, renderTarget);
|
// 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.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||||
cmdList->ResourceBarrier(1, &barrier);
|
cmdList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
cmdList->Close();
|
cmdList->Close();
|
||||||
@@ -852,10 +883,15 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D12_RESOURCE_DESC desc = readbackBuffer->GetDesc();
|
|
||||||
UINT rowPitch = width * 4;
|
|
||||||
Log("[DEBUG] SaveScreenshot: rowPitch = %u\n", rowPitch);
|
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");
|
Log("[DEBUG] SaveScreenshot: writing file\n");
|
||||||
FILE* fp = fopen(filename, "wb");
|
FILE* fp = fopen(filename, "wb");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
@@ -871,9 +907,9 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
|||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
int idx = y * rowPitch + x * 4;
|
int idx = y * rowPitch + x * 4;
|
||||||
unsigned char r = mappedData[idx + 2];
|
unsigned char r = mappedData[idx + 0]; // R8G8B8A8: R is at offset 0
|
||||||
unsigned char g = mappedData[idx + 1];
|
unsigned char g = mappedData[idx + 1]; // G is at offset 1
|
||||||
unsigned char b = mappedData[idx + 0];
|
unsigned char b = mappedData[idx + 2]; // B is at offset 2
|
||||||
fwrite(&r, 1, 1, fp);
|
fwrite(&r, 1, 1, fp);
|
||||||
fwrite(&g, 1, 1, fp);
|
fwrite(&g, 1, 1, fp);
|
||||||
fwrite(&b, 1, 1, fp);
|
fwrite(&b, 1, 1, fp);
|
||||||
@@ -1018,6 +1054,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
|
|
||||||
int imageWidth, imageHeight, imageChannel;
|
int imageWidth, imageHeight, imageChannel;
|
||||||
stbi_uc* pixels = stbi_load("Res/Image/earth_d.jpg", &imageWidth, &imageHeight, &imageChannel, 4);
|
stbi_uc* pixels = stbi_load("Res/Image/earth_d.jpg", &imageWidth, &imageHeight, &imageChannel, 4);
|
||||||
|
Log("[DEBUG] Texture loaded: width=%d, height=%d, channels=%d, pixels=%p\n", imageWidth, imageHeight, imageChannel, pixels);
|
||||||
ID3D12Resource* texture = CreateTexture2D(commandList, pixels,
|
ID3D12Resource* texture = CreateTexture2D(commandList, pixels,
|
||||||
imageWidth * imageHeight * imageChannel, imageWidth, imageHeight, DXGI_FORMAT_R8G8B8A8_UNORM);
|
imageWidth * imageHeight * imageChannel, imageWidth, imageHeight, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||||
delete[] pixels;
|
delete[] pixels;
|
||||||
@@ -1062,14 +1099,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
} else {
|
} else {
|
||||||
frameCount++;
|
frameCount++;
|
||||||
if (frameCount == 2) {
|
|
||||||
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();
|
WaitForCompletionOfCommandList();
|
||||||
DWORD current_time = timeGetTime();
|
DWORD current_time = timeGetTime();
|
||||||
DWORD frameTime = current_time - last_time;
|
DWORD frameTime = current_time - last_time;
|
||||||
@@ -1090,9 +1119,27 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
commandList->SetGraphicsRootShaderResourceView(3, sb->GetGPUVirtualAddress());
|
commandList->SetGraphicsRootShaderResourceView(3, sb->GetGPUVirtualAddress());
|
||||||
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||||
staticMeshComponent.Render(commandList);
|
staticMeshComponent.Render(commandList);
|
||||||
EndRenderToSwapChain(commandList);
|
|
||||||
|
// On screenshot frame, don't transition to PRESENT - keep RENDER_TARGET for screenshot
|
||||||
|
if (frameCount != 120) {
|
||||||
|
EndRenderToSwapChain(commandList);
|
||||||
|
}
|
||||||
EndCommandList();
|
EndCommandList();
|
||||||
SwapD3D12Buffers();
|
|
||||||
|
// On screenshot frame, don't Present - we'll screenshot before next frame
|
||||||
|
if (frameCount != 120) {
|
||||||
|
SwapD3D12Buffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Screenshot after rendering is done
|
||||||
|
if (frameCount == 120) {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gLogFile) {
|
if (gLogFile) {
|
||||||
|
|||||||
Reference in New Issue
Block a user