From d4ad181d085472be28e67cb69bcabff540415bee Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 20 May 2026 16:02:50 +0100 Subject: [PATCH] VK IndirectDraw fixes primarily for overlay handling - Unify Draw Indirect replay callback handling into a helper functions (for Draw* and DrawMesh*) - Replay callback handling uses its own Indirect Buffer for partial replay - Fix event skipping when at an event inside the outer Indirect action - The curEID might not be the same as baseEventID. - This could skip later events which should be replayed, if for example doing multiple partial replays. - Fix DrawMesh Indirect HandlePreCallback() not specifying ActionFlags::MeshDispatch in all cases --- renderdoc/driver/vulkan/vk_core.cpp | 4 + renderdoc/driver/vulkan/vk_core.h | 4 + .../vulkan/wrappers/vk_device_funcs.cpp | 1 + .../driver/vulkan/wrappers/vk_draw_funcs.cpp | 631 +++++++----------- 4 files changed, 249 insertions(+), 391 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index ed08a2fe6..6d0bb378a 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -3799,6 +3799,10 @@ RDResult WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructured GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferIndirectBuffer); m_IndirectBuffer.Name("m_IndirectBuffer"); + m_IndirectBufferCB.Create(this, GetDev(), m_IndirectBufferSize * 2, 1, + GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferIndirectBuffer); + m_IndirectBufferCB.Name("m_IndirectBufferActionCB"); + m_IndirectCommandBuffer = GetNextCmd(); // steal the command buffer out of the pending commands - we'll manage its lifetime ourselves diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index e82eea2a1..d01f10520 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -693,6 +693,7 @@ private: GPUBuffer m_IndirectBuffer; size_t m_IndirectBufferSize = 0; + GPUBuffer m_IndirectBufferCB; VkCommandBuffer m_IndirectCommandBuffer = VK_NULL_HANDLE; bool m_IndirectDraw = false; @@ -1186,6 +1187,9 @@ private: VkDeviceSize counterOffset = 0); void ExecuteIndirectReadback(VkCommandBuffer commandBuffer, const VkIndirectRecordData &indirectcopy); + void ReplayIndirectCB(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, + uint32_t countToReplay, uint32_t stride, uint32_t curEID, + uint32_t baseEventID, VkIndirectPatchType type); WriteSerialiser &GetThreadSerialiser(); template diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index 4bd3e6c9a..5023a278e 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -1057,6 +1057,7 @@ void WrappedVulkan::Shutdown() m_Replay->DestroyResources(); m_IndirectBuffer.Destroy(); + m_IndirectBufferCB.Destroy(); // destroy debug manager and any objects it created SAFE_DELETE(m_DebugManager); diff --git a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp index 61944e62a..9184d9cc0 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp @@ -162,6 +162,149 @@ void WrappedVulkan::ExecuteIndirectReadback(VkCommandBuffer commandBuffer, } } +void WrappedVulkan::ReplayIndirectCB(VkCommandBuffer commandBuffer, VkBuffer buffer, + VkDeviceSize offset, uint32_t countToReplay, uint32_t stride, + uint32_t curEID, uint32_t baseEventID, VkIndirectPatchType type) +{ + const uint32_t drawidx = (curEID > baseEventID) ? (curEID - baseEventID - 1) : 0; + const uint32_t firstDraw = m_FirstEventID <= 1 ? 0 : drawidx; + + // copy off the buffer segment to our indirect draw buffer + + VkMarkerRegion::Begin(StringFormat::Fmt("Drawcall callback replay (drawCount=%u)", countToReplay), + commandBuffer); + + VkBufferMemoryBarrier bufBarrier = {VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + NULL, + VK_ACCESS_INDIRECT_COMMAND_READ_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + m_IndirectBufferCB.UnwrappedBuffer(), + 0, + m_IndirectBufferSize}; + + DoPipelineBarrier(commandBuffer, 1, &bufBarrier); + + // initialise to 0 so all other draws don't draw anything + ObjDisp(commandBuffer) + ->CmdFillBuffer(Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), 0, + m_IndirectBufferSize, 0); + + const VkDeviceSize drawStart = firstDraw * stride; + VkDeviceSize indirectCmdSize = 0; + bool draw = false; + bool drawIndexed = false; + bool drawMesh = false; + switch(type) + { + case VkIndirectPatchType::DrawIndirect: + case VkIndirectPatchType::DrawIndirectCount: + indirectCmdSize = sizeof(VkDrawIndirectCommand); + draw = true; + break; + case VkIndirectPatchType::DrawIndexedIndirect: + case VkIndirectPatchType::DrawIndexedIndirectCount: + indirectCmdSize = sizeof(VkDrawIndexedIndirectCommand); + drawIndexed = true; + break; + case VkIndirectPatchType::MeshIndirect: + case VkIndirectPatchType::MeshIndirectCount: + indirectCmdSize = sizeof(VkDrawMeshTasksIndirectCommandEXT); + drawMesh = true; + break; + default: RDCERR("Unhandled VkIndirectPatchType %u", (uint32_t)(type)); break; + }; + ActionFlags actionFlags = drawMesh ? ActionFlags::MeshDispatch : ActionFlags::Drawcall; + + const VkDeviceSize drawEnd = + ((countToReplay > 0) ? (countToReplay - 1) * stride : 0) + indirectCmdSize; + + const VkDeviceSize regionSize = (drawEnd > drawStart) ? drawEnd - drawStart : 0; + VkBufferCopy region = {offset + drawStart, drawStart, regionSize}; + ObjDisp(commandBuffer) + ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), m_IndirectBufferCB.UnwrappedBuffer(), + 1, ®ion); + + // finally wait for copy to complete before drawing from it + bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + DoPipelineBarrier(commandBuffer, 1, &bufBarrier); + + bufBarrier.offset = 0; + bufBarrier.size = indirectCmdSize; + + for(uint32_t i = firstDraw; i < countToReplay; i++) + { + uint32_t eventId = HandlePreCallback(commandBuffer, actionFlags, i + 1); + + // action up to and including i. The previous draws will be nop'd out + const uint32_t countToDraw = i + 1; + if(draw) + { + ObjDisp(commandBuffer) + ->CmdDrawIndirect(Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), 0, + countToDraw, stride); + } + else if(drawIndexed) + { + ObjDisp(commandBuffer) + ->CmdDrawIndexedIndirect(Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), 0, + countToDraw, stride); + } + else if(drawMesh) + { + ObjDisp(commandBuffer) + ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), + 0, countToDraw, stride); + } + else + { + RDCERR("Missing Draw Type"); + } + + if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer)) + { + if(draw) + { + ObjDisp(commandBuffer) + ->CmdDrawIndirect(Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), 0, + countToDraw, stride); + } + else if(drawIndexed) + { + ObjDisp(commandBuffer) + ->CmdDrawIndexedIndirect(Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), 0, + countToDraw, stride); + } + else if(drawMesh) + { + ObjDisp(commandBuffer) + ->CmdDrawMeshTasksIndirectEXT( + Unwrap(commandBuffer), m_IndirectBufferCB.UnwrappedBuffer(), 0, countToDraw, stride); + } + + m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer); + } + + // now that we're done, nop out this draw so that the next time around we only draw + // the next draw. + bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + DoPipelineBarrier(commandBuffer, 1, &bufBarrier); + ObjDisp(commandBuffer) + ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset, + bufBarrier.size, 0); + bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + DoPipelineBarrier(commandBuffer, 1, &bufBarrier); + + bufBarrier.offset += stride; + } + + VkMarkerRegion::End(commandBuffer); +} + template bool WrappedVulkan::Serialise_vkCmdDraw(SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, @@ -393,6 +536,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu } else { + uint32_t countEventsReplayed = count; + if(InRerecordRange(m_LastCmdBufferID)) { commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); @@ -416,74 +561,19 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu } else { - uint32_t baseEventID = it->eventId; + const uint32_t baseEventID = it->eventId; + + uint32_t executesReplayed = RDCMIN(m_LastEventID - baseEventID, count); + // executesReplayed is relative to baseEventID + // compute the number of events to skip relative to the curEID + countEventsReplayed = (baseEventID + executesReplayed) - curEID; // when we have a callback, submit every action individually to the callback if(m_ActionCallback) { - VkMarkerRegion::Begin( - StringFormat::Fmt("Drawcall callback replay (drawCount=%u)", count), commandBuffer); - - // first copy off the buffer segment to our indirect action buffer - VkBufferMemoryBarrier bufBarrier = { - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - NULL, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(buffer), - offset, - (count > 0 ? stride * (count - 1) : 0) + sizeof(VkDrawIndirectCommand), - }; - - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - VkBufferCopy region = {offset, 0, bufBarrier.size}; - ObjDisp(commandBuffer) - ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), - m_IndirectBuffer.UnwrappedBuffer(), 1, ®ion); - - // wait for the copy to finish - bufBarrier.buffer = m_IndirectBuffer.UnwrappedBuffer(); - bufBarrier.offset = 0; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.size = sizeof(VkDrawIndirectCommand); - - for(uint32_t i = 0; i < count; i++) - { - uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::Drawcall, i + 1); - - // action up to and including i. The previous draws will be nop'd out - ObjDisp(commandBuffer) - ->CmdDrawIndirect(Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), 0, - i + 1, stride); - - if(eventId && - m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawIndirect(Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), - 0, i + 1, stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer); - } - - // now that we're done, nop out this draw so that the next time around we only draw - // the next draw. - bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - ObjDisp(commandBuffer) - ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset, - bufBarrier.size, 0); - bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.offset += stride; - } - - VkMarkerRegion::End(commandBuffer); + uint32_t countToReplay = RDCMIN(count, executesReplayed); + ReplayIndirectCB(commandBuffer, buffer, offset, countToReplay, stride, curEID, + baseEventID, VkIndirectPatchType::DrawIndirect); } // To add the multidraw, we made an event N that is the 'parent' marker, then // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected @@ -584,10 +674,11 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu } // multidraws skip the event ID past the whole thing + ++countEventsReplayed; if(m_FirstEventID > 1) - m_RootEventID += count + 1; + m_RootEventID += countEventsReplayed; else - m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += count + 1; + m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += countEventsReplayed; } } else @@ -601,8 +692,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu // add on the size we'll need for an indirect buffer in the worst case. // Note that we'll only ever be partially replaying one draw at a time, so we only need the // worst case. - m_IndirectBufferSize = - RDCMAX(m_IndirectBufferSize, sizeof(VkDrawIndirectCommand) + count * stride); + m_IndirectBufferSize = RDCMAX(m_IndirectBufferSize, sizeof(VkDrawIndirectCommand) + + (count > 0 ? count - 1 : 0) * stride); rdcstr name = "vkCmdDrawIndirect"; @@ -804,6 +895,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser, } else { + uint32_t countEventsReplayed = count; + if(InRerecordRange(m_LastCmdBufferID)) { commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); @@ -827,30 +920,19 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser, } else { - uint32_t baseEventID = it->eventId; + const uint32_t baseEventID = it->eventId; + + uint32_t executesReplayed = RDCMIN(m_LastEventID - baseEventID, count); + // executesReplayed is relative to baseEventID + // compute the number of events to skip relative to the curEID + countEventsReplayed = (baseEventID + executesReplayed) - curEID; // when we have a callback, submit every action individually to the callback if(m_ActionCallback) { - for(uint32_t i = 0; i < count; i++) - { - uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::Drawcall, i + 1); - - ObjDisp(commandBuffer) - ->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, 1, - stride); - - if(eventId && - m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, 1, - stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer); - } - - offset += stride; - } + uint32_t countToReplay = RDCMIN(count, executesReplayed); + ReplayIndirectCB(commandBuffer, buffer, offset, countToReplay, stride, curEID, + baseEventID, VkIndirectPatchType::DrawIndexedIndirect); } // To add the multidraw, we made an event N that is the 'parent' marker, then // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected @@ -963,10 +1045,11 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser, } // multidraws skip the event ID past the whole thing + ++countEventsReplayed; if(m_FirstEventID > 1) - m_RootEventID += count + 1; + m_RootEventID += countEventsReplayed; else - m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += count + 1; + m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += countEventsReplayed; } } else @@ -980,8 +1063,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser, // add on the size we'll need for an indirect buffer in the worst case. // Note that we'll only ever be partially replaying one draw at a time, so we only need the // worst case. - m_IndirectBufferSize = - RDCMAX(m_IndirectBufferSize, sizeof(VkDrawIndexedIndirectCommand) + count * stride); + m_IndirectBufferSize = RDCMAX(m_IndirectBufferSize, sizeof(VkDrawIndexedIndirectCommand) + + (count > 0 ? count - 1 : 0) * stride); rdcstr name = "vkCmdDrawIndexedIndirect"; @@ -2765,6 +2848,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectCount(SerialiserType &ser, // Fortunately when we're not in the re-recording command buffer the EID tracking isn't // needed. uint32_t count = maxDrawCount; + uint32_t countEventsReplayed = count; if(InRerecordRange(m_LastCmdBufferID)) { @@ -2789,7 +2873,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectCount(SerialiserType &ser, } else { - uint32_t baseEventID = it->eventId; + const uint32_t baseEventID = it->eventId; // get the number of draws by looking at how many children the parent action has. const rdcarray &children = GetAction(it->eventId)->children; @@ -2799,71 +2883,17 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectCount(SerialiserType &ser, if(!children.empty() && children.back().flags & ActionFlags::PopMarker) count--; + uint32_t executesReplayed = RDCMIN(m_LastEventID - baseEventID, count); + // executesReplayed is relative to baseEventID + // compute the number of events to skip relative to the curEID + countEventsReplayed = (baseEventID + executesReplayed) - curEID; + // when we have a callback, submit every action individually to the callback if(m_ActionCallback) { - VkMarkerRegion::Begin( - StringFormat::Fmt("Drawcall callback replay (drawCount=%u)", count), commandBuffer); - - // first copy off the buffer segment to our indirect draw buffer - VkBufferMemoryBarrier bufBarrier = { - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - NULL, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(buffer), - offset, - (count > 0 ? stride * (count - 1) : 0) + sizeof(VkDrawIndirectCommand), - }; - - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - VkBufferCopy region = {offset, 0, bufBarrier.size}; - ObjDisp(commandBuffer) - ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), - m_IndirectBuffer.UnwrappedBuffer(), 1, ®ion); - - // wait for the copy to finish - bufBarrier.buffer = m_IndirectBuffer.UnwrappedBuffer(); - bufBarrier.offset = 0; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.size = sizeof(VkDrawIndirectCommand); - - for(uint32_t i = 0; i < count; i++) - { - uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::Drawcall, i + 1); - - // action up to and including i. The previous draws will be nop'd out - ObjDisp(commandBuffer) - ->CmdDrawIndirect(Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), 0, - i + 1, stride); - - if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawIndirect(Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), 0, - i + 1, stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer); - } - - // now that we're done, nop out this draw so that the next time around we only draw - // the next draw. - bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - ObjDisp(commandBuffer) - ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset, - bufBarrier.size, 0); - bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.offset += stride; - } - - VkMarkerRegion::End(commandBuffer); + uint32_t countToReplay = RDCMIN(count, executesReplayed); + ReplayIndirectCB(commandBuffer, buffer, offset, countToReplay, stride, curEID, + baseEventID, VkIndirectPatchType::DrawIndirectCount); } // To add the multidraw, we made an event N that is the 'parent' marker, then // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected @@ -2961,10 +2991,11 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectCount(SerialiserType &ser, } // multidraws skip the event ID past the whole thing + ++countEventsReplayed; if(m_FirstEventID > 1) - m_RootEventID += count + 1; + m_RootEventID += countEventsReplayed; else - m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += count + 1; + m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += countEventsReplayed; } else { @@ -3121,6 +3152,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirectCount( // Fortunately when we're not in the re-recording command buffer the EID tracking isn't // needed. uint32_t count = maxDrawCount; + uint32_t countEventsReplayed = count; if(InRerecordRange(m_LastCmdBufferID)) { @@ -3145,7 +3177,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirectCount( } else { - uint32_t baseEventID = it->eventId; + const uint32_t baseEventID = it->eventId; // get the number of draws by looking at how many children the parent action has. const rdcarray &children = GetAction(it->eventId)->children; @@ -3155,71 +3187,17 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirectCount( if(!children.empty() && children.back().flags & ActionFlags::PopMarker) count--; + uint32_t executesReplayed = RDCMIN(m_LastEventID - baseEventID, count); + // executesReplayed is relative to baseEventID + // compute the number of events to skip relative to the curEID + countEventsReplayed = (baseEventID + executesReplayed) - curEID; + // when we have a callback, submit every action individually to the callback if(m_ActionCallback) { - VkMarkerRegion::Begin( - StringFormat::Fmt("Drawcall callback replay (drawCount=%u)", count), commandBuffer); - - // first copy off the buffer segment to our indirect draw buffer - VkBufferMemoryBarrier bufBarrier = { - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - NULL, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(buffer), - offset, - (count > 0 ? stride * (count - 1) : 0) + sizeof(VkDrawIndirectCommand), - }; - - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - VkBufferCopy region = {offset, 0, bufBarrier.size}; - ObjDisp(commandBuffer) - ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), - m_IndirectBuffer.UnwrappedBuffer(), 1, ®ion); - - // wait for the copy to finish - bufBarrier.buffer = m_IndirectBuffer.UnwrappedBuffer(); - bufBarrier.offset = 0; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.size = sizeof(VkDrawIndexedIndirectCommand); - - for(uint32_t i = 0; i < count; i++) - { - uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::Drawcall, i + 1); - - // action up to and including i. The previous draws will be nop'd out - ObjDisp(commandBuffer) - ->CmdDrawIndexedIndirect(Unwrap(commandBuffer), - m_IndirectBuffer.UnwrappedBuffer(), 0, i + 1, stride); - - if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawIndexedIndirect(Unwrap(commandBuffer), - m_IndirectBuffer.UnwrappedBuffer(), 0, i + 1, stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer); - } - - // now that we're done, nop out this draw so that the next time around we only draw - // the next draw. - bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - ObjDisp(commandBuffer) - ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset, - bufBarrier.size, 0); - bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.offset += stride; - } - - VkMarkerRegion::End(commandBuffer); + uint32_t countToReplay = RDCMIN(count, executesReplayed); + ReplayIndirectCB(commandBuffer, buffer, offset, countToReplay, stride, curEID, + baseEventID, VkIndirectPatchType::DrawIndexedIndirectCount); } // To add the multidraw, we made an event N that is the 'parent' marker, then // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected @@ -3318,10 +3296,11 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirectCount( } // multidraws skip the event ID past the whole thing + ++countEventsReplayed; if(m_FirstEventID > 1) - m_RootEventID += count + 1; + m_RootEventID += countEventsReplayed; else - m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += count + 1; + m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += countEventsReplayed; } else { @@ -4477,7 +4456,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, { commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); - uint32_t eventId = HandlePreCallback(commandBuffer); + uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch); ObjDisp(commandBuffer) ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset, @@ -4496,6 +4475,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, } else { + uint32_t countEventsReplayed = drawCount; + if(InRerecordRange(m_LastCmdBufferID)) { commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); @@ -4521,75 +4502,17 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, { uint32_t baseEventID = it->eventId; + uint32_t executesReplayed = RDCMIN(m_LastEventID - baseEventID, drawCount); + // executesReplayed is relative to baseEventID + // compute the number of events to skip relative to the curEID + countEventsReplayed = (baseEventID + executesReplayed) - curEID; + // when we have a callback, submit every action individually to the callback if(m_ActionCallback) { - VkMarkerRegion::Begin( - StringFormat::Fmt("Mesh Drawcall callback replay (drawCount=%u)", drawCount), - commandBuffer); - - // first copy off the buffer segment to our indirect action buffer - VkBufferMemoryBarrier bufBarrier = { - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - NULL, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(buffer), - offset, - (drawCount > 0 ? stride * (drawCount - 1) : 0) + - sizeof(VkDrawMeshTasksIndirectCommandEXT), - }; - - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - VkBufferCopy region = {offset, 0, bufBarrier.size}; - ObjDisp(commandBuffer) - ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), - m_IndirectBuffer.UnwrappedBuffer(), 1, ®ion); - - // wait for the copy to finish - bufBarrier.buffer = m_IndirectBuffer.UnwrappedBuffer(); - bufBarrier.offset = 0; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.size = sizeof(VkDrawMeshTasksIndirectCommandEXT); - - for(uint32_t i = 0; i < drawCount; i++) - { - uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, i + 1); - - // action up to and including i. The previous draws will be nop'd out - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT( - Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), 0, i + 1, stride); - - if(eventId && - m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), - m_IndirectBuffer.UnwrappedBuffer(), 0, i + 1, - stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer); - } - - // now that we're done, nop out this draw so that the next time around we only draw - // the next draw. - bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - ObjDisp(commandBuffer) - ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset, - bufBarrier.size, 0); - bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.offset += stride; - } - - VkMarkerRegion::End(commandBuffer); + uint32_t countToReplay = RDCMIN(drawCount, executesReplayed); + ReplayIndirectCB(commandBuffer, buffer, offset, countToReplay, stride, curEID, + baseEventID, VkIndirectPatchType::MeshIndirect); } // To add the multidraw, we made an event N that is the 'parent' marker, then // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected @@ -4683,29 +4606,19 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, stride = sizeof(VkDrawMeshTasksIndirectCommandEXT); } - { - uint32_t eventId = - HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, drawidx + 1); - - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), unwrappedBuffer, offset, - drawCount, stride); - - if(eventId && - m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), unwrappedBuffer, offset, - drawCount, stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer); - } - } + ObjDisp(commandBuffer) + ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), unwrappedBuffer, offset, + drawCount, stride); } } } // multidraws skip the event ID past the whole thing - m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += drawCount + 1; + ++countEventsReplayed; + if(m_FirstEventID > 1) + m_RootEventID += countEventsReplayed; + else + m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += countEventsReplayed; } } else @@ -4720,8 +4633,9 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, // add on the size we'll need for an indirect buffer in the worst case. // Note that we'll only ever be partially replaying one draw at a time, so we only need the // worst case. - m_IndirectBufferSize = RDCMAX(m_IndirectBufferSize, - sizeof(VkDrawMeshTasksIndirectCommandEXT) + drawCount * stride); + m_IndirectBufferSize = + RDCMAX(m_IndirectBufferSize, sizeof(VkDrawMeshTasksIndirectCommandEXT) + + (drawCount > 0 ? drawCount - 1 : 0) * stride); rdcstr name = "vkCmdDrawMeshTasksIndirectEXT"; @@ -4897,6 +4811,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectCountEXT( // Fortunately when we're not in the re-recording command buffer the EID tracking isn't // needed. uint32_t count = maxDrawCount; + uint32_t countEventsReplayed = count; if(InRerecordRange(m_LastCmdBufferID)) { @@ -4931,73 +4846,17 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectCountEXT( if(!children.empty() && children.back().flags & ActionFlags::PopMarker) count--; + uint32_t executesReplayed = RDCMIN(m_LastEventID - baseEventID, count); + // executesReplayed is relative to baseEventID + // compute the number of events to skip relative to the curEID + countEventsReplayed = (baseEventID + executesReplayed) - curEID; + // when we have a callback, submit every action individually to the callback if(m_ActionCallback) { - VkMarkerRegion::Begin( - StringFormat::Fmt("Mesh Dispatch callback replay (drawCount=%u)", count), - commandBuffer); - - // first copy off the buffer segment to our indirect draw buffer - VkBufferMemoryBarrier bufBarrier = { - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - NULL, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(buffer), - offset, - (count > 0 ? stride * (count - 1) : 0) + sizeof(VkDrawMeshTasksIndirectCommandEXT), - }; - - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - VkBufferCopy region = {offset, 0, bufBarrier.size}; - ObjDisp(commandBuffer) - ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), - m_IndirectBuffer.UnwrappedBuffer(), 1, ®ion); - - // wait for the copy to finish - bufBarrier.buffer = m_IndirectBuffer.UnwrappedBuffer(); - bufBarrier.offset = 0; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.size = sizeof(VkDrawMeshTasksIndirectCommandEXT); - - for(uint32_t i = 0; i < count; i++) - { - uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, i + 1); - - // action up to and including i. The previous draws will be nop'd out - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT( - Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), 0, i + 1, stride); - - if(eventId && - m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT( - Unwrap(commandBuffer), m_IndirectBuffer.UnwrappedBuffer(), 0, i + 1, stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer); - } - - // now that we're done, nop out this draw so that the next time around we only draw - // the next draw. - bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - ObjDisp(commandBuffer) - ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset, - bufBarrier.size, 0); - bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - DoPipelineBarrier(commandBuffer, 1, &bufBarrier); - - bufBarrier.offset += stride; - } - - VkMarkerRegion::End(commandBuffer); + uint32_t countToReplay = RDCMIN(count, executesReplayed); + ReplayIndirectCB(commandBuffer, buffer, offset, countToReplay, stride, curEID, + baseEventID, VkIndirectPatchType::MeshIndirectCount); } // To add the multidraw, we made an event N that is the 'parent' marker, then // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected @@ -5090,29 +4949,19 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectCountEXT( stride = sizeof(VkDrawMeshTasksIndirectCommandEXT); } - { - uint32_t eventId = - HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, drawidx + 1); - - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), unwrappedBuffer, offset, - count, stride); - - if(eventId && - m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer)) - { - ObjDisp(commandBuffer) - ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), unwrappedBuffer, offset, - count, stride); - m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer); - } - } + ObjDisp(commandBuffer) + ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), unwrappedBuffer, offset, count, + stride); } } } // multidraws skip the event ID past the whole thing - m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += count + 1; + ++countEventsReplayed; + if(m_FirstEventID > 1) + m_RootEventID += countEventsReplayed; + else + m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += countEventsReplayed; } else {