diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 3a43f5885..c1c79dac4 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -570,7 +570,10 @@ private: void FirstFrame(VkSwapchainKHR swap); + std::vector GetImplicitRenderPassBarriers(uint32_t subpass = 0); string MakeRenderPassOpString(bool store); + void MakeSubpassLoadRP(VkRenderPassCreateInfo &info, const VkRenderPassCreateInfo *origInfo, + uint32_t s); void StartFrameCapture(void *dev, void *wnd); bool EndFrameCapture(void *dev, void *wnd); diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 44ffca5b2..45e11cd1c 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -403,14 +403,7 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, { attachments.reserve(pCreateInfo->attachmentCount); for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) - { - Attachment a; - a.loadOp = pCreateInfo->pAttachments[i].loadOp; - a.storeOp = pCreateInfo->pAttachments[i].storeOp; - a.stencilLoadOp = pCreateInfo->pAttachments[i].stencilLoadOp; - a.stencilStoreOp = pCreateInfo->pAttachments[i].stencilStoreOp; - attachments.push_back(a); - } + attachments.push_back(pCreateInfo->pAttachments[i]); subpasses.resize(pCreateInfo->subpassCount); for(uint32_t subp = 0; subp < pCreateInfo->subpassCount; subp++) @@ -419,18 +412,30 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, Subpass &dst = subpasses[subp]; dst.inputAttachments.resize(src.inputAttachmentCount); + dst.inputLayouts.resize(src.inputAttachmentCount); for(uint32_t i = 0; i < src.inputAttachmentCount; i++) + { dst.inputAttachments[i] = src.pInputAttachments[i].attachment; + dst.inputLayouts[i] = src.pInputAttachments[i].layout; + } dst.colorAttachments.resize(src.colorAttachmentCount); + dst.colorLayouts.resize(src.colorAttachmentCount); for(uint32_t i = 0; i < src.colorAttachmentCount; i++) + { dst.colorAttachments[i] = src.pColorAttachments[i].attachment; + dst.colorLayouts[i] = src.pColorAttachments[i].layout; + } dst.depthstencilAttachment = (src.pDepthStencilAttachment != NULL && src.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED ? (int32_t)src.pDepthStencilAttachment->attachment : -1); + dst.depthstencilLayout = (src.pDepthStencilAttachment != NULL && + src.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED + ? src.pDepthStencilAttachment->layout + : VK_IMAGE_LAYOUT_UNDEFINED); } } diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 5373ff3c9..51746f357 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -197,24 +197,25 @@ struct VulkanCreationInfo void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, const VkRenderPassCreateInfo *pCreateInfo); - struct Attachment - { - VkAttachmentLoadOp loadOp; - VkAttachmentStoreOp storeOp; - VkAttachmentLoadOp stencilLoadOp; - VkAttachmentStoreOp stencilStoreOp; - }; - vector attachments; + vector attachments; struct Subpass { + // these are split apart since they layout is + // rarely used but the indices are often used vector inputAttachments; vector colorAttachments; int32_t depthstencilAttachment; + + vector inputLayouts; + vector colorLayouts; + VkImageLayout depthstencilLayout; }; vector subpasses; - VkRenderPass loadRP; + // one for each subpass, as we preserve attachments + // in the layout that the subpass uses + vector loadRPs; }; map m_RenderPass; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index fbd1dec24..a04ee6bd4 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -5180,11 +5180,15 @@ byte *VulkanReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t m // if we have no tmpImage, we're copying directly from the real image if(tmpImage == VK_NULL_HANDLE) { + // ensure transfer has completed + srcimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + // image layout back to normal for(size_t si = 0; si < layouts.subresourceStates.size(); si++) { srcimBarrier.subresourceRange = layouts.subresourceStates[si].subresourceRange; srcimBarrier.newLayout = layouts.subresourceStates[si].newLayout; + srcimBarrier.dstAccessMask = MakeAccessMask(srcimBarrier.newLayout); DoPipelineBarrier(cmd, 1, &srcimBarrier); } } diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index f22997509..b47f172ae 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -748,8 +748,8 @@ VkResourceRecord::~VkResourceRecord() if(resType == eResCommandBuffer) SAFE_DELETE(cmdInfo); - if(resType == eResFramebuffer) - SAFE_DELETE(imageAttachments); + if(resType == eResFramebuffer || resType == eResRenderPass) + SAFE_DELETE_ARRAY(imageAttachments); // only the descriptor set layout actually owns this pointer, descriptor sets // have a pointer to it but don't own it diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index e0e5ad85c..fbe1ad1ef 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -871,6 +871,8 @@ struct CmdBufferRecordingInfo VkDevice device; VkCommandBufferAllocateInfo allocInfo; + VkResourceRecord *framebuffer; + vector > imgbarriers; // sparse resources referenced by this command buffer (at submit time @@ -935,6 +937,17 @@ struct MemMapState byte *refData; }; +struct AttachmentInfo +{ + VkResourceRecord *record; + + // the implicit barrier applied from initialLayout to finalLayout across a render pass + // for render passes this is partial (doesn't contain the image pointer), the image + // and subresource range are filled in when creating the framebuffer, which is what is + // used to apply the barrier in EndRenderPass + VkImageMemoryBarrier barrier; +}; + struct VkResourceRecord : public ResourceRecord { public: @@ -1046,18 +1059,82 @@ public: SwapchainInfo *swapInfo; // only for swapchains MemMapState *memMapState; // only for device memory CmdBufferRecordingInfo *cmdInfo; // only for command buffers - VkResourceRecord **imageAttachments; // only for framebuffers + AttachmentInfo *imageAttachments; // only for framebuffers and render passes DescriptorSetData *descInfo; // only for descriptor sets and descriptor set layouts }; VkResourceRecord *bakedCommands; - static const int MaxImageAttachments = 9; // 8 Colour and 1 Depth + static const int MaxImageAttachments = 17; // 8 Input, 8 Colour and 1 Depth // pointer to either the pool this item is allocated from, or the children allocated // from this pool. Protected by the chunk lock VkResourceRecord *pool; vector pooledChildren; + + // we only need a couple of bytes to store the view's range, + // so just pack/unpack into bitfields + struct ViewRange + { + ViewRange &operator=(const VkImageSubresourceRange &range) + { + aspectMask = (uint32_t)range.aspectMask; + baseMipLevel = range.baseMipLevel; + baseArrayLayer = range.baseArrayLayer; + + if(range.levelCount == VK_REMAINING_MIP_LEVELS) + levelCount = MipMaxValue; + else + levelCount = range.levelCount; + + if(range.layerCount == VK_REMAINING_ARRAY_LAYERS) + layerCount = SliceMaxValue; + else + layerCount = range.layerCount; + + return *this; + } + + operator VkImageSubresourceRange() const + { + VkImageSubresourceRange ret; + + ret.aspectMask = (VkImageAspectFlags)aspectMask; + ret.baseMipLevel = baseMipLevel; + ret.baseArrayLayer = baseArrayLayer; + + if(levelCount == MipMaxValue) + ret.levelCount = VK_REMAINING_MIP_LEVELS; + else + ret.levelCount = levelCount; + + if(layerCount == SliceMaxValue) + ret.layerCount = VK_REMAINING_ARRAY_LAYERS; + else + ret.layerCount = layerCount; + + return ret; + } + + // only need 4 bits for the aspects + uint32_t aspectMask : 4; + + // 6 bits = refer to up to 62 mips = bloody huge textures. + // note we also need to pack in VK_REMAINING_MIPS etc so we can't + // necessarily use the maximum levelCount value (64) + uint32_t baseMipLevel : 6; + uint32_t levelCount : 6; + + static const uint32_t MipMaxValue = 0x3f; + + // 16 bits = refer to up to 64k array layers. This is less + // future proof than above, but at time of writing typical + // maxImageArrayLayers is 2048. + uint32_t baseArrayLayer : 16; + uint32_t layerCount : 16; + + static const uint32_t SliceMaxValue = 0xffff; + } viewRange; }; struct ImageLayouts diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index 90c87c392..70c1abc33 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -98,7 +98,7 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd) VkRenderPassBeginInfo rpbegin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, - Unwrap(m_CreationInfo->m_RenderPass[renderPass].loadRP), + Unwrap(m_CreationInfo->m_RenderPass[renderPass].loadRPs[subpass]), Unwrap(GetResourceManager()->GetCurrentHandle(framebuffer)), renderArea, (uint32_t)m_CreationInfo->m_RenderPass[renderPass].attachments.size(), @@ -106,9 +106,6 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd) }; ObjDisp(cmd)->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); - for(uint32_t i = 0; i < subpass; i++) - ObjDisp(cmd)->CmdNextSubpass(Unwrap(cmd), VK_SUBPASS_CONTENTS_INLINE); - BindPipeline(cmd); if(ibuffer.buf != ResourceId()) @@ -306,10 +303,5 @@ void VulkanRenderState::BindPipeline(VkCommandBuffer cmd) void VulkanRenderState::EndRenderPass(VkCommandBuffer cmd) { - uint32_t numSubpasses = (uint32_t)m_CreationInfo->m_RenderPass[renderPass].subpasses.size(); - - for(uint32_t sub = subpass; sub < numSubpasses - 1; sub++) - ObjDisp(cmd)->CmdNextSubpass(Unwrap(cmd), VK_SUBPASS_CONTENTS_INLINE); - ObjDisp(cmd)->CmdEndRenderPass(Unwrap(cmd)); } diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 5656465fb..fe740c4d8 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -24,6 +24,155 @@ #include "../vk_core.h" +std::vector WrappedVulkan::GetImplicitRenderPassBarriers(uint32_t subpass) +{ + ResourceId rp, fb; + + if(m_State == EXECUTING) + { + rp = m_RenderState.renderPass; + fb = m_RenderState.framebuffer; + } + else + { + rp = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass; + fb = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer; + } + + std::vector ret; + + VulkanCreationInfo::Framebuffer fbinfo = m_CreationInfo.m_Framebuffer[fb]; + VulkanCreationInfo::RenderPass rpinfo = m_CreationInfo.m_RenderPass[rp]; + + std::vector atts; + + // a bit of dancing to get a subpass index. Because we don't increment + // the subpass counter on EndRenderPass the value is the same for the last + // NextSubpass. Instead we pass in the subpass index of ~0U for End + if(subpass == ~0U) + { + // we transition all attachments to finalLayout from whichever they + // were in previously + atts.resize(rpinfo.attachments.size()); + for(size_t i = 0; i < rpinfo.attachments.size(); i++) + { + atts[i].attachment = (uint32_t)i; + atts[i].layout = rpinfo.attachments[i].finalLayout; + } + } + else + { + if(m_State == EXECUTING) + subpass = m_RenderState.subpass; + else + subpass = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass; + + // transition the attachments in this subpass + for(size_t i = 0; i < rpinfo.subpasses[subpass].colorAttachments.size(); i++) + { + atts.push_back(VkAttachmentReference()); + atts.back().attachment = rpinfo.subpasses[subpass].colorAttachments[i]; + atts.back().layout = rpinfo.subpasses[subpass].colorLayouts[i]; + } + + for(size_t i = 0; i < rpinfo.subpasses[subpass].inputAttachments.size(); i++) + { + atts.push_back(VkAttachmentReference()); + atts.back().attachment = rpinfo.subpasses[subpass].inputAttachments[i]; + atts.back().layout = rpinfo.subpasses[subpass].inputLayouts[i]; + } + + int32_t ds = rpinfo.subpasses[subpass].depthstencilAttachment; + + if(ds != -1) + { + atts.push_back(VkAttachmentReference()); + atts.back().attachment = (uint32_t)rpinfo.subpasses[subpass].depthstencilAttachment; + atts.back().layout = rpinfo.subpasses[subpass].depthstencilLayout; + } + } + + for(size_t i = 0; i < atts.size(); i++) + { + uint32_t idx = atts[i].attachment; + + VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + + ResourceId view = fbinfo.attachments[idx].view; + + barrier.subresourceRange = m_CreationInfo.m_ImageView[view].range; + barrier.image = Unwrap( + GetResourceManager()->GetCurrentHandle(m_CreationInfo.m_ImageView[view].image)); + + barrier.newLayout = atts[i].layout; + + // search back from this subpass to see which layout it was in before. If it's + // not been used in a previous subpass, then default to initialLayout + barrier.oldLayout = rpinfo.attachments[idx].initialLayout; + + if(subpass == ~0U) + subpass = (uint32_t)rpinfo.subpasses.size(); + + // subpass is at this point a 1-indexed value essentially, as it's the index + // of the subpass we just finished (or 0 if we're in BeginRenderPass in which + // case the loop just skips completely and we use initialLayout, which is + // correct). + + for(uint32_t s = subpass; s > 0; s--) + { + bool found = false; + + for(size_t a = 0; !found && a < rpinfo.subpasses[s - 1].colorAttachments.size(); a++) + { + if(rpinfo.subpasses[s - 1].colorAttachments[a] == idx) + { + barrier.oldLayout = rpinfo.subpasses[s - 1].colorLayouts[a]; + found = true; + break; + } + } + + if(found) + break; + + for(size_t a = 0; !found && a < rpinfo.subpasses[s - 1].inputAttachments.size(); a++) + { + if(rpinfo.subpasses[s - 1].inputAttachments[a] == idx) + { + barrier.oldLayout = rpinfo.subpasses[s - 1].inputLayouts[a]; + found = true; + break; + } + } + + if(found) + break; + + if((uint32_t)rpinfo.subpasses[s - 1].depthstencilAttachment == idx) + { + barrier.oldLayout = rpinfo.subpasses[s - 1].depthstencilLayout; + break; + } + } + + ReplacePresentableImageLayout(barrier.oldLayout); + ReplacePresentableImageLayout(barrier.newLayout); + + ret.push_back(barrier); + } + + // erase any do-nothing barriers + for(auto it = ret.begin(); it != ret.end();) + { + if(it->oldLayout == it->newLayout) + it = ret.erase(it); + else + ++it; + } + + return ret; +} + string WrappedVulkan::MakeRenderPassOpString(bool store) { string opDesc = ""; @@ -33,7 +182,7 @@ string WrappedVulkan::MakeRenderPassOpString(bool store) const VulkanCreationInfo::Framebuffer &fbinfo = m_CreationInfo.m_Framebuffer[m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer]; - const vector &atts = info.attachments; + const vector &atts = info.attachments; if(atts.empty()) { @@ -680,6 +829,12 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(Serialiser *localSerialiser, m_RenderState.renderPass = GetResourceManager()->GetNonDispWrapper(beginInfo.renderPass)->id; m_RenderState.framebuffer = GetResourceManager()->GetNonDispWrapper(beginInfo.framebuffer)->id; m_RenderState.renderArea = beginInfo.renderArea; + + std::vector imgBarriers = GetImplicitRenderPassBarriers(); + + ResourceId cmd = GetResID(commandBuffer); + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, + (uint32_t)imgBarriers.size(), &imgBarriers[0]); } } else if(m_State == READING) @@ -695,6 +850,12 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(Serialiser *localSerialiser, m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer = GetResourceManager()->GetNonDispWrapper(beginInfo.framebuffer)->id; + std::vector imgBarriers = GetImplicitRenderPassBarriers(); + + ResourceId cmd = GetResID(commandBuffer); + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, + (uint32_t)imgBarriers.size(), &imgBarriers[0]); + const string desc = localSerialiser->GetDebugStr(); string opDesc = MakeRenderPassOpString(false); @@ -738,16 +899,19 @@ void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, record->MarkResourceFrameReferenced(fb->GetResourceID(), eFrameRef_Read); for(size_t i = 0; i < VkResourceRecord::MaxImageAttachments; i++) { - if(fb->imageAttachments[i] == NULL) + VkResourceRecord *att = fb->imageAttachments[i].record; + if(att == NULL) break; - record->MarkResourceFrameReferenced(fb->imageAttachments[i]->baseResource, eFrameRef_Write); - if(fb->imageAttachments[i]->baseResourceMem != ResourceId()) - record->MarkResourceFrameReferenced(fb->imageAttachments[i]->baseResourceMem, eFrameRef_Read); - if(fb->imageAttachments[i]->sparseInfo) - record->cmdInfo->sparse.insert(fb->imageAttachments[i]->sparseInfo); - record->cmdInfo->dirtied.insert(fb->imageAttachments[i]->baseResource); + record->MarkResourceFrameReferenced(att->baseResource, eFrameRef_Write); + if(att->baseResourceMem != ResourceId()) + record->MarkResourceFrameReferenced(att->baseResourceMem, eFrameRef_Read); + if(att->sparseInfo) + record->cmdInfo->sparse.insert(att->sparseInfo); + record->cmdInfo->dirtied.insert(att->baseResource); } + + record->cmdInfo->framebuffer = fb; } } @@ -772,6 +936,12 @@ bool WrappedVulkan::Serialise_vkCmdNextSubpass(Serialiser *localSerialiser, m_RenderState.subpass++; ObjDisp(commandBuffer)->CmdNextSubpass(Unwrap(commandBuffer), cont); + + std::vector imgBarriers = GetImplicitRenderPassBarriers(); + + ResourceId cmd = GetResID(commandBuffer); + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, + (uint32_t)imgBarriers.size(), &imgBarriers[0]); } } else if(m_State == READING) @@ -783,6 +953,12 @@ bool WrappedVulkan::Serialise_vkCmdNextSubpass(Serialiser *localSerialiser, // track while reading, for fetching the right set of outputs in AddDrawcall m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass++; + std::vector imgBarriers = GetImplicitRenderPassBarriers(); + + ResourceId cmd = GetResID(commandBuffer); + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, + (uint32_t)imgBarriers.size(), &imgBarriers[0]); + const string desc = localSerialiser->GetDebugStr(); AddEvent(NEXT_SUBPASS, desc); @@ -833,6 +1009,12 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass(Serialiser *localSerialiser, m_Partial[Primary].renderPassActive = false; ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer)); + + std::vector imgBarriers = GetImplicitRenderPassBarriers(~0U); + + ResourceId cmd = GetResID(commandBuffer); + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, + (uint32_t)imgBarriers.size(), &imgBarriers[0]); } } else if(m_State == READING) @@ -841,6 +1023,12 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass(Serialiser *localSerialiser, ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer)); + std::vector imgBarriers = GetImplicitRenderPassBarriers(~0U); + + ResourceId cmd = GetResID(commandBuffer); + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, + (uint32_t)imgBarriers.size(), &imgBarriers[0]); + const string desc = localSerialiser->GetDebugStr(); string opDesc = MakeRenderPassOpString(true); @@ -877,6 +1065,25 @@ void WrappedVulkan::vkCmdEndRenderPass(VkCommandBuffer commandBuffer) Serialise_vkCmdEndRenderPass(localSerialiser, commandBuffer); record->AddChunk(scope.Get()); + + VkResourceRecord *fb = record->cmdInfo->framebuffer; + + std::vector barriers; + + for(size_t i = 0; i < VkResourceRecord::MaxImageAttachments; i++) + { + if(fb->imageAttachments[i].barrier.oldLayout == fb->imageAttachments[i].barrier.newLayout) + continue; + + barriers.push_back(fb->imageAttachments[i].barrier); + } + + // apply the implicit layout transitions here + { + SCOPED_LOCK(m_ImageLayoutsLock); + GetResourceManager()->RecordBarriers(GetRecord(commandBuffer)->cmdInfo->imgbarriers, + m_ImageLayouts, (uint32_t)barriers.size(), &barriers[0]); + } } } @@ -1635,7 +1842,7 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier( (uint32_t)bufBarriers.size(), &bufBarriers[0], (uint32_t)imgBarriers.size(), &imgBarriers[0]); - ResourceId cmd = GetResID(RerecordCmdBuf(cmdid)); + ResourceId cmd = GetResID(commandBuffer); GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, (uint32_t)imgBarriers.size(), &imgBarriers[0]); } diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 523a9a8f8..0c2296ac6 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -24,6 +24,40 @@ #include "../vk_core.h" +void WrappedVulkan::MakeSubpassLoadRP(VkRenderPassCreateInfo &info, + const VkRenderPassCreateInfo *origInfo, uint32_t s) +{ + info.subpassCount = 1; + info.pSubpasses = origInfo->pSubpasses + s; + + // remove any dependencies + info.dependencyCount = 0; + + const VkSubpassDescription *sub = info.pSubpasses; + VkAttachmentDescription *att = (VkAttachmentDescription *)info.pAttachments; + + // apply this subpass's attachment layouts to the initial and final layouts + // so that this RP doesn't perform any layout transitions + for(uint32_t a = 0; a < sub->colorAttachmentCount; a++) + { + att[sub->pColorAttachments[a].attachment].initialLayout = + att[sub->pColorAttachments[a].attachment].finalLayout = sub->pColorAttachments[a].layout; + } + + for(uint32_t a = 0; a < sub->inputAttachmentCount; a++) + { + att[sub->pInputAttachments[a].attachment].initialLayout = + att[sub->pInputAttachments[a].attachment].finalLayout = sub->pInputAttachments[a].layout; + } + + if(sub->pDepthStencilAttachment && sub->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) + { + att[sub->pDepthStencilAttachment->attachment].initialLayout = + att[sub->pDepthStencilAttachment->attachment].finalLayout = + sub->pDepthStencilAttachment->layout; + } +} + // note, for threading reasons we ensure to release the wrappers before // releasing the underlying object. Otherwise after releasing the vulkan object // that same handle could be returned by create on another thread, and we @@ -501,20 +535,26 @@ VkResult WrappedVulkan::vkCreateFramebuffer(VkDevice device, VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pFramebuffer); record->AddChunk(chunk); - record->imageAttachments = new VkResourceRecord *[VkResourceRecord::MaxImageAttachments]; + record->imageAttachments = new AttachmentInfo[VkResourceRecord::MaxImageAttachments]; RDCASSERT(pCreateInfo->attachmentCount <= VkResourceRecord::MaxImageAttachments); RDCEraseMem(record->imageAttachments, - sizeof(VkResourceRecord *) * VkResourceRecord::MaxImageAttachments); + sizeof(AttachmentInfo) * VkResourceRecord::MaxImageAttachments); + + VkResourceRecord *rpRecord = GetRecord(pCreateInfo->renderPass); + + record->AddParent(rpRecord); - if(pCreateInfo->renderPass != VK_NULL_HANDLE) - record->AddParent(GetRecord(pCreateInfo->renderPass)); for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { VkResourceRecord *attRecord = GetRecord(pCreateInfo->pAttachments[i]); record->AddParent(attRecord); - record->imageAttachments[i] = attRecord; + record->imageAttachments[i].record = attRecord; + record->imageAttachments[i].barrier = rpRecord->imageAttachments[i].barrier; + record->imageAttachments[i].barrier.image = + GetResourceManager()->GetCurrentHandle(attRecord->baseResource); + record->imageAttachments[i].barrier.subresourceRange = attRecord->viewRange; } } else @@ -547,12 +587,20 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(Serialiser *localSerialiser, Vk // we want to store off the data so we can display it after the pass. // override any user-specified DONT_CARE. + // Likewise we don't want to throw away data before we're ready, so change + // any load ops to LOAD instead of DONT_CARE (which is valid!). We of course + // leave any LOAD_OP_CLEAR alone. 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; + if(att[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) + att[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + if(att[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) + att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + // renderpass can't start or end in presentable layout on replay ReplacePresentableImageLayout(att[i].initialLayout); ReplacePresentableImageLayout(att[i].finalLayout); @@ -593,28 +641,39 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(Serialiser *localSerialiser, Vk att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; } - ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRP); - RDCASSERTEQUAL(ret, VK_SUCCESS); + VkRenderPassCreateInfo loadInfo = info; - // handle the loadRP being a duplicate - if(GetResourceManager()->HasWrapper(ToTypedHandle(rpinfo.loadRP))) + rpinfo.loadRPs.resize(info.subpassCount); + + // create a render pass for each subpass that maintains attachment layouts + for(uint32_t s = 0; s < info.subpassCount; s++) { - // just fetch the existing wrapped object - rpinfo.loadRP = - (VkRenderPass)(uint64_t)GetResourceManager()->GetNonDispWrapper(rpinfo.loadRP); + MakeSubpassLoadRP(loadInfo, &info, s); - // destroy this instance of the duplicate, as we must have matching create/destroy - // calls and there won't be a wrapped resource hanging around to destroy this one. - ObjDisp(device)->DestroyRenderPass(Unwrap(device), rpinfo.loadRP, NULL); + ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRPs[s]); + RDCASSERTEQUAL(ret, VK_SUCCESS); - // don't need to ReplaceResource as no IDs are involved - } - else - { - ResourceId loadRPid = GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRP); + // handle the loadRP being a duplicate + if(GetResourceManager()->HasWrapper(ToTypedHandle(rpinfo.loadRPs[s]))) + { + // just fetch the existing wrapped object + rpinfo.loadRPs[s] = + (VkRenderPass)(uint64_t)GetResourceManager()->GetNonDispWrapper(rpinfo.loadRPs[s]); - // register as a live-only resource, so it is cleaned up properly - GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRP); + // destroy this instance of the duplicate, as we must have matching create/destroy + // calls and there won't be a wrapped resource hanging around to destroy this one. + ObjDisp(device)->DestroyRenderPass(Unwrap(device), rpinfo.loadRPs[s], NULL); + + // don't need to ReplaceResource as no IDs are involved + } + else + { + ResourceId loadRPid = + GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRPs[s]); + + // register as a live-only resource, so it is cleaned up properly + GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRPs[s]); + } } m_CreationInfo.m_RenderPass[live] = rpinfo; @@ -651,6 +710,20 @@ VkResult WrappedVulkan::vkCreateRenderPass(VkDevice device, const VkRenderPassCr VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pRenderPass); record->AddChunk(chunk); + + record->imageAttachments = new AttachmentInfo[VkResourceRecord::MaxImageAttachments]; + RDCASSERT(pCreateInfo->attachmentCount <= VkResourceRecord::MaxImageAttachments); + + RDCEraseMem(record->imageAttachments, + sizeof(AttachmentInfo) * VkResourceRecord::MaxImageAttachments); + + for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) + { + record->imageAttachments[i].record = NULL; + record->imageAttachments[i].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + record->imageAttachments[i].barrier.oldLayout = pCreateInfo->pAttachments[i].initialLayout; + record->imageAttachments[i].barrier.newLayout = pCreateInfo->pAttachments[i].finalLayout; + } } else { @@ -676,13 +749,21 @@ VkResult WrappedVulkan::vkCreateRenderPass(VkDevice device, const VkRenderPassCr info.pAttachments = atts; - ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRP); - RDCASSERTEQUAL(ret, VK_SUCCESS); + rpinfo.loadRPs.resize(pCreateInfo->subpassCount); - ResourceId loadRPid = GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRP); + // create a render pass for each subpass that maintains attachment layouts + for(uint32_t s = 0; s < pCreateInfo->subpassCount; s++) + { + MakeSubpassLoadRP(info, pCreateInfo, s); - // register as a live-only resource, so it is cleaned up properly - GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRP); + ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRPs[s]); + RDCASSERTEQUAL(ret, VK_SUCCESS); + + ResourceId loadRPid = GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRPs[s]); + + // register as a live-only resource, so it is cleaned up properly + GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRPs[s]); + } m_CreationInfo.m_RenderPass[id] = rpinfo; } diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index 6833e7161..582ec94d2 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -1254,6 +1254,7 @@ VkResult WrappedVulkan::vkCreateImageView(VkDevice device, const VkImageViewCrea record->baseResource = imageRecord->GetResourceID(); record->baseResourceMem = imageRecord->baseResource; record->sparseInfo = imageRecord->sparseInfo; + record->viewRange = pCreateInfo->subresourceRange; } else {