diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 5890bbf9c..4e7456f8e 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -337,6 +337,7 @@ WrappedVulkan::WrappedVulkan(const char *logFilename) m_PartialReplayData.renderPassActive = false; m_PartialReplayData.resultPartialCmdBuffer = VK_NULL_HANDLE; + m_PartialReplayData.singleDrawCmdBuffer = VK_NULL_HANDLE; m_PartialReplayData.partialParent = ResourceId(); m_PartialReplayData.baseEvent = 0; @@ -835,12 +836,6 @@ void WrappedVulkan::ContextReplayLog(LogState readType, uint32_t startEventID, u m_FirstEventID = startEventID; m_LastEventID = endEventID; - - m_PartialReplayData.renderPassActive = false; - RDCASSERT(m_PartialReplayData.resultPartialCmdBuffer == VK_NULL_HANDLE); - m_PartialReplayData.partialParent = ResourceId(); - m_PartialReplayData.baseEvent = 0; - m_PartialReplayData.state = PartialReplayData::StateVector(); } else if(m_State == READING) { @@ -873,6 +868,10 @@ void WrappedVulkan::ContextReplayLog(LogState readType, uint32_t startEventID, u // but for now this will do. if(context == CONTEXT_CAPTURE_FOOTER) break; + + // break out if we were only executing one event + if(m_State == EXECUTING && startEventID == endEventID) + break; if(m_LastCmdBufferID != ResourceId()) { @@ -1329,10 +1328,6 @@ void WrappedVulkan::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t { RDCASSERT(frameID < (uint32_t)m_FrameRecord.size()); - // VKTODOHIGH figure out how replaying only a draw will work. - if(replayType == eReplay_OnlyDraw) return; - if(replayType == eReplay_WithoutDraw) { replayType = eReplay_Full; } - uint64_t offs = m_FrameRecord[frameID].frameInfo.fileOffset; m_pSerialiser->SetOffset(offs); @@ -1401,12 +1396,142 @@ void WrappedVulkan::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t } { + if(!partial) + { + m_PartialReplayData.renderPassActive = false; + RDCASSERT(m_PartialReplayData.resultPartialCmdBuffer == VK_NULL_HANDLE); + m_PartialReplayData.partialParent = ResourceId(); + m_PartialReplayData.baseEvent = 0; + m_PartialReplayData.state = PartialReplayData::StateVector(); + } + if(replayType == eReplay_Full) + { ContextReplayLog(EXECUTING, startEventID, endEventID, partial); + } else if(replayType == eReplay_WithoutDraw) + { ContextReplayLog(EXECUTING, startEventID, RDCMAX(1U,endEventID)-1, partial); + } else if(replayType == eReplay_OnlyDraw) + { + VkCmdBuffer cmd = m_PartialReplayData.singleDrawCmdBuffer = GetNextCmd(); + + VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT }; + + VkResult vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERT(vkr == VK_SUCCESS); + + // if a render pass was active, begin it and set up the partial replay state + if(m_PartialReplayData.renderPassActive) + { + auto &s = m_PartialReplayData.state; + + RDCASSERT(s.renderPass != ResourceId()); + + // clear values don't matter as we're using the load renderpass here, that + // has all load ops set to load (as we're doing a partial replay - can't + // just clear the targets that are partially written to). + + VkClearValue empty[16] = {0}; + + RDCASSERT(ARRAY_COUNT(empty) >= m_CreationInfo.m_RenderPass[s.renderPass].attachCount); + + VkRenderPassBeginInfo rpbegin = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, + Unwrap(m_CreationInfo.m_RenderPass[s.renderPass].loadRP), + Unwrap(GetResourceManager()->GetCurrentHandle(s.framebuffer)), + s.renderArea, + m_CreationInfo.m_RenderPass[s.renderPass].attachCount, empty, + }; + ObjDisp(cmd)->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_RENDER_PASS_CONTENTS_INLINE); + + if(s.graphics.pipeline != ResourceId()) + { + ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(GetResourceManager()->GetCurrentHandle(s.graphics.pipeline))); + + ResourceId pipeLayoutId = m_CreationInfo.m_Pipeline[s.graphics.pipeline].layout; + VkPipelineLayout layout = GetResourceManager()->GetCurrentHandle(pipeLayoutId); + + const vector &descSetLayouts = m_CreationInfo.m_PipelineLayout[pipeLayoutId].descSetLayouts; + + // only iterate over the desc sets that this layout actually uses, not all that were bound + for(size_t i=0; i < descSetLayouts.size(); i++) + { + const DescSetLayout &descLayout = m_CreationInfo.m_DescSetLayout[ descSetLayouts[i] ]; + + if(s.graphics.descSets[i] != ResourceId()) + { + // if there are dynamic buffers, pass along the offsets + ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(layout), (uint32_t)i, + 1, UnwrapPtr(GetResourceManager()->GetCurrentHandle(s.graphics.descSets[i])), + descLayout.dynamicCount, descLayout.dynamicCount == 0 ? NULL : &s.graphics.offsets[i][0]); + } + else + { + RDCERR("Descriptor set is not bound but pipeline layout expects one"); + } + } + } + + if(s.compute.pipeline != ResourceId()) + { + ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetResourceManager()->GetCurrentHandle(s.compute.pipeline))); + + ResourceId pipeLayoutId = m_CreationInfo.m_Pipeline[s.compute.pipeline].layout; + VkPipelineLayout layout = GetResourceManager()->GetCurrentHandle(pipeLayoutId); + + const vector &descSetLayouts = m_CreationInfo.m_PipelineLayout[pipeLayoutId].descSetLayouts; + + for(size_t i=0; i < descSetLayouts.size(); i++) + { + const DescSetLayout &descLayout = m_CreationInfo.m_DescSetLayout[ descSetLayouts[i] ]; + + if(s.compute.descSets[i] != ResourceId()) + { + ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(layout), (uint32_t)i, + 1, UnwrapPtr(GetResourceManager()->GetCurrentHandle(s.compute.descSets[i])), + descLayout.dynamicCount, descLayout.dynamicCount == 0 ? NULL : &s.compute.offsets[i][0]); + } + } + } + + if(!s.views.empty()) + ObjDisp(cmd)->CmdSetViewport(Unwrap(cmd), (uint32_t)s.views.size(), &s.views[0]); + if(!s.scissors.empty()) + ObjDisp(cmd)->CmdSetScissor(Unwrap(cmd), (uint32_t)s.scissors.size(), &s.scissors[0]); + + ObjDisp(cmd)->CmdSetBlendConstants(Unwrap(cmd), s.blendConst); + ObjDisp(cmd)->CmdSetDepthBounds(Unwrap(cmd), s.mindepth, s.maxdepth); + ObjDisp(cmd)->CmdSetLineWidth(Unwrap(cmd), s.lineWidth); + ObjDisp(cmd)->CmdSetDepthBias(Unwrap(cmd), s.bias.depth, s.bias.biasclamp, s.bias.slope); + + ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, s.back.ref); + ObjDisp(cmd)->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, s.back.compare); + ObjDisp(cmd)->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, s.back.write); + + ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, s.front.ref); + ObjDisp(cmd)->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, s.front.compare); + ObjDisp(cmd)->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, s.front.write); + + if(s.ibuffer.buf != ResourceId()) + ObjDisp(cmd)->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(GetResourceManager()->GetCurrentHandle(s.ibuffer.buf)), s.ibuffer.offs, s.ibuffer.bytewidth == 4 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16); + + for(size_t i=0; i < s.vbuffers.size(); i++) + ObjDisp(cmd)->CmdBindVertexBuffers(Unwrap(cmd), (uint32_t)i, 1, UnwrapPtr(GetResourceManager()->GetCurrentHandle(s.vbuffers[i].buf)), &s.vbuffers[i].offs); + } + ContextReplayLog(EXECUTING, endEventID, endEventID, partial); + + if(m_PartialReplayData.renderPassActive) + ObjDisp(cmd)->CmdEndRenderPass(Unwrap(cmd)); + + ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); + + SubmitCmds(); + + m_PartialReplayData.singleDrawCmdBuffer = 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 9ee668c6f..9f8221ec9 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -239,6 +239,11 @@ private: VkCmdBuffer resultPartialCmdBuffer; VkDevice partialDevice; // device for above cmd buffer + // if we're replaying just a single draw we don't go through the + // whole original command buffers to set up the partial replay, + // so we just set this command buffer + VkCmdBuffer singleDrawCmdBuffer; + // 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 // we need to construct a partial command buffer for future @@ -286,6 +291,8 @@ private: compute.pipeline = graphics.pipeline = renderPass = framebuffer = ResourceId(); compute.descSets.clear(); graphics.descSets.clear(); + compute.offsets.clear(); + graphics.offsets.clear(); lineWidth = 1.0f; RDCEraseEl(bias); @@ -317,6 +324,7 @@ private: { ResourceId pipeline; vector descSets; + vector< vector > offsets; } compute, graphics; struct IdxBuffer @@ -335,9 +343,22 @@ private: } state; } m_PartialReplayData; - bool IsPartialCmd(ResourceId cmdid) { return cmdid == m_PartialReplayData.partialParent; } - bool InPartialRange() { return m_BakedCmdBufferInfo[m_PartialReplayData.partialParent].curEventID <= m_LastEventID - m_PartialReplayData.baseEvent; } - VkCmdBuffer PartialCmdBuf() { return m_PartialReplayData.resultPartialCmdBuffer; } + bool IsPartialCmd(ResourceId cmdid) + { + return m_PartialReplayData.singleDrawCmdBuffer != VK_NULL_HANDLE || + cmdid == m_PartialReplayData.partialParent; + } + bool InPartialRange() + { + return m_PartialReplayData.singleDrawCmdBuffer != VK_NULL_HANDLE || + m_BakedCmdBufferInfo[m_PartialReplayData.partialParent].curEventID <= m_LastEventID - m_PartialReplayData.baseEvent; + } + VkCmdBuffer PartialCmdBuf() + { + if(m_PartialReplayData.singleDrawCmdBuffer != VK_NULL_HANDLE) + return m_PartialReplayData.singleDrawCmdBuffer; + return m_PartialReplayData.resultPartialCmdBuffer; + } // this info is stored in the record on capture, but we diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index d95dfc8fd..b60073897 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -26,6 +26,8 @@ void DescSetLayout::Init(const VkDescriptorSetLayoutCreateInfo* pCreateInfo) { + dynamicCount = 0; + bindings.resize(pCreateInfo->count); for(uint32_t i=0; i < pCreateInfo->count; i++) { @@ -33,6 +35,10 @@ void DescSetLayout::Init(const VkDescriptorSetLayoutCreateInfo* pCreateInfo) bindings[i].descriptorType = pCreateInfo->pBinding[i].descriptorType; bindings[i].stageFlags = pCreateInfo->pBinding[i].stageFlags; + if(bindings[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || + bindings[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) + dynamicCount++; + if(pCreateInfo->pBinding[i].pImmutableSamplers) { bindings[i].immutableSampler = new ResourceId[bindings[i].arraySize]; @@ -57,6 +63,8 @@ void VulkanCreationInfo::Pipeline::Init(const VkGraphicsPipelineCreateInfo* pCre { flags = pCreateInfo->flags; + layout = VKMGR()->GetNonDispWrapper(pCreateInfo->layout)->id; + // need to figure out which states are valid to be NULL // VkPipelineShaderStageCreateInfo @@ -171,6 +179,8 @@ void VulkanCreationInfo::Pipeline::Init(const VkComputePipelineCreateInfo* pCrea { flags = pCreateInfo->flags; + layout = VKMGR()->GetNonDispWrapper(pCreateInfo->layout)->id; + // need to figure out which states are valid to be NULL // VkPipelineShaderStageCreateInfo @@ -212,8 +222,17 @@ void VulkanCreationInfo::Pipeline::Init(const VkComputePipelineCreateInfo* pCrea logicOp = VK_LOGIC_OP_NOOP; } +void VulkanCreationInfo::PipelineLayout::Init(const VkPipelineLayoutCreateInfo* pCreateInfo) +{ + descSetLayouts.resize(pCreateInfo->descriptorSetCount); + for(uint32_t i=0; i < pCreateInfo->descriptorSetCount; i++) + descSetLayouts[i] = VKMGR()->GetNonDispWrapper(pCreateInfo->pSetLayouts[i])->id; +} + void VulkanCreationInfo::RenderPass::Init(const VkRenderPassCreateInfo* pCreateInfo) { + attachCount = pCreateInfo->attachmentCount; + // VKTODOMED figure out how subpasses work RDCASSERT(pCreateInfo->subpassCount > 0); const VkSubpassDescription &subp = pCreateInfo->pSubpasses[0]; diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index a5e8a9ad1..e3622ab45 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -46,6 +46,8 @@ struct DescSetLayout ResourceId *immutableSampler; }; vector bindings; + + uint32_t dynamicCount; }; struct VulkanCreationInfo { @@ -53,6 +55,8 @@ struct VulkanCreationInfo { void Init(const VkGraphicsPipelineCreateInfo* pCreateInfo); void Init(const VkComputePipelineCreateInfo* pCreateInfo); + + ResourceId layout; // VkGraphicsPipelineCreateInfo VkPipelineCreateFlags flags; @@ -146,6 +150,14 @@ struct VulkanCreationInfo }; map m_Pipeline; + struct PipelineLayout + { + void Init(const VkPipelineLayoutCreateInfo* pCreateInfo); + + vector descSetLayouts; + }; + map m_PipelineLayout; + struct RenderPass { void Init(const VkRenderPassCreateInfo* pCreateInfo); @@ -153,6 +165,10 @@ struct VulkanCreationInfo vector inputAttachments; vector colorAttachments; int32_t depthstencilAttachment; + + uint32_t attachCount; + + VkRenderPass loadRP; }; map m_RenderPass; diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index bc4dab651..ace418571 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -191,7 +191,7 @@ bool WrappedVulkan::Serialise_vkBeginCommandBuffer( for(auto it=baseEvents.begin(); it != baseEvents.end(); ++it) { - if(*it < m_LastEventID && m_LastEventID < (*it + length)) + if(*it <= m_LastEventID && m_LastEventID < (*it + length)) { RDCDEBUG("vkBegin - partial detected %u < %u < %u, %llu -> %llu", *it, m_LastEventID, *it + length, cmdId, bakeId); @@ -929,13 +929,33 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorSets( (bind == VK_PIPELINE_BIND_POINT_GRAPHICS) ? m_PartialReplayData.state.graphics.descSets : m_PartialReplayData.state.compute.descSets; + + vector< vector > &offsets = + (bind == VK_PIPELINE_BIND_POINT_GRAPHICS) + ? m_PartialReplayData.state.graphics.offsets + : m_PartialReplayData.state.compute.offsets; // expand as necessary if(descsets.size() < first + numSets) + { descsets.resize(first + numSets); + offsets.resize(first + numSets); + } + const vector &descSetLayouts = m_CreationInfo.m_PipelineLayout[GetResID(layout)].descSetLayouts; + + uint32_t *offsIter = offs; + uint32_t dynConsumed = 0; + + // consume the offsets linearly along the descriptor set layouts for(uint32_t i=0; i < numSets; i++) + { descsets[first+i] = descriptorIDs[i]; + uint32_t dynCount = m_CreationInfo.m_DescSetLayout[ descSetLayouts[first+i] ].dynamicCount; + offsets[first+i].assign(offsIter, offsIter+dynCount); + dynConsumed += dynCount; + RDCASSERT(dynConsumed <= offsCount); + } // if there are dynamic offsets, bake them into the current bindings by alias'ing // the image layout member (which is never used for buffer views). diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 07c2447ff..cf491c199 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -385,6 +385,18 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass( device = GetResourceManager()->GetLiveHandle(devId); VkRenderPass rp = VK_NULL_HANDLE; + VulkanCreationInfo::RenderPass rpinfo; + rpinfo.Init(&info); + + // we want to store off the data so we can display it after the pass. + // override any user-specified DONT_CARE. + VkAttachmentDescription *att = (VkAttachmentDescription *)info.pAttachments; + for(uint32_t i=0; i < info.attachmentCount; i++) + { + att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + } + VkResult ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, &rp); if(ret != VK_SUCCESS) @@ -395,8 +407,22 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass( { ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), rp); GetResourceManager()->AddLiveResource(id, rp); + + // make a version of the render pass that loads from its attachments, + // so it can be used for replaying a single draw after a render pass + // without doing a clear or a DONT_CARE load. + for(uint32_t i=0; i < info.attachmentCount; i++) + { + att[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + } + + ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, &rpinfo.loadRP); + RDCASSERT(ret == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRP); - m_CreationInfo.m_RenderPass[live].Init(&info); + m_CreationInfo.m_RenderPass[live] = rpinfo; } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index 708ea48ac..675c0d980 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -234,7 +234,11 @@ bool WrappedVulkan::Serialise_vkQueueSubmit( m_RootEventID--; - if(m_LastEventID < m_RootEventID) + if(m_LastEventID == startEID) + { + RDCDEBUG("Queue Submit no replay %u == %u", m_LastEventID, startEID); + } + else if(m_LastEventID > startEID && m_LastEventID < m_RootEventID) { RDCDEBUG("Queue Submit partial replay %u < %u", m_LastEventID, m_RootEventID); @@ -272,7 +276,7 @@ bool WrappedVulkan::Serialise_vkQueueSubmit( ObjDisp(queue)->QueueSubmit(Unwrap(queue), (uint32_t)trimmedCmds.size(), &trimmedCmds[0], Unwrap(fence)); - for(uint32_t i=0; i < numCmds; i++) + for(uint32_t i=0; i < trimmedCmdIds.size(); i++) { ResourceId cmd = trimmedCmdIds[i]; GetResourceManager()->ApplyTransitions(m_BakedCmdBufferInfo[cmd].imgtransitions, m_ImageLayouts); diff --git a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp index 9368619bc..ac61c9118 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp @@ -53,6 +53,8 @@ bool WrappedVulkan::Serialise_vkCreatePipelineLayout( { ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), layout); GetResourceManager()->AddLiveResource(id, layout); + + m_CreationInfo.m_PipelineLayout[live].Init(&info); } } @@ -101,6 +103,8 @@ VkResult WrappedVulkan::vkCreatePipelineLayout( else { GetResourceManager()->AddLiveResource(id, *pPipelineLayout); + + m_CreationInfo.m_PipelineLayout[id].Init(pCreateInfo); } } @@ -428,6 +432,8 @@ VkResult WrappedVulkan::vkCreateGraphicsPipelines( else { GetResourceManager()->AddLiveResource(id, pPipelines[i]); + + m_CreationInfo.m_Pipeline[id].Init(&pCreateInfos[i]); } } } @@ -526,6 +532,8 @@ VkResult WrappedVulkan::vkCreateComputePipelines( else { GetResourceManager()->AddLiveResource(id, pPipelines[i]); + + m_CreationInfo.m_Pipeline[id].Init(&pCreateInfos[i]); } } }