fix: 修复D3D12截图功能 - 使用正确的buffer尺寸和row pitch获取
This commit is contained in:
@@ -680,8 +680,13 @@ ID3D12GraphicsCommandList* GetCommandList() {
|
||||
}
|
||||
|
||||
void WaitForCompletionOfCommandList() {
|
||||
if (gFence.GetCompletedValue() < gFenceValue) {
|
||||
gFence.Wait(gFenceValue);
|
||||
UINT64 completed = gFence.GetCompletedValue();
|
||||
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");
|
||||
WaitForCompletionOfCommandList();
|
||||
|
||||
// Get current back buffer index
|
||||
int currentRTIndex = swapChain->GetCurrentBackBufferIndex();
|
||||
Log("[DEBUG] SaveScreenshot: currentRTIndex = %d\n", currentRTIndex);
|
||||
// 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[currentRTIndex];
|
||||
ID3D12Resource* renderTarget = gColorRTs[captureRTIndex];
|
||||
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;
|
||||
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 = (UINT64)width * height * 4;
|
||||
readbackDesc.Width = totalSize;
|
||||
readbackDesc.Height = 1;
|
||||
readbackDesc.DepthOrArraySize = 1;
|
||||
readbackDesc.MipLevels = 1;
|
||||
@@ -794,17 +813,11 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
||||
|
||||
if (FAILED(hr)) {
|
||||
Log("[DEBUG] SaveScreenshot: CreateCommittedResource failed! hr=%08X\n", hr);
|
||||
cmdAlloc->Release();
|
||||
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;
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* cmdList = nullptr;
|
||||
hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList));
|
||||
if (FAILED(hr)) {
|
||||
@@ -819,15 +832,33 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
||||
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.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);
|
||||
|
||||
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.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
cmdList->ResourceBarrier(1, &barrier);
|
||||
|
||||
cmdList->Close();
|
||||
@@ -852,10 +883,15 @@ bool SaveScreenshot(const char* filename, int width, int height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_DESC desc = readbackBuffer->GetDesc();
|
||||
UINT rowPitch = width * 4;
|
||||
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) {
|
||||
@@ -871,9 +907,9 @@ bool SaveScreenshot(const char* filename, int width, int 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 + 2];
|
||||
unsigned char g = mappedData[idx + 1];
|
||||
unsigned char b = mappedData[idx + 0];
|
||||
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);
|
||||
@@ -1018,6 +1054,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
|
||||
int imageWidth, imageHeight, imageChannel;
|
||||
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,
|
||||
imageWidth * imageHeight * imageChannel, imageWidth, imageHeight, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
delete[] pixels;
|
||||
@@ -1062,14 +1099,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
DispatchMessage(&msg);
|
||||
} else {
|
||||
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();
|
||||
DWORD current_time = timeGetTime();
|
||||
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->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
staticMeshComponent.Render(commandList);
|
||||
EndRenderToSwapChain(commandList);
|
||||
|
||||
// On screenshot frame, don't transition to PRESENT - keep RENDER_TARGET for screenshot
|
||||
if (frameCount != 120) {
|
||||
EndRenderToSwapChain(commandList);
|
||||
}
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user