From 50438987df78d66d40a5eb75446b9f97896ce432 Mon Sep 17 00:00:00 2001 From: Aliya Pazylbekova Date: Wed, 20 May 2020 13:49:26 -0400 Subject: [PATCH] Vk Pixel History: depth images history Add support for requesting pixel history for depth/stencil images. Also, adjust which index is used to patch primitive ID and fixed fragment color shaders. Before it was using the index of the target image in the framebuffer attachments. But it should be the index of the corresponding color attachment. Add support for other depth/stencil formats (other than D32_SFLOAT). Remember the depth/stencil attachment format to correctly update the values. --- renderdoc/driver/vulkan/vk_pixelhistory.cpp | 436 ++++++++++++-------- util/test/tests/Vulkan/VK_Pixel_History.py | 36 ++ 2 files changed, 292 insertions(+), 180 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_pixelhistory.cpp b/renderdoc/driver/vulkan/vk_pixelhistory.cpp index 05d48dc45..efffdaa99 100644 --- a/renderdoc/driver/vulkan/vk_pixelhistory.cpp +++ b/renderdoc/driver/vulkan/vk_pixelhistory.cpp @@ -60,7 +60,6 @@ enum struct CopyPixelParams { - bool depthCopy; VkImage srcImage; VkFormat srcImageFormat; VkImageLayout srcImageLayout; @@ -556,14 +555,14 @@ protected: // depth-stencil attachment, and covers a single subpass. This will be used to replay // a single draw. The new renderpass also replaces the depth stencil attachment, so // it can be used to count the number of fragments. Optionally, the new renderpass - // changes the format for the color image that corresponds to subImage. - VkRenderPass CreateRenderPass(ResourceId rp, ResourceId fb, uint32_t subpassIdx, - VkImage subImage = VK_NULL_HANDLE, - VkFormat newFormat = VK_FORMAT_UNDEFINED) + // changes the format for the color image that corresponds to colorIdx attachment. + VkRenderPass CreateRenderPass(ResourceId rp, VkFormat newColorFormat = VK_FORMAT_UNDEFINED, + uint32_t colorIdx = 0) { const VulkanCreationInfo::RenderPass &rpInfo = m_pDriver->GetDebugManager()->GetRenderPassInfo(rp); - const VulkanCreationInfo::RenderPass::Subpass &sub = rpInfo.subpasses[subpassIdx]; + // Currently only single subpass render passes are supported. + const VulkanCreationInfo::RenderPass::Subpass &sub = rpInfo.subpasses.front(); // Copy color and input attachments, and ignore resolve attachments. // Since we are only using this renderpass to replay a single draw, we don't @@ -650,13 +649,34 @@ protected: subpassDesc.pDepthStencilAttachment = &dsAttachment; // If needed substitute the color attachment with the new format. - const VulkanCreationInfo::Framebuffer &fbInfo = - m_pDriver->GetDebugManager()->GetFramebufferInfo(fb); - for(uint32_t i = 0; i < fbInfo.attachments.size(); i++) + if(newColorFormat != VK_FORMAT_UNDEFINED) { - if(m_pDriver->GetDebugManager()->GetImageViewInfo(fbInfo.attachments[i].createdView).image == - GetResID(subImage)) - descs[i].format = newFormat; + if(colorIdx < descs.size()) + { + // It is an existing attachment. + descs[colorIdx].format = newColorFormat; + } + else + { + // We are adding a new color attachment. + VkAttachmentReference attRef = {}; + attRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attRef.attachment = colorIdx; + colorAttachments.push_back(attRef); + subpassDesc.colorAttachmentCount = (uint32_t)colorAttachments.size(); + subpassDesc.pColorAttachments = colorAttachments.data(); + + VkAttachmentDescription attDesc = {}; + attDesc.format = newColorFormat; + attDesc.samples = m_CallbackInfo.samples; + attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + descs.push_back(attDesc); + } } VkRenderPassCreateInfo rpCreateInfo = {}; @@ -678,14 +698,15 @@ protected: // CreateFrambuffer creates a new VkFramebuffer that is based on the original, but // substitutes the depth stencil image view. If there is no depth stencil attachment, - // it will be added. Optionally, also substitutes the original target image view. - VkFramebuffer CreateFramebuffer(ResourceId rp, VkRenderPass newRp, uint32_t subpassIndex, - ResourceId origFb, VkImageView newDsImageView, - VkImageView newImageView = VK_NULL_HANDLE) + // it will be added. Optionally, also substitutes the original target image view with + // the newColorAtt. + VkFramebuffer CreateFramebuffer(ResourceId rp, VkRenderPass newRp, ResourceId origFb, + VkImageView newColorAtt = VK_NULL_HANDLE, uint32_t colorIdx = 0) { const VulkanCreationInfo::RenderPass &rpInfo = m_pDriver->GetDebugManager()->GetRenderPassInfo(rp); - const VulkanCreationInfo::RenderPass::Subpass &sub = rpInfo.subpasses[subpassIndex]; + // Currently only single subpass render passes are supported. + const VulkanCreationInfo::RenderPass::Subpass &sub = rpInfo.subpasses.front(); const VulkanCreationInfo::Framebuffer &fbInfo = m_pDriver->GetDebugManager()->GetFramebufferInfo(origFb); rdcarray atts(fbInfo.attachments.size()); @@ -694,19 +715,23 @@ protected: { atts[i] = m_pDriver->GetResourceManager()->GetCurrentHandle( fbInfo.attachments[i].createdView); - if(newImageView != VK_NULL_HANDLE) - { - ResourceId img = - m_pDriver->GetDebugManager()->GetImageViewInfo(fbInfo.attachments[i].createdView).image; - if(img == GetResID(m_CallbackInfo.targetImage)) - atts[i] = newImageView; - } } + // Either modify the existing color attachment view, or add a new one. + if(newColorAtt != VK_NULL_HANDLE) + { + if(colorIdx < atts.size()) + atts[colorIdx] = newColorAtt; + else + atts.push_back(newColorAtt); + } + + // Either modify the existing depth stencil attachment, or add one. if(sub.depthstencilAttachment != -1) - atts[sub.depthstencilAttachment] = newDsImageView; + atts[sub.depthstencilAttachment] = m_CallbackInfo.dsImageView; else - atts.push_back(newDsImageView); + atts.push_back(m_CallbackInfo.dsImageView); + VkFramebufferCreateInfo fbCI = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; fbCI.renderPass = newRp; fbCI.attachmentCount = (uint32_t)atts.size(); @@ -725,7 +750,8 @@ protected: void CopyImagePixel(VkCommandBuffer cmd, CopyPixelParams &p, size_t offset) { VkImageAspectFlags aspectFlags = 0; - if(p.depthCopy) + bool depthCopy = IsDepthOrStencilFormat(p.srcImageFormat); + if(depthCopy) { if(IsDepthOnlyFormat(p.srcImageFormat) || IsDepthAndStencilFormat(p.srcImageFormat)) aspectFlags |= VK_IMAGE_ASPECT_DEPTH_BIT; @@ -761,7 +787,7 @@ protected: VK_ACCESS_SHADER_READ_BIT, p.srcImageLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(p.srcImage), subresource}; - if(p.depthCopy && p.srcImage != m_CallbackInfo.dsImage) + if(depthCopy && p.srcImage != m_CallbackInfo.dsImage) { // This is an original depth image that is used in a draw. // The descriptor for MSAA copy has the dsImage created for pixel history. @@ -808,7 +834,7 @@ protected: m_pDriver->GetReplay()->CopyPixelForPixelHistory( cmd, {(int32_t)m_CallbackInfo.x, (int32_t)m_CallbackInfo.y}, - m_CallbackInfo.targetSubresource.sample, (uint32_t)offset / 16, p.depthCopy); + m_CallbackInfo.targetSubresource.sample, (uint32_t)offset / 16, depthCopy); // Transition src image back to its layout. barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; @@ -835,7 +861,7 @@ protected: region.imageSubresource.mipLevel = baseMip; region.imageSubresource.layerCount = 1; - if(!p.depthCopy) + if(!depthCopy) { region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; regions.push_back(region); @@ -883,6 +909,37 @@ protected: return (rpInfo.subpasses.size() > 1); } + // Returns teh color attachment index that corresponds to the target image for + // pixel history. + uint32_t GetColorAttachmentIndex(const VulkanRenderState &renderstate) + { + if(IsDepthOrStencilFormat(m_CallbackInfo.targetImageFormat)) + return 0; + + uint32_t framebufferIndex = 0; + const rdcarray &atts = renderstate.GetFramebufferAttachments(); + + for(uint32_t i = 0; i < atts.size(); i++) + { + ResourceId img = m_pDriver->GetDebugManager()->GetImageViewInfo(atts[i]).image; + if(img == GetResID(m_CallbackInfo.targetImage)) + { + framebufferIndex = i; + break; + } + } + + const VulkanCreationInfo::RenderPass &rpInfo = + m_pDriver->GetDebugManager()->GetRenderPassInfo(renderstate.renderPass); + const VulkanCreationInfo::RenderPass::Subpass &sub = rpInfo.subpasses.front(); + for(uint32_t i = 0; i < sub.colorAttachments.size(); i++) + { + if(framebufferIndex == sub.colorAttachments[i]) + return i; + } + return 0; + } + WrappedVulkan *m_pDriver; PixelHistoryShaderCache *m_ShaderCache; PixelHistoryCallbackInfo m_CallbackInfo; @@ -918,18 +975,8 @@ struct VulkanOcclusionCallback : public VulkanPixelHistoryCallback VulkanRenderState prevState = m_pDriver->GetCmdRenderState(); VulkanRenderState &pipestate = m_pDriver->GetCmdRenderState(); - uint32_t framebufferIndex = 0; - const rdcarray &atts = pipestate.GetFramebufferAttachments(); - for(uint32_t i = 0; i < atts.size(); i++) - { - ResourceId img = m_pDriver->GetDebugManager()->GetImageViewInfo(atts[i]).image; - if(img == GetResID(m_CallbackInfo.targetImage)) - { - framebufferIndex = i; - break; - } - } - VkPipeline pipe = GetPixelOcclusionPipeline(eid, prevState.graphics.pipeline, framebufferIndex); + VkPipeline pipe = GetPixelOcclusionPipeline(eid, prevState.graphics.pipeline, + GetColorAttachmentIndex(prevState)); // set the scissor for(uint32_t i = 0; i < pipestate.views.size(); i++) ScissorToPixel(pipestate.views[i], pipestate.scissors[i]); @@ -1013,7 +1060,7 @@ private: m_OcclusionQueries.insert(std::make_pair(eventId, occlIndex)); } - VkPipeline GetPixelOcclusionPipeline(uint32_t eid, ResourceId pipeline, uint32_t framebufferIndex) + VkPipeline GetPixelOcclusionPipeline(uint32_t eid, ResourceId pipeline, uint32_t outputIndex) { auto it = m_PipeCache.find(pipeline); if(it != m_PipeCache.end()) @@ -1027,7 +1074,7 @@ private: { if(stages[i].stage == VK_SHADER_STAGE_FRAGMENT_BIT) { - stages[i].module = m_ShaderCache->GetFixedColShader(framebufferIndex); + stages[i].module = m_ShaderCache->GetFixedColShader(outputIndex); stages[i].pName = "main"; break; } @@ -1090,19 +1137,8 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback // Get pre-modification values size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - VkImage depthImage = VK_NULL_HANDLE; - VkFormat depthFormat = VK_FORMAT_UNDEFINED; - const DrawcallDescription *draw = m_pDriver->GetDrawcall(eid); - if(draw && draw->depthOut != ResourceId()) - { - ResourceId resId = m_pDriver->GetResourceManager()->GetLiveID(draw->depthOut); - depthImage = m_pDriver->GetResourceManager()->GetCurrentHandle(resId); - const VulkanCreationInfo::Image &imginfo = m_pDriver->GetDebugManager()->GetImageInfo(resId); - depthFormat = imginfo.format; - } - CopyPixel(m_CallbackInfo.targetImage, m_CallbackInfo.targetImageFormat, depthImage, depthFormat, - cmd, storeOffset); + CopyPixel(eid, cmd, storeOffset); ResourceId prevRenderpass = pipestate.renderPass; ResourceId prevFramebuffer = pipestate.GetFramebuffer(); @@ -1111,23 +1147,12 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback uint32_t prevSubpass = pipestate.subpass; { - VkRenderPass newRp = - CreateRenderPass(pipestate.renderPass, pipestate.GetFramebuffer(), pipestate.subpass); - VkFramebuffer newFb = CreateFramebuffer(pipestate.renderPass, newRp, pipestate.subpass, - pipestate.GetFramebuffer(), m_CallbackInfo.dsImageView); - uint32_t framebufferIndex = 0; - const rdcarray &atts = pipestate.GetFramebufferAttachments(); - for(uint32_t i = 0; i < atts.size(); i++) - { - ResourceId img = m_pDriver->GetDebugManager()->GetImageViewInfo(atts[i]).image; - if(img == GetResID(m_CallbackInfo.targetImage)) - { - framebufferIndex = i; - break; - } - } - PipelineReplacements replacements = - GetPipelineReplacements(eid, pipestate.graphics.pipeline, newRp, framebufferIndex); + VkRenderPass newRp = CreateRenderPass(pipestate.renderPass); + VkFramebuffer newFb = + CreateFramebuffer(pipestate.renderPass, newRp, pipestate.GetFramebuffer()); + + PipelineReplacements replacements = GetPipelineReplacements( + eid, pipestate.graphics.pipeline, newRp, GetColorAttachmentIndex(prevState)); for(uint32_t i = 0; i < pipestate.views.size(); i++) ScissorToPixel(pipestate.views[i], pipestate.scissors[i]); @@ -1149,7 +1174,6 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback params.srcImage = m_CallbackInfo.dsImage; params.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; params.srcImageFormat = m_CallbackInfo.dsFormat; - params.depthCopy = true; // Copy stencil value that indicates the number of fragments ignoring // shader discard. CopyImagePixel(cmd, params, storeOffset + offsetof(struct EventInfo, dsWithoutShaderDiscard)); @@ -1193,19 +1217,8 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback m_pDriver->GetCmdRenderState().EndRenderPass(cmd); size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - VkImage depthImage = VK_NULL_HANDLE; - VkFormat depthFormat = VK_FORMAT_UNDEFINED; - const DrawcallDescription *draw = m_pDriver->GetDrawcall(eid); - if(draw && draw->depthOut != ResourceId()) - { - ResourceId resId = m_pDriver->GetResourceManager()->GetLiveID(draw->depthOut); - depthImage = m_pDriver->GetResourceManager()->GetCurrentHandle(resId); - const VulkanCreationInfo::Image &imginfo = m_pDriver->GetDebugManager()->GetImageInfo(resId); - depthFormat = imginfo.format; - } - CopyPixel(m_CallbackInfo.targetImage, m_CallbackInfo.targetImageFormat, depthImage, depthFormat, - cmd, storeOffset + offsetof(struct EventInfo, postmod)); + CopyPixel(eid, cmd, storeOffset + offsetof(struct EventInfo, postmod)); m_pDriver->GetCmdRenderState().BeginRenderPassAndApplyState(m_pDriver, cmd, VulkanRenderState::BindGraphics); @@ -1252,8 +1265,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback // Copy size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(m_CallbackInfo.targetImage, m_CallbackInfo.targetImageFormat, VK_NULL_HANDLE, - VK_FORMAT_UNDEFINED, cmd, storeOffset); + CopyPixel(eventId, cmd, storeOffset); m_EventIndices.insert(std::make_pair(eventId, m_EventIndices.size())); m_pDriver->GetCmdRenderState().BeginRenderPassAndApplyState(m_pDriver, cmd, @@ -1300,8 +1312,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback storeOffset = m_EventIndices.size() * sizeof(EventInfo); m_EventIndices.insert(std::make_pair(eventId, m_EventIndices.size())); } - CopyPixel(m_CallbackInfo.targetImage, m_CallbackInfo.targetImageFormat, VK_NULL_HANDLE, - VK_FORMAT_UNDEFINED, cmd, storeOffset + offsetof(struct EventInfo, postmod)); + CopyPixel(eventId, cmd, storeOffset + offsetof(struct EventInfo, postmod)); m_pDriver->GetCmdRenderState().BeginRenderPassAndApplyState(m_pDriver, cmd, VulkanRenderState::BindNone); } @@ -1312,8 +1323,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback return; size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(m_CallbackInfo.targetImage, m_CallbackInfo.targetImageFormat, VK_NULL_HANDLE, - VK_FORMAT_UNDEFINED, cmd, storeOffset); + CopyPixel(eid, cmd, storeOffset); } bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { @@ -1321,8 +1331,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback return false; size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(m_CallbackInfo.targetImage, m_CallbackInfo.targetImageFormat, VK_NULL_HANDLE, - VK_FORMAT_UNDEFINED, cmd, storeOffset + offsetof(struct EventInfo, postmod)); + CopyPixel(eid, cmd, storeOffset + offsetof(struct EventInfo, postmod)); m_EventIndices.insert(std::make_pair(eid, m_EventIndices.size())); return false; } @@ -1375,26 +1384,46 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback return (int32_t)it->second; } -private: - void CopyPixel(VkImage srcImage, VkFormat srcFormat, VkImage depthImage, VkFormat depthFormat, - VkCommandBuffer cmd, size_t offset) + VkFormat GetDepthFormat(uint32_t eventId) { - CopyPixelParams colourCopyParams = {}; - colourCopyParams.srcImage = srcImage; - colourCopyParams.srcImageFormat = srcFormat; - colourCopyParams.srcImageLayout = m_pDriver->GetDebugManager()->GetImageLayout( - GetResID(srcImage), VK_IMAGE_ASPECT_COLOR_BIT, m_CallbackInfo.targetSubresource.mip, - m_CallbackInfo.targetSubresource.slice); - CopyImagePixel(cmd, colourCopyParams, offset); + if(IsDepthOrStencilFormat(m_CallbackInfo.targetImageFormat)) + return m_CallbackInfo.targetImageFormat; + auto it = m_DepthFormats.find(eventId); + if(it == m_DepthFormats.end()) + return VK_FORMAT_UNDEFINED; + return it->second; + } - if(depthImage != VK_NULL_HANDLE) +private: + void CopyPixel(uint32_t eid, VkCommandBuffer cmd, size_t offset) + { + CopyPixelParams targetCopyParams = {}; + targetCopyParams.srcImage = m_CallbackInfo.targetImage; + targetCopyParams.srcImageFormat = m_CallbackInfo.targetImageFormat; + targetCopyParams.srcImageLayout = m_pDriver->GetDebugManager()->GetImageLayout( + GetResID(m_CallbackInfo.targetImage), VK_IMAGE_ASPECT_COLOR_BIT, + m_CallbackInfo.targetSubresource.mip, m_CallbackInfo.targetSubresource.slice); + if(IsDepthOrStencilFormat(m_CallbackInfo.targetImageFormat)) + offset += offsetof(struct PixelHistoryValue, depth); + CopyImagePixel(cmd, targetCopyParams, offset); + + // If the target image is a depth/stencil attachment, we already + // copied the value above. + if(IsDepthOrStencilFormat(m_CallbackInfo.targetImageFormat)) + return; + + const DrawcallDescription *draw = m_pDriver->GetDrawcall(eid); + if(draw && draw->depthOut != ResourceId()) { - CopyPixelParams depthCopyParams = colourCopyParams; - depthCopyParams.depthCopy = true; + ResourceId resId = m_pDriver->GetResourceManager()->GetLiveID(draw->depthOut); + VkImage depthImage = m_pDriver->GetResourceManager()->GetCurrentHandle(resId); + const VulkanCreationInfo::Image &imginfo = m_pDriver->GetDebugManager()->GetImageInfo(resId); + CopyPixelParams depthCopyParams = targetCopyParams; depthCopyParams.srcImage = depthImage; depthCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depthCopyParams.srcImageFormat = depthFormat; + depthCopyParams.srcImageFormat = imginfo.format; CopyImagePixel(cmd, depthCopyParams, offset + offsetof(struct PixelHistoryValue, depth)); + m_DepthFormats.insert(std::make_pair(eid, imginfo.format)); } } @@ -1438,7 +1467,7 @@ private: // and use either fixed or original fragment shader, and shaders that don't // have side effects. PipelineReplacements GetPipelineReplacements(uint32_t eid, ResourceId pipeline, VkRenderPass rp, - uint32_t framebufferIndex) + uint32_t outputIndex) { // The map does not keep track of the event ID, event ID is only used to figure out // which shaders need to be modified. Those flags are based on the shaders bound, @@ -1465,7 +1494,7 @@ private: { if(stages[i].stage == VK_SHADER_STAGE_FRAGMENT_BIT) { - stages[i].module = m_ShaderCache->GetFixedColShader(framebufferIndex); + stages[i].module = m_ShaderCache->GetFixedColShader(outputIndex); stages[i].pName = "main"; break; } @@ -1486,6 +1515,7 @@ private: // Key is event ID, and value is an index of where the event data is stored. std::map m_EventIndices; bool multipleSubpassWarningPrinted; + std::map m_DepthFormats; }; // TestsFailedCallback replays draws to figure out which tests failed (for ex., depth, @@ -1517,23 +1547,11 @@ struct TestsFailedCallback : public VulkanPixelHistoryCallback m_HasEarlyFragments[eid] = earlyFragmentTests; ResourceId curPipeline = pipestate.graphics.pipeline; - VulkanRenderState m_PrevState = m_pDriver->GetCmdRenderState(); + VulkanRenderState prevState = m_pDriver->GetCmdRenderState(); - uint32_t framebufferIndex = 0; - const rdcarray &atts = pipestate.GetFramebufferAttachments(); - for(uint32_t i = 0; i < atts.size(); i++) - { - ResourceId img = m_pDriver->GetDebugManager()->GetImageViewInfo(atts[i]).image; - if(img == GetResID(m_CallbackInfo.targetImage)) - { - framebufferIndex = i; - break; - } - } + ReplayDrawWithTests(cmd, eid, eventFlags, curPipeline, GetColorAttachmentIndex(prevState)); - ReplayDrawWithTests(cmd, eid, eventFlags, curPipeline, framebufferIndex); - - m_pDriver->GetCmdRenderState() = m_PrevState; + m_pDriver->GetCmdRenderState() = prevState; m_pDriver->GetCmdRenderState().BindPipeline(m_pDriver, cmd, VulkanRenderState::BindGraphics, false); } @@ -1733,7 +1751,7 @@ private: }; void ReplayDrawWithTests(VkCommandBuffer cmd, uint32_t eid, uint32_t eventFlags, - ResourceId basePipeline, uint32_t framebufferIndex) + ResourceId basePipeline, uint32_t outputIndex) { // Backface culling if(eventFlags & TestMustFail_Culling) @@ -1767,7 +1785,7 @@ private: uint32_t pipeFlags = PipelineCreationFlags_DisableDepthTest | PipelineCreationFlags_DisableDepthBoundsTest | PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_FixedColorShader; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); ReplayDraw(cmd, pipe, eid, TestEnabled_Culling); } @@ -1781,7 +1799,7 @@ private: PipelineCreationFlags_IntersectOriginalScissor | PipelineCreationFlags_DisableDepthTest | PipelineCreationFlags_DisableDepthBoundsTest | PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_FixedColorShader; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); // This will change the scissor for the later tests, but since those // tests happen later in the pipeline, it does not matter. for(uint32_t i = 0; i < pipestate.views.size(); i++) @@ -1798,7 +1816,7 @@ private: uint32_t pipeFlags = PipelineCreationFlags_DisableDepthBoundsTest | PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_DisableDepthTest | PipelineCreationFlags_FixedColorShader; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); ReplayDraw(cmd, pipe, eid, TestEnabled_SampleMask); } @@ -1808,7 +1826,7 @@ private: uint32_t pipeFlags = PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_DisableDepthTest | PipelineCreationFlags_FixedColorShader; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); ReplayDraw(cmd, pipe, eid, TestEnabled_DepthBounds); } @@ -1820,7 +1838,7 @@ private: { uint32_t pipeFlags = PipelineCreationFlags_DisableDepthTest | PipelineCreationFlags_FixedColorShader; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); ReplayDraw(cmd, pipe, eid, TestEnabled_StencilTesting); } @@ -1835,7 +1853,7 @@ private: uint32_t pipeFlags = PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_FixedColorShader; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); ReplayDraw(cmd, pipe, eid, TestEnabled_DepthTesting); } @@ -1848,7 +1866,7 @@ private: uint32_t pipeFlags = PipelineCreationFlags_DisableDepthBoundsTest | PipelineCreationFlags_DisableStencilTest | PipelineCreationFlags_DisableDepthTest; - VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, framebufferIndex); + VkPipeline pipe = CreatePipeline(basePipeline, pipeFlags, replacementShaders, outputIndex); ReplayDraw(cmd, pipe, eid, TestEnabled_FragmentDiscard); } } @@ -1857,8 +1875,7 @@ private: // pipeline flags. Modifies the base pipeline according to the flags, and // leaves the original pipeline behavior if a flag is not set. VkPipeline CreatePipeline(ResourceId basePipeline, uint32_t pipeCreateFlags, - const rdcarray &replacementShaders, - uint32_t framebufferIndex) + const rdcarray &replacementShaders, uint32_t outputIndex) { rdcpair pipeKey(basePipeline, pipeCreateFlags); auto it = m_PipeCache.find(pipeKey); @@ -1901,7 +1918,7 @@ private: if((ci.pStages[i].stage == VK_SHADER_STAGE_FRAGMENT_BIT) && (pipeCreateFlags & PipelineCreationFlags_FixedColorShader)) { - stages[i].module = m_ShaderCache->GetFixedColShader(framebufferIndex); + stages[i].module = m_ShaderCache->GetFixedColShader(outputIndex); stages[i].pName = "main"; } else if(replacementShaders[StageIndex(stages[i].stage)] != VK_NULL_HANDLE) @@ -1998,26 +2015,47 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback uint32_t numFragmentsInEvent = m_EventFragments[eid]; - VkRenderPass newRp = CreateRenderPass(state.renderPass, state.GetFramebuffer(), state.subpass, - m_CallbackInfo.targetImage, VK_FORMAT_R32G32B32A32_SFLOAT); - - VkFramebuffer newFb = - CreateFramebuffer(state.renderPass, newRp, state.subpass, state.GetFramebuffer(), - m_CallbackInfo.dsImageView, m_CallbackInfo.subImageView); - uint32_t framebufferIndex = 0; + uint32_t colorOutputIndex = 0; const rdcarray &atts = prevState.GetFramebufferAttachments(); - for(uint32_t i = 0; i < atts.size(); i++) + const VulkanCreationInfo::RenderPass &rpInfo = + m_pDriver->GetDebugManager()->GetRenderPassInfo(prevState.renderPass); + const VulkanCreationInfo::RenderPass::Subpass &sub = rpInfo.subpasses.front(); + + if(IsDepthOrStencilFormat(m_CallbackInfo.targetImageFormat)) { - ResourceId img = m_pDriver->GetDebugManager()->GetImageViewInfo(atts[i]).image; - if(img == GetResID(m_CallbackInfo.targetImage)) + // Going to add another color attachment. + framebufferIndex = (uint32_t)atts.size(); + colorOutputIndex = (uint32_t)sub.colorAttachments.size(); + } + else + { + for(uint32_t i = 0; i < atts.size(); i++) { - framebufferIndex = i; - break; + ResourceId img = m_pDriver->GetDebugManager()->GetImageViewInfo(atts[i]).image; + if(img == GetResID(m_CallbackInfo.targetImage)) + { + framebufferIndex = i; + break; + } + } + for(uint32_t i = 0; i < sub.colorAttachments.size(); i++) + { + if(framebufferIndex == sub.colorAttachments[i]) + { + colorOutputIndex = i; + break; + } } } - Pipelines pipes = CreatePerFragmentPipelines(curPipeline, newRp, eid, 0, framebufferIndex); + VkRenderPass newRp = + CreateRenderPass(state.renderPass, VK_FORMAT_R32G32B32A32_SFLOAT, framebufferIndex); + + VkFramebuffer newFb = CreateFramebuffer(state.renderPass, newRp, state.GetFramebuffer(), + m_CallbackInfo.subImageView, framebufferIndex); + + Pipelines pipes = CreatePerFragmentPipelines(curPipeline, newRp, eid, 0, colorOutputIndex); for(uint32_t i = 0; i < state.views.size(); i++) ScissorToPixel(state.views[i], state.scissors[i]); @@ -2031,12 +2069,19 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback CopyPixelParams colourCopyParams = {}; colourCopyParams.srcImage = m_CallbackInfo.subImage; - // Use the layout of the image we are substituting for. - VkImageLayout srcImageLayout = m_pDriver->GetDebugManager()->GetImageLayout( - GetResID(m_CallbackInfo.targetImage), VK_IMAGE_ASPECT_COLOR_BIT, - m_CallbackInfo.targetSubresource.mip, m_CallbackInfo.targetSubresource.slice); - colourCopyParams.srcImageLayout = srcImageLayout; - colourCopyParams.srcImageFormat = m_CallbackInfo.targetImageFormat; + colourCopyParams.srcImageFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + if(IsDepthOrStencilFormat(m_CallbackInfo.targetImageFormat)) + { + colourCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + else + { + // Use the layout of the image we are substituting for. + VkImageLayout srcImageLayout = m_pDriver->GetDebugManager()->GetImageLayout( + GetResID(m_CallbackInfo.targetImage), VK_IMAGE_ASPECT_COLOR_BIT, + m_CallbackInfo.targetSubresource.mip, m_CallbackInfo.targetSubresource.slice); + colourCopyParams.srcImageLayout = srcImageLayout; + } const VulkanCreationInfo::Pipeline &p = m_pDriver->GetDebugManager()->GetPipelineInfo(prevState.graphics.pipeline); @@ -2117,7 +2162,6 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback if(depthEnabled) { CopyPixelParams depthCopyParams = colourCopyParams; - depthCopyParams.depthCopy = true; depthCopyParams.srcImage = m_CallbackInfo.dsImage; depthCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; depthCopyParams.srcImageFormat = m_CallbackInfo.dsFormat; @@ -2163,10 +2207,11 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback if(f == 0) { // Before starting the draw, initialize the pixel to the premodification value - // for this event, for both color and stencil. + // for this event, for both color and depth. VkClearAttachment clearAtts[2] = {}; + clearAtts[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - clearAtts[0].colorAttachment = framebufferIndex; + clearAtts[0].colorAttachment = colorOutputIndex; memcpy(clearAtts[0].clearValue.color.float32, premod.col.floatValue, sizeof(clearAtts[0].clearValue.color)); @@ -2195,7 +2240,6 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback if(depthImage != VK_NULL_HANDLE) { CopyPixelParams depthCopyParams = colourCopyParams; - depthCopyParams.depthCopy = true; depthCopyParams.srcImage = m_CallbackInfo.dsImage; depthCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; depthCopyParams.srcImageFormat = depthFormat; @@ -2216,7 +2260,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback void PostRedraw(uint32_t eid, VkCommandBuffer cmd) {} // CreatePerFragmentPipelines for getting per fragment information. Pipelines CreatePerFragmentPipelines(ResourceId pipe, VkRenderPass rp, uint32_t eid, - uint32_t fragmentIndex, uint32_t framebufferIndex) + uint32_t fragmentIndex, uint32_t colorOutputIndex) { const VulkanCreationInfo::Pipeline &p = m_pDriver->GetDebugManager()->GetPipelineInfo(pipe); VkGraphicsPipelineCreateInfo pipeCreateInfo = {}; @@ -2270,29 +2314,49 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback pipeCreateInfo.pStages = stages.data(); pipeCreateInfo.renderPass = rp; + VkPipelineColorBlendStateCreateInfo *cbs = + (VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState; + // Turn off blending so that we can get shader output values. + VkPipelineColorBlendAttachmentState *atts = + (VkPipelineColorBlendAttachmentState *)cbs->pAttachments; + rdcarray newAtts; + + // Check if we need to add a new color attachment. + if(colorOutputIndex == cbs->attachmentCount) + { + newAtts.resize(cbs->attachmentCount + 1); + memcpy(newAtts.data(), cbs->pAttachments, + cbs->attachmentCount * sizeof(VkPipelineColorBlendAttachmentState)); + VkPipelineColorBlendAttachmentState newAtt = {}; + if(cbs->attachmentCount > 0) + { + // If there are existing color attachments, copy the blend information from it. + // It will be adjusted later. + newAtt = cbs->pAttachments[0]; + } + else + { + newAtt.blendEnable = VK_FALSE; + newAtt.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR; + } + newAtts[cbs->attachmentCount] = newAtt; + cbs->attachmentCount = (uint32_t)newAtts.size(); + cbs->pAttachments = newAtts.data(); + + atts = newAtts.data(); + } + Pipelines pipes = {}; VkResult vkr = m_pDriver->vkCreateGraphicsPipelines(m_pDriver->GetDev(), VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL, &pipes.postModPipe); RDCASSERTEQUAL(vkr, VK_SUCCESS); m_PipesToDestroy.push_back(pipes.postModPipe); - VkPipelineColorBlendStateCreateInfo *cbs = - (VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState; - // Turn off blending so that we can get shader output values. - VkPipelineColorBlendAttachmentState *atts = - (VkPipelineColorBlendAttachmentState *)cbs->pAttachments; for(uint32_t i = 0; i < cbs->attachmentCount; i++) { - if(i == framebufferIndex) - { - atts[i].blendEnable = 0; - atts[i].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - } - else - { - atts[i].colorWriteMask = 0; - } + atts[i].blendEnable = 0; + atts[i].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; } { @@ -2315,7 +2379,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback VkPipelineShaderStageCreateInfo stageCI = {}; stageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - stageCI.module = m_ShaderCache->GetPrimitiveIdShader(framebufferIndex); + stageCI.module = m_ShaderCache->GetPrimitiveIdShader(colorOutputIndex); stageCI.pName = "main"; bool fsFound = false; for(uint32_t i = 0; i < pipeCreateInfo.stageCount; i++) @@ -2668,6 +2732,8 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou } VkBufferCreateInfo bufferInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; + // TODO: the size for memory is calculated to fit pre and post modification values and + // stencil values. But we might run out of space when getting per fragment data. bufferInfo.size = AlignUp((uint32_t)(numEvents * sizeof(EventInfo)), 4096U); bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; @@ -2927,6 +2993,12 @@ void FillInColor(ResourceFormat fmt, const PixelHistoryValue &value, Modificatio memcpy(mod.col.floatValue, &v4.x, sizeof(v4)); } +float GetDepthValue(VkFormat depthFormat, const PixelHistoryValue &value) +{ + FloatVector v4 = ConvertComponents(MakeResourceFormat(depthFormat), (byte *)&value.depth); + return v4.x; +} + rdcarray VulkanReplay::PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast) @@ -3143,10 +3215,14 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even const EventInfo &ei = eventsInfo[eventIndex]; FillInColor(fmt, ei.premod, mod.preMod); FillInColor(fmt, ei.postmod, mod.postMod); - mod.preMod.depth = ei.premod.depth.fdepth; - mod.preMod.stencil = ei.premod.stencil; - mod.postMod.depth = ei.postmod.depth.fdepth; - mod.postMod.stencil = ei.postmod.stencil; + VkFormat depthFormat = cb.GetDepthFormat(mod.eventId); + if(depthFormat != VK_FORMAT_UNDEFINED) + { + mod.preMod.depth = GetDepthValue(depthFormat, ei.premod); + mod.preMod.stencil = ei.premod.stencil; + mod.postMod.depth = GetDepthValue(depthFormat, ei.postmod); + mod.postMod.stencil = ei.postmod.stencil; + } int32_t frags = int32_t(ei.dsWithoutShaderDiscard[4]); int32_t fragsClipped = int32_t(ei.dsWithShaderDiscard[4]); diff --git a/util/test/tests/Vulkan/VK_Pixel_History.py b/util/test/tests/Vulkan/VK_Pixel_History.py index 6aa644bc9..e201a38e4 100644 --- a/util/test/tests/Vulkan/VK_Pixel_History.py +++ b/util/test/tests/Vulkan/VK_Pixel_History.py @@ -14,6 +14,8 @@ def shader_out_col(x): return value_selector(x.shaderOut.col) def shader_out_depth(x): return x.shaderOut.depth def pre_mod_col(x): return value_selector(x.preMod.col) def post_mod_col(x): return value_selector(x.postMod.col) +def shader_out_depth(x): return x.shaderOut.depth +def pre_mod_depth(x): return x.preMod.depth def post_mod_depth(x): return x.postMod.depth def primitive_id(x): return x.primitiveID def unboundPS(x): return x.unboundPS @@ -32,6 +34,7 @@ class VK_Pixel_History(rdtest.TestCase): self.primary_test() self.multisampled_image_test() self.secondary_cmd_test() + self.depth_target_test() def primary_test(self): test_marker: rd.DrawcallDescription = self.find_draw("Test") @@ -245,6 +248,39 @@ class VK_Pixel_History(rdtest.TestCase): self.check_events(events, modifs, True) self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast) + def depth_target_test(self): + test_marker: rd.DrawcallDescription = self.find_draw("Test") + self.controller.SetFrameEvent(test_marker.next.eventId, True) + + pipe: rd.PipeState = self.controller.GetPipelineState() + + rt: rd.BoundResource = pipe.GetDepthTarget() + + tex = rt.resourceId + tex_details = self.get_texture(tex) + + sub = rd.Subresource() + if tex_details.arraysize > 1: + sub.slice = rt.firstSlice + if tex_details.mips > 1: + sub.mip = rt.firstMip + + begin_renderpass_eid = self.find_draw("Begin RenderPass").next.eventId + background_eid = self.find_draw("Background").next.eventId + test_eid = self.find_draw("Test").next.eventId + + x, y = 200, 190 + rdtest.log.print("Testing pixel {}, {}".format(x, y)) + modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + events = [ + [[event_id, begin_renderpass_eid], [passed, True], [post_mod_depth, 1.0]], + [[event_id, background_eid], [passed, True], [primitive_id, 0], [pre_mod_depth, 1.0], [post_mod_depth, 0.95]], + [[event_id, test_eid], [passed, True], [primitive_id, 0], [shader_out_depth, 0.5], [post_mod_depth, 0.5]], + [[event_id, test_eid], [passed, True], [primitive_id, 1], [shader_out_depth, 0.6], [post_mod_depth, 0.5]], + ] + self.check_events(events, modifs, False) + + def check_events(self, events, modifs, hasSecondary): self.check(len(modifs) == len(events), "Expected {} events, got {}".format(len(events), len(modifs))) # Check for consistency first. For secondary command buffers,