Validate surface using shader code and write BMP file for each frame

This commit is contained in:
2025-10-06 03:13:02 +09:00
parent b4efc1be82
commit 1fa499013f
10 changed files with 691 additions and 216 deletions

View File

@@ -177,6 +177,91 @@ void D3D12Manager::WaitForGPU()
}
}
void D3D12Manager::ExecuteCommandListAndWait()
{
// Close command list
m_command_list->Close();
// Execute command list
ID3D12CommandList* cmdLists[] = { m_command_list };
m_command_queue->ExecuteCommandLists(1, cmdLists);
// Wait for GPU to finish
WaitForGPU();
// Reset command list for next use
m_command_allocator->Reset();
m_command_list->Reset(m_command_allocator, nullptr);
}
bool D3D12Manager::SaveTextureToBMP(ID3D12Resource* texture, uint32_t width, uint32_t height, const char* filename)
{
if (!texture || !filename) {
printf("[D3D12Manager] Invalid parameters for SaveTextureToBMP\n");
return false;
}
// Readback texture to CPU
uint8_t* rgba_data = ReadbackTexture(texture, width, height);
if (!rgba_data) {
printf("[D3D12Manager] Failed to readback texture\n");
return false;
}
// BMP file header (14 bytes)
uint32_t file_size = 54 + (width * height * 4);
uint8_t bmp_header[54] = {
'B', 'M', // Signature
(uint8_t)(file_size), (uint8_t)(file_size >> 8), (uint8_t)(file_size >> 16), (uint8_t)(file_size >> 24), // File size
0, 0, 0, 0, // Reserved
54, 0, 0, 0, // Data offset
40, 0, 0, 0, // Info header size
(uint8_t)(width), (uint8_t)(width >> 8), (uint8_t)(width >> 16), (uint8_t)(width >> 24), // Width
(uint8_t)(height), (uint8_t)(height >> 8), (uint8_t)(height >> 16), (uint8_t)(height >> 24), // Height
1, 0, // Planes
32, 0, // Bits per pixel (32-bit RGBA)
0, 0, 0, 0, // Compression (none)
(uint8_t)(width * height * 4), (uint8_t)((width * height * 4) >> 8),
(uint8_t)((width * height * 4) >> 16), (uint8_t)((width * height * 4) >> 24), // Image size
0, 0, 0, 0, // X pixels per meter
0, 0, 0, 0, // Y pixels per meter
0, 0, 0, 0, // Colors used
0, 0, 0, 0 // Important colors
};
// Open file
FILE* file = nullptr;
fopen_s(&file, filename, "wb");
if (!file) {
printf("[D3D12Manager] Failed to open file: %s\n", filename);
delete[] rgba_data;
return false;
}
// Write BMP header
fwrite(bmp_header, 1, 54, file);
// Convert RGBA to BGRA for BMP and flip vertically
for (int y = height - 1; y >= 0; y--) {
for (uint32_t x = 0; x < width; x++) {
uint32_t src_idx = (y * width + x) * 4;
uint8_t bgra[4] = {
rgba_data[src_idx + 2], // B
rgba_data[src_idx + 1], // G
rgba_data[src_idx + 0], // R
rgba_data[src_idx + 3] // A
};
fwrite(bgra, 1, 4, file);
}
}
fclose(file);
delete[] rgba_data;
printf("[D3D12Manager] Saved texture to: %s\n", filename);
return true;
}
ID3D12Resource* D3D12Manager::CreateNV12Texture(uint32_t width, uint32_t height)
{
D3D12_RESOURCE_DESC desc = {};
@@ -252,13 +337,13 @@ uint8_t* D3D12Manager::ReadbackTexture(ID3D12Resource* texture, uint32_t width,
// Get texture description
D3D12_RESOURCE_DESC desc = texture->GetDesc();
// Calculate layout for both Y and UV planes
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layouts[2];
UINT num_rows[2] = { 0 };
UINT64 row_sizes[2] = { 0 };
// RGBA format has only 1 subresource
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
UINT num_rows = 0;
UINT64 row_size = 0;
UINT64 total_bytes = 0;
m_device->GetCopyableFootprints(&desc, 0, 2, 0, layouts, num_rows, row_sizes, &total_bytes);
m_device->GetCopyableFootprints(&desc, 0, 1, 0, &layout, &num_rows, &row_size, &total_bytes);
// Create readback buffer
D3D12_HEAP_PROPERTIES readback_heap_props = {};
@@ -301,31 +386,18 @@ uint8_t* D3D12Manager::ReadbackTexture(ID3D12Resource* texture, uint32_t width,
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
m_command_list->ResourceBarrier(1, &barrier);
// Copy Y plane (subresource 0)
D3D12_TEXTURE_COPY_LOCATION src_y = {};
src_y.pResource = texture;
src_y.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_y.SubresourceIndex = 0;
// Copy RGBA texture (single subresource)
D3D12_TEXTURE_COPY_LOCATION src = {};
src.pResource = texture;
src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION dst_y = {};
dst_y.pResource = readback_buffer;
dst_y.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst_y.PlacedFootprint = layouts[0];
D3D12_TEXTURE_COPY_LOCATION dst = {};
dst.pResource = readback_buffer;
dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst.PlacedFootprint = layout;
m_command_list->CopyTextureRegion(&dst_y, 0, 0, 0, &src_y, nullptr);
// Copy UV plane (subresource 1)
D3D12_TEXTURE_COPY_LOCATION src_uv = {};
src_uv.pResource = texture;
src_uv.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_uv.SubresourceIndex = 1;
D3D12_TEXTURE_COPY_LOCATION dst_uv = {};
dst_uv.pResource = readback_buffer;
dst_uv.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst_uv.PlacedFootprint = layouts[1];
m_command_list->CopyTextureRegion(&dst_uv, 0, 0, 0, &src_uv, nullptr);
m_command_list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
// Transition texture back to COMMON
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
@@ -350,28 +422,20 @@ uint8_t* D3D12Manager::ReadbackTexture(ID3D12Resource* texture, uint32_t width,
return nullptr;
}
// Allocate CPU buffer (NV12 format: Y plane + UV plane)
uint32_t y_size = width * height;
uint32_t uv_size = width * (height / 2);
uint8_t* cpu_buffer = new uint8_t[y_size + uv_size];
// Allocate CPU buffer for RGBA data
uint8_t* rgba_data = new uint8_t[width * height * 4];
// Copy Y plane
uint8_t* src_y_data = static_cast<uint8_t*>(mapped_data) + layouts[0].Offset;
uint8_t* dst_y_data = cpu_buffer;
for (UINT row = 0; row < height; ++row) {
memcpy(dst_y_data + row * width, src_y_data + row * layouts[0].Footprint.RowPitch, width);
}
// Copy UV plane
uint8_t* src_uv_data = static_cast<uint8_t*>(mapped_data) + layouts[1].Offset;
uint8_t* dst_uv_data = cpu_buffer + y_size;
for (UINT row = 0; row < height / 2; ++row) {
memcpy(dst_uv_data + row * width, src_uv_data + row * layouts[1].Footprint.RowPitch, width);
// Copy RGBA data (considering row pitch alignment)
uint8_t* src_ptr = static_cast<uint8_t*>(mapped_data);
for (uint32_t y = 0; y < height; y++) {
memcpy(rgba_data + y * width * 4,
src_ptr + y * layout.Footprint.RowPitch,
width * 4);
}
// Unmap and release readback buffer
readback_buffer->Unmap(0, nullptr);
readback_buffer->Release();
return cpu_buffer;
return rgba_data;
}