diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index b0069d18f..81039f9da 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -283,11 +283,13 @@ WrappedVulkan::WrappedVulkan(const char *logFilename) m_FirstEventID = 0; m_LastEventID = ~0U; + m_DrawcallCallback = NULL; + m_LastCmdBufferID = ResourceId(); m_PartialReplayData.renderPassActive = false; m_PartialReplayData.resultPartialCmdBuffer = VK_NULL_HANDLE; - m_PartialReplayData.singleDrawCmdBuffer = VK_NULL_HANDLE; + m_PartialReplayData.outsideCmdBuffer = VK_NULL_HANDLE; m_PartialReplayData.partialParent = ResourceId(); m_PartialReplayData.baseEvent = 0; @@ -1277,16 +1279,21 @@ void WrappedVulkan::ContextReplayLog(LogState readType, uint32_t startEventID, u if(m_State == EXECUTING && startEventID == endEventID) break; - if(m_LastCmdBufferID != ResourceId()) + // increment root event ID either if we didn't just replay a cmd + // buffer event, OR if we are doing a frame sub-section replay, + // in which case it's up to the calling code to make sure we only + // replay inside a command buffer (if we crossed command buffer + // boundaries, the event IDs would no longer match up). + if(m_LastCmdBufferID == ResourceId() || startEventID > 1) + { + m_RootEventID++; + } + else { // these events are completely omitted, so don't increment the curEventID if(context != BEGIN_CMD_BUFFER && context != END_CMD_BUFFER) m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++; } - else - { - m_RootEventID++; - } } if(m_State == READING) @@ -1766,6 +1773,22 @@ void WrappedVulkan::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t m_RenderState.m_ResourceManager = GetResourceManager(); } + VkResult vkr = VK_SUCCESS; + + // we'll need our own command buffer if we're replaying just a subsection + // of events within a single command buffer record - always if it's only + // one drawcall, or if start event ID is > 0 we assume the outside code + // has chosen a subsection that lies within a command buffer + if(partial) + { + VkCommandBuffer cmd = m_PartialReplayData.outsideCmdBuffer = GetNextCmd(); + + VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; + + vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERT(vkr == VK_SUCCESS); + } + if(replayType == eReplay_Full) { ContextReplayLog(EXECUTING, startEventID, endEventID, partial); @@ -1776,13 +1799,8 @@ void WrappedVulkan::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t } else if(replayType == eReplay_OnlyDraw) { - VkCommandBuffer cmd = m_PartialReplayData.singleDrawCmdBuffer = GetNextCmd(); - - VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; + VkCommandBuffer cmd = m_PartialReplayData.outsideCmdBuffer; - VkResult vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); - RDCASSERT(vkr == VK_SUCCESS); - bool rpWasActive = m_PartialReplayData.renderPassActive; // if a render pass was active, begin it and set up the partial replay state @@ -1800,15 +1818,20 @@ void WrappedVulkan::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t // but we want to keep the partial replay data state intact, so restore // whether or not a render pass was active. m_PartialReplayData.renderPassActive = rpWasActive; - + } + else + RDCFATAL("Unexpected replay type"); + + if(m_PartialReplayData.outsideCmdBuffer != VK_NULL_HANDLE) + { + VkCommandBuffer cmd = m_PartialReplayData.outsideCmdBuffer; + ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); SubmitCmds(); - m_PartialReplayData.singleDrawCmdBuffer = VK_NULL_HANDLE; + m_PartialReplayData.outsideCmdBuffer = VK_NULL_HANDLE; } - else - RDCFATAL("Unexpected replay type"); } } diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 925835866..48e0b1cd8 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -96,6 +96,12 @@ struct DrawcallTreeNode #undef SERIALISED_PARAMETER #define SERIALISED_PARAMETER Serialiser *localSerialiser, +struct DrawcallCallback +{ + virtual void PreDraw(uint32_t eid) = 0; + virtual void PostDraw(uint32_t eid) = 0; +}; + class WrappedVulkan : public IFrameCapturer { private: @@ -147,6 +153,8 @@ private: VulkanDebugManager *m_DebugManager; Threading::CriticalSection m_CapTransitionLock; + + DrawcallCallback *m_DrawcallCallback; uint32_t m_FrameCounter; @@ -284,10 +292,11 @@ private: VkCommandBuffer resultPartialCmdBuffer; VkDevice partialDevice; // device for above cmd buffer - // if we're replaying just a single draw we don't go through the + // if we're replaying just a single draw or a particular command + // buffer subsection of command events, we don't go through the // whole original command buffers to set up the partial replay, // so we just set this command buffer - VkCommandBuffer singleDrawCmdBuffer; + VkCommandBuffer outsideCmdBuffer; // this records where in the frame a command buffer was submitted, // so that we know if our replay range ends in one of these ranges @@ -332,18 +341,18 @@ private: bool IsPartialCmd(ResourceId cmdid) { - return m_PartialReplayData.singleDrawCmdBuffer != VK_NULL_HANDLE || + return m_PartialReplayData.outsideCmdBuffer != VK_NULL_HANDLE || cmdid == m_PartialReplayData.partialParent; } bool InPartialRange() { - return m_PartialReplayData.singleDrawCmdBuffer != VK_NULL_HANDLE || + return m_PartialReplayData.outsideCmdBuffer != VK_NULL_HANDLE || m_BakedCmdBufferInfo[m_PartialReplayData.partialParent].curEventID <= m_LastEventID - m_PartialReplayData.baseEvent; } VkCommandBuffer PartialCmdBuf() { - if(m_PartialReplayData.singleDrawCmdBuffer != VK_NULL_HANDLE) - return m_PartialReplayData.singleDrawCmdBuffer; + if(m_PartialReplayData.outsideCmdBuffer != VK_NULL_HANDLE) + return m_PartialReplayData.outsideCmdBuffer; return m_PartialReplayData.resultPartialCmdBuffer; } @@ -494,6 +503,8 @@ public: FetchAPIEvent GetEvent(uint32_t eventID); const FetchDrawcall *GetDrawcall(uint32_t frameID, uint32_t eventID); + void SetDrawcallCB(DrawcallCallback *cb) { m_DrawcallCallback = cb; } + // Device initialization IMPLEMENT_FUNCTION_SERIALISED(VkResult, vkCreateInstance, diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 01e7d7cf8..a7e5f8091 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -583,7 +583,11 @@ vector VulkanReplay::GetPassEvents(uint32_t frameID, uint32_t eventID) if(start == draw) break; - if(start->flags & eDraw_Drawcall) + // include pass boundaries, these will be filtered out later + // so we don't actually do anything (init postvs/draw overlay) + // but it's useful to have the first part of the pass as part + // of the list + if(start->flags & (eDraw_Drawcall|eDraw_PassBoundary)) passEvents.push_back(start->eventID); start = m_pDriver->GetDrawcall(frameID, (uint32_t)start->next); @@ -3772,9 +3776,46 @@ void VulkanReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID) GetDebugManager()->InitPostVSBuffers(frameID, eventID); } +struct InitPostVSCallback : public DrawcallCallback +{ + InitPostVSCallback(WrappedVulkan *vk, VulkanReplay *rp, uint32_t frameID, const vector &events) + : m_pDriver(vk) + , m_pReplay(rp) + , m_FrameID(frameID) + , m_Events(events) + { m_pDriver->SetDrawcallCB(this); } + ~InitPostVSCallback() + { m_pDriver->SetDrawcallCB(NULL); } + + void PreDraw(uint32_t eid) + { + if(std::find(m_Events.begin(), m_Events.end(), eid) != m_Events.end()) + m_pReplay->InitPostVSBuffers(m_FrameID, eid); + } + + void PostDraw(uint32_t eid) + { + } + + uint32_t m_FrameID; + WrappedVulkan *m_pDriver; + VulkanReplay *m_pReplay; + const vector &m_Events; +}; + void VulkanReplay::InitPostVSBuffers(uint32_t frameID, const vector &events) { - // Stub, will implement this in a minute + // first we must replay up to the first event without replaying it. This ensures any + // non-command buffer calls like memory unmaps etc all happen correctly before this + // command buffer + m_pDriver->ReplayLog(frameID, 0, events.front(), eReplay_WithoutDraw); + + InitPostVSCallback cb(m_pDriver, this, frameID, events); + + // now we replay the events, which are guaranteed (because we generated them in + // GetPassEvents above) to come from the same command buffer, so the event IDs are + // still locally continuous, even if we jump into replaying. + m_pDriver->ReplayLog(frameID, events.front(), events.back(), eReplay_Full); } vector VulkanReplay::GetUsage(ResourceId id) diff --git a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp index e4dd35a8a..61c68b840 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp @@ -45,8 +45,12 @@ bool WrappedVulkan::Serialise_vkCmdDraw( { if(IsPartialCmd(cmdid) && InPartialRange()) { + if(m_DrawcallCallback) m_DrawcallCallback->PreDraw(m_RootEventID); + commandBuffer = PartialCmdBuf(); ObjDisp(commandBuffer)->CmdDraw(Unwrap(commandBuffer), vtxCount, instCount, firstVtx, firstInst); + + if(m_DrawcallCallback) m_DrawcallCallback->PostDraw(m_RootEventID); } } else if(m_State == READING) @@ -975,8 +979,12 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexed( { if(IsPartialCmd(cmdid) && InPartialRange()) { + if(m_DrawcallCallback) m_DrawcallCallback->PreDraw(m_RootEventID); + commandBuffer = PartialCmdBuf(); ObjDisp(commandBuffer)->CmdDrawIndexed(Unwrap(commandBuffer), idxCount, instCount, firstIdx, vtxOffs, firstInst); + + if(m_DrawcallCallback) m_DrawcallCallback->PostDraw(m_RootEventID); } } else if(m_State == READING)