Ensure only one thread at a time uses GPU readbacks during capture

* This was already done on D3D12 but Vulkan was missing the appropriate locks
This commit is contained in:
baldurk
2025-11-11 12:25:36 +00:00
parent 4a3447af78
commit 75175ce8d9
3 changed files with 29 additions and 11 deletions
+10 -1
View File
@@ -2698,8 +2698,10 @@ void VulkanDebugManager::FillWithDiscardPatternOnHost(VkDevice device, DiscardTy
}
}
void VulkanDebugManager::InitReadbackBuffer(VkDeviceSize sz)
VulkanDebugManager::ReadbackWindow VulkanDebugManager::LockReadbackBuffer(VkDeviceSize sz)
{
m_ReadbackLock.Lock();
if(m_ReadbackWindow.TotalSize() < sz)
{
if(m_ReadbackWindow.TotalSize() > 0)
@@ -2722,6 +2724,13 @@ void VulkanDebugManager::InitReadbackBuffer(VkDeviceSize sz)
CHECK_VKR(m_pDriver, VK_ERROR_MEMORY_MAP_FAILED);
}
}
return {m_ReadbackWindow.UnwrappedBuffer(), m_ReadbackWindow.UnwrappedMemory(), m_ReadbackPtr};
}
void VulkanDebugManager::UnlockReadbackBuffer()
{
m_ReadbackLock.Unlock();
}
void VulkanReplay::AllocAndAddReservedDescriptors(
+10 -4
View File
@@ -83,10 +83,15 @@ public:
VkImageLayout curLayout, VkImageSubresourceRange discardRange,
VkRect2D discardRect);
void InitReadbackBuffer(VkDeviceSize sz);
byte *GetReadbackPtr() { return m_ReadbackPtr; }
VkBuffer GetUnwrappedReadbackBuffer() { return m_ReadbackWindow.UnwrappedBuffer(); }
VkDeviceMemory GetUnwrappedReadbackMemory() { return m_ReadbackWindow.UnwrappedMemory(); }
struct ReadbackWindow
{
VkBuffer unwrappedBuffer;
VkDeviceMemory unwrappedMemory;
byte *ptr;
};
ReadbackWindow LockReadbackBuffer(VkDeviceSize sz);
void UnlockReadbackBuffer();
VkPipelineCache GetPipelineCache() { return m_PipelineCache; }
VkPipeline GetCustomPipeline() { return m_Custom.TexPipeline; }
VkPipeline GetDummyPipeline() { return m_DummyPipeline; }
@@ -134,6 +139,7 @@ private:
// GetBufferData
GPUBuffer m_ReadbackWindow;
byte *m_ReadbackPtr = NULL;
Threading::CriticalSection m_ReadbackLock;
// CacheMeshDisplayPipelines
std::map<uint64_t, VKMeshDisplayPipelines> m_CachedMeshPipelines;
@@ -1228,7 +1228,8 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
{
RDCDEBUG("Reading back %s with GPU for comparison", ToStr(record->GetResourceID()).c_str());
GetDebugManager()->InitReadbackBuffer(state.mapOffset + state.mapSize);
VulkanDebugManager::ReadbackWindow readback =
GetDebugManager()->LockReadbackBuffer(state.mapOffset + state.mapSize);
// immediately issue a command buffer to copy back the data. We do that on this queue to
// avoid complexity with synchronising with another queue, but the transfer queue if
@@ -1250,8 +1251,7 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
VkBufferCopy region = {state.mapOffset, state.mapOffset, state.mapSize};
ObjDisp(copycmd)->CmdCopyBuffer(Unwrap(copycmd), Unwrap(state.wholeMemBuf),
GetDebugManager()->GetUnwrappedReadbackBuffer(), 1,
&region);
readback.unwrappedBuffer, 1, &region);
// wait for transfer to finish before reading on CPU
VkBufferMemoryBarrier bufBarrier = {
@@ -1261,7 +1261,7 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
VK_ACCESS_HOST_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
GetDebugManager()->GetUnwrappedReadbackBuffer(),
readback.unwrappedBuffer,
0,
VK_WHOLE_SIZE,
};
@@ -1291,7 +1291,7 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
VkMappedMemoryRange range = {
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
NULL,
GetDebugManager()->GetUnwrappedReadbackMemory(),
readback.unwrappedMemory,
0,
VK_WHOLE_SIZE,
};
@@ -1300,7 +1300,7 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
ObjDisp(queue)->InvalidateMappedMemoryRanges(Unwrap(m_Device), 1, &range);
RDCASSERTEQUAL(copyret, VK_SUCCESS);
state.cpuReadPtr = GetDebugManager()->GetReadbackPtr();
state.cpuReadPtr = readback.ptr;
}
else
{
@@ -1349,6 +1349,9 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
// restore this just in case
state.cpuReadPtr = state.mappedPtr;
if(state.readbackOnGPU)
GetDebugManager()->UnlockReadbackBuffer();
}
}
}