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
This commit is contained in:
Jake Turner
2026-05-20 16:02:50 +01:00
parent d3c5312377
commit d4ad181d08
4 changed files with 249 additions and 391 deletions
+4
View File
@@ -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
+4
View File
@@ -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 <typename SerialiserType>
@@ -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);
+240 -391
View File
@@ -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, &region);
// 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 <typename SerialiserType>
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, &region);
// 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<ActionDescription> &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, &region);
// 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<ActionDescription> &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, &region);
// 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, &region);
// 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, &region);
// 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
{