From 3b8f8989be11df8bda7535d2e9c521a0f984d703 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 12 Feb 2026 18:08:41 +0000 Subject: [PATCH] Support VK_EXT_custom_resolve extension --- renderdoc/driver/vulkan/vk_common.h | 1 + renderdoc/driver/vulkan/vk_core.cpp | 88 ++++++-- renderdoc/driver/vulkan/vk_core.h | 7 + renderdoc/driver/vulkan/vk_hookset_defs.h | 10 +- renderdoc/driver/vulkan/vk_info.cpp | 23 +++ renderdoc/driver/vulkan/vk_info.h | 16 ++ renderdoc/driver/vulkan/vk_overlay.cpp | 14 ++ renderdoc/driver/vulkan/vk_replay.cpp | 15 +- renderdoc/driver/vulkan/vk_shader_cache.cpp | 31 +++ renderdoc/driver/vulkan/vk_state.cpp | 25 +++ renderdoc/driver/vulkan/vk_state.h | 16 ++ renderdoc/driver/vulkan/vk_stringise.cpp | 3 +- .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 190 +++++++++++++++++- 13 files changed, 413 insertions(+), 26 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index ee57b11a9..a3be1f84e 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -1404,6 +1404,7 @@ enum class VulkanChunk : uint32_t vkCmdEndRendering2EXT, SetQueueAnnotation, SetCommandAnnotation, + vkCmdBeginCustomResolveEXT, Max, }; diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 37520b26d..d1a2a4fe3 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1119,6 +1119,12 @@ static const VkExtensionProperties supportedExtensions[] = { VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, VK_EXT_CUSTOM_BORDER_COLOR_SPEC_VERSION, }, +#ifdef VK_EXT_custom_resolve + { + VK_EXT_CUSTOM_RESOLVE_EXTENSION_NAME, + VK_EXT_CUSTOM_RESOLVE_SPEC_VERSION, + }, +#endif { VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_SPEC_VERSION, @@ -4921,6 +4927,8 @@ bool WrappedVulkan::ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk) return Serialise_vkCmdPushDescriptorSet2(ser, VK_NULL_HANDLE, NULL); case VulkanChunk::vkCmdPushDescriptorSetWithTemplate2: return Serialise_vkCmdPushDescriptorSetWithTemplate2(ser, VK_NULL_HANDLE, NULL); + case VulkanChunk::vkCmdBeginCustomResolveEXT: + return Serialise_vkCmdBeginCustomResolveEXT(ser, VK_NULL_HANDLE, NULL); case VulkanChunk::SetQueueAnnotation: return Serialise_SetQueueAnnotation(ser, VK_NULL_HANDLE, rdcstr(), eRENDERDOC_AnnotationMax, @@ -6561,9 +6569,10 @@ void WrappedVulkan::AddFramebufferUsage(VulkanActionTreeNode &actionNode, uint32_t att = sub.colorAttachments[i]; if(att == VK_ATTACHMENT_UNUSED) continue; - actionNode.resourceUsage.push_back( - make_rdcpair(c.m_ImageView[fbattachments[att]].image, - EventUsage(e, ResourceUsage::ColorTarget, fbattachments[att]))); + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[fbattachments[att]].image, + EventUsage(e, sub.customResolve ? ResourceUsage::ResolveDst : ResourceUsage::ColorTarget, + fbattachments[att]))); } if(sub.depthstencilAttachment >= 0) @@ -6584,23 +6593,66 @@ void WrappedVulkan::AddFramebufferUsage(VulkanActionTreeNode &actionNode, if(dyn.color[i].imageView == VK_NULL_HANDLE) continue; - actionNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.color[i].imageView)].image, - EventUsage(e, ResourceUsage::ColorTarget, GetResID(dyn.color[i].imageView)))); + bool isCustomResolve = renderState.dynamicRendering.beginCustomResolve && + (dyn.color[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT); + if(!isCustomResolve) + { + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.color[i].imageView)].image, + EventUsage(e, ResourceUsage::ColorTarget, GetResID(dyn.color[i].imageView)))); + } + else + { + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.color[i].imageView)].image, + EventUsage(e, ResourceUsage::InputTarget, GetResID(dyn.color[i].imageView)))); + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.color[i].resolveImageView)].image, + EventUsage(e, ResourceUsage::ResolveDst, GetResID(dyn.color[i].resolveImageView)))); + } } if(dyn.depth.imageView != VK_NULL_HANDLE) { - actionNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.depth.imageView)].image, - EventUsage(e, ResourceUsage::DepthStencilTarget, GetResID(dyn.depth.imageView)))); + bool isCustomResolve = renderState.dynamicRendering.beginCustomResolve && + (dyn.depth.resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT); + if(!isCustomResolve) + { + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.depth.imageView)].image, + EventUsage(e, ResourceUsage::DepthStencilTarget, GetResID(dyn.depth.imageView)))); + } + else + { + actionNode.resourceUsage.push_back( + make_rdcpair(c.m_ImageView[GetResID(dyn.depth.imageView)].image, + EventUsage(e, ResourceUsage::InputTarget, GetResID(dyn.depth.imageView)))); + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.depth.resolveImageView)].image, + EventUsage(e, ResourceUsage::ResolveDst, GetResID(dyn.depth.resolveImageView)))); + } } if(dyn.stencil.imageView != VK_NULL_HANDLE && dyn.depth.imageView != dyn.stencil.imageView) { - actionNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.stencil.imageView)].image, - EventUsage(e, ResourceUsage::DepthStencilTarget, GetResID(dyn.stencil.imageView)))); + bool isCustomResolve = renderState.dynamicRendering.beginCustomResolve && + (dyn.stencil.resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT); + if(!isCustomResolve) + + { + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.stencil.imageView)].image, + EventUsage(e, ResourceUsage::DepthStencilTarget, GetResID(dyn.stencil.imageView)))); + } + else + { + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.stencil.imageView)].image, + EventUsage(e, ResourceUsage::InputTarget, GetResID(dyn.stencil.imageView)))); + actionNode.resourceUsage.push_back(make_rdcpair( + c.m_ImageView[GetResID(dyn.stencil.resolveImageView)].image, + EventUsage(e, ResourceUsage::ResolveDst, GetResID(dyn.stencil.resolveImageView)))); + } } } } @@ -6753,10 +6805,10 @@ bool WrappedVulkan::EraseImageState(ResourceId id) void WrappedVulkan::UpdateImageStates(const rdcflatmap &dstStates) { - // this function expects the number of updates to be orders of magnitude fewer than the number of - // existing images. If there are a small number of images in total then it doesn't matter much, - // and if there are a large number of images then it's better to do repeated map lookups rather - // than spend time iterating linearly across the map for a sparse set of updates. + // this function expects the number of updates to be orders of magnitude fewer than the number + // of existing images. If there are a small number of images in total then it doesn't matter + // much, and if there are a large number of images then it's better to do repeated map lookups + // rather than spend time iterating linearly across the map for a sparse set of updates. SCOPED_LOCK(m_ImageStatesLock); auto dstIt = dstStates.begin(); ImageTransitionInfo info = GetImageTransitionInfo(); @@ -6800,8 +6852,8 @@ void WrappedVulkan::ReplayDraw(VkCommandBuffer cmd, const ActionDescription &act } else { - // otherwise it's a bit more complex, we need to set up a multidraw with the first N draws nop'd - // out and the parameters added into the last one + // otherwise it's a bit more complex, we need to set up a multidraw with the first N draws + // nop'd out and the parameters added into the last one VkMarkerRegion::Begin(StringFormat::Fmt("ReplayDraw(drawIndex=%u)", action.drawIndex), cmd); diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 45011a3a3..166854497 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -854,6 +854,9 @@ private: // vkCmdNextSubpass for valid barrier counting. int activeSubpass = 0; + // Is custom resolve active : when it is active the resolve target of the colour attachment is the output + bool customResolve; + ResourceId GetPushDescriptorID(VkPipelineBindPoint bindpoint, uint32_t set) { return pushDescriptorID[bindpoint == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR ? 2 : bindpoint][set]; @@ -3359,4 +3362,8 @@ public: // VK_EXT_image_drm_format_modifier VkResult vkGetImageDrmFormatModifierPropertiesEXT(VkDevice device, VkImage image, VkImageDrmFormatModifierPropertiesEXT *pProperties); + + // VK_EXT_custom_resolve + IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdBeginCustomResolveEXT, VkCommandBuffer commandBuffer, + const VkBeginCustomResolveInfoEXT *pBeginCustomResolveInfo); }; diff --git a/renderdoc/driver/vulkan/vk_hookset_defs.h b/renderdoc/driver/vulkan/vk_hookset_defs.h index 3fff4f9c7..54315e613 100644 --- a/renderdoc/driver/vulkan/vk_hookset_defs.h +++ b/renderdoc/driver/vulkan/vk_hookset_defs.h @@ -559,7 +559,8 @@ DeclExt(KHR_map_memory2); \ DeclExt(KHR_present_wait2); \ DeclExt(EXT_fragment_density_map_offset); \ - DeclExt(EXT_image_drm_format_modifier); + DeclExt(EXT_image_drm_format_modifier); \ + DeclExt(EXT_custom_resolve); // for simplicity and since the check itself is platform agnostic, // these aren't protected in platform defines @@ -705,7 +706,8 @@ CheckExt(KHR_map_memory2, VK14); \ CheckExt(KHR_present_wait2, VKXX); \ CheckExt(EXT_fragment_density_map_offset, VKXX); \ - CheckExt(EXT_image_drm_format_modifier, VKXX); + CheckExt(EXT_image_drm_format_modifier, VKXX); \ + CheckExt(EXT_custom_resolve, VKXX); #define HookInitVulkanInstanceExts_PhysDev() \ HookInitExtension(KHR_surface, GetPhysicalDeviceSurfaceSupportKHR); \ @@ -983,6 +985,7 @@ HookInitPromotedExtension(KHR_dynamic_rendering, CmdBeginRendering, KHR); \ HookInitPromotedExtension(KHR_dynamic_rendering, CmdEndRendering, KHR); \ HookInitExtension(EXT_fragment_density_map_offset, CmdEndRendering2EXT); \ + HookInitExtension(EXT_custom_resolve, CmdBeginCustomResolveEXT); \ HookInitPromotedExtension(KHR_dynamic_rendering_local_read, CmdSetRenderingAttachmentLocations, \ KHR); \ HookInitPromotedExtension(KHR_dynamic_rendering_local_read, \ @@ -1110,6 +1113,7 @@ HookInitPromotedExtension(KHR_map_memory2, UnmapMemory2, KHR); \ HookInitExtension(KHR_present_wait2, WaitForPresent2KHR); \ HookInitExtension(EXT_image_drm_format_modifier, GetImageDrmFormatModifierPropertiesEXT); \ + HookInitExtension(EXT_custom_resolve, CmdBeginCustomResolveEXT); \ HookInitExtension_Device_Win32(); \ HookInitExtension_Device_Linux(); \ HookInitExtension_Device_Android(); \ @@ -2082,6 +2086,8 @@ pBindDescriptorBufferEmbeddedSamplersInfo); \ HookDefine3(VkResult, vkGetImageDrmFormatModifierPropertiesEXT, VkDevice, device, VkImage, \ image, VkImageDrmFormatModifierPropertiesEXT *, pProperties); \ + HookDefine2(void, vkCmdBeginCustomResolveEXT, VkCommandBuffer, commandBuffer, \ + const VkBeginCustomResolveInfoEXT *, pBeginCustomResolveInfo); \ HookDefine_Win32(); \ HookDefine_Linux(); \ HookDefine_Android(); \ diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 8d42e649b..9922bb3fc 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -1167,6 +1167,15 @@ void VulkanCreationInfo::ShaderObject::Init(VulkanResourceManager *resourceMan, } } + const VkCustomResolveCreateInfoEXT *customResInfo = + (const VkCustomResolveCreateInfoEXT *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); + if(customResInfo) + { + hasCustomResCreateInfo = true; + customResolve = (customResInfo->customResolve == VK_TRUE); + } + ShaderModuleReflection &reflData = info.m_ShaderModule[id].m_Reflections[key]; reflData.Init(resourceMan, info, id, info.m_ShaderModule[id].spirv, shad.entryPoint, @@ -1230,6 +1239,18 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, depthFormat = VK_FORMAT_UNDEFINED; stencilFormat = VK_FORMAT_UNDEFINED; } + const VkCustomResolveCreateInfoEXT *customResInfo = + (const VkCustomResolveCreateInfoEXT *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); + if(customResInfo) + { + hasCustomResCreateInfo = true; + customResCreateInfo.customResolve = (customResInfo->customResolve == VK_TRUE); + customResCreateInfo.colorFormats.assign(customResInfo->pColorAttachmentFormats, + customResInfo->colorAttachmentCount); + customResCreateInfo.depthFormat = customResInfo->depthAttachmentFormat; + customResCreateInfo.stencilFormat = customResInfo->stencilAttachmentFormat; + } dynamicRenderingLocalRead.Init((const VkBaseInStructure *)pCreateInfo); @@ -2249,6 +2270,7 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, dst.multiviews.push_back(i); } } + dst.customResolve = (src.flags & VK_SUBPASS_DESCRIPTION_CUSTOM_RESOLVE_BIT_EXT) != 0; } for(uint32_t i = 0; i < pCreateInfo->dependencyCount; i++) @@ -2415,6 +2437,7 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, if(src.viewMask & (1 << i)) dst.multiviews.push_back(i); } + dst.customResolve = (src.flags & VK_SUBPASS_DESCRIPTION_CUSTOM_RESOLVE_BIT_EXT) != 0; } } diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 00669cec6..4ed6373e7 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -312,6 +312,16 @@ struct VulkanCreationInfo VkFormat depthFormat; VkFormat stencilFormat; + // VkCustomResolveCreateInfoEXT + bool hasCustomResCreateInfo = false; + struct CustomResInfo + { + bool customResolve; + rdcarray colorFormats; + VkFormat depthFormat; + VkFormat stencilFormat; + } customResCreateInfo; + // VkRenderingAttachmentLocationInfo and VkRenderingInputAttachmentIndexInfo DynamicRenderingLocalRead dynamicRenderingLocalRead; @@ -491,6 +501,11 @@ struct VulkanCreationInfo // 64-bit aligned and with an offset equal to their ID. In other words this is big enough for the max ID uint32_t virtualSpecialisationByteSize = 0; + // VkCustomResolveCreateInfoEXT + bool hasCustomResCreateInfo = false; + // For Shader Objects only "customResolve" is used + bool customResolve; + rdcarray staticDescriptorAccess; }; std::unordered_map m_ShaderObject; @@ -558,6 +573,7 @@ struct VulkanCreationInfo bool feedbackLoop; bool tileOnlyMSAAEnable; + bool customResolve; }; rdcarray subpasses; diff --git a/renderdoc/driver/vulkan/vk_overlay.cpp b/renderdoc/driver/vulkan/vk_overlay.cpp index 9c7d0e491..0c15ca108 100644 --- a/renderdoc/driver/vulkan/vk_overlay.cpp +++ b/renderdoc/driver/vulkan/vk_overlay.cpp @@ -1331,6 +1331,8 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D // don't use dynamic rendering RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO); + // don't use custom resolve + RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); if(!state.graphics.shaderObject) { @@ -1377,6 +1379,7 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D state.SetRenderPass(GetResID(m_Overlay.NoDepthRP)); state.subpass = 0; state.SetFramebuffer(m_pDriver, GetResID(m_Overlay.NoDepthFB)); + state.dynamicRendering.beginCustomResolve = false; state.subpassContents = VK_SUBPASS_CONTENTS_INLINE; state.dynamicRendering.flags &= ~VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT; @@ -1573,6 +1576,8 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D // don't use dynamic rendering RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO); + // don't use custom resolve + RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); VkPipelineShaderStageCreateInfo *fragShader = NULL; for(uint32_t i = 0; i < pipeCreateInfo.stageCount; i++) @@ -1640,6 +1645,7 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D state.SetRenderPass(GetResID(m_Overlay.NoDepthRP)); state.subpass = 0; state.SetFramebuffer(m_pDriver, GetResID(m_Overlay.NoDepthFB)); + state.dynamicRendering.beginCustomResolve = false; state.graphics.pipeline = GetResID(pipe[0]); state.scissors = prevstate.scissors; @@ -1929,6 +1935,8 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D // don't use dynamic rendering RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO); + // don't use custom resolve + RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); VkPipelineShaderStageCreateInfo *fragShader = NULL; @@ -1998,6 +2006,7 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D state.SetRenderPass(GetResID(m_Overlay.NoDepthRP)); state.subpass = 0; state.SetFramebuffer(m_pDriver, GetResID(m_Overlay.NoDepthFB)); + state.dynamicRendering.beginCustomResolve = false; state.graphics.pipeline = GetResID(pipe[0]); @@ -2580,6 +2589,8 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D // don't use dynamic rendering RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO); + // don't use custom resolve + RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL, &passpipe); @@ -2642,6 +2653,7 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D state.SetRenderPass(GetResID(m_Overlay.NoDepthRP)); state.subpass = 0; state.SetFramebuffer(m_pDriver, GetResID(m_Overlay.NoDepthFB)); + state.dynamicRendering.beginCustomResolve = false; state.graphics.pipeline = GetResID(failpipe); @@ -3621,6 +3633,8 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D // don't use dynamic rendering RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO); + // don't use custom resolve + RemoveNextStruct(&pipeCreateInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); if(pipeCreateInfo.pDynamicState) { diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 6f099d8b8..904653765 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -1798,6 +1798,9 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) fbState.attachments.push_back({}); ResourceId viewid = GetResID(dyn.color[i].imageView); + if(state.dynamicRendering.beginCustomResolve && + (dyn.color[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) + viewid = GetResID(dyn.color[i].resolveImageView); if(viewid != ResourceId()) { @@ -1825,7 +1828,9 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) rpState.colorAttachments.push_back(uint32_t(attIdx++)); - if(dyn.color[i].resolveMode && dyn.color[i].resolveImageView != VK_NULL_HANDLE) + if((dyn.color[i].resolveMode != VK_RESOLVE_MODE_NONE) && + !(dyn.color[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT) && + (dyn.color[i].resolveImageView != VK_NULL_HANDLE)) { fbState.attachments.push_back({}); @@ -1851,8 +1856,16 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) fbState.attachments.push_back({}); ResourceId viewid = GetResID(dyn.depth.imageView); + if(state.dynamicRendering.beginCustomResolve && + (dyn.depth.resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) + viewid = GetResID(dyn.depth.resolveImageView); if(dyn.depth.imageView == VK_NULL_HANDLE) + { viewid = GetResID(dyn.stencil.imageView); + if(state.dynamicRendering.beginCustomResolve && + (dyn.stencil.resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) + viewid = GetResID(dyn.stencil.resolveImageView); + } fbState.attachments.back().view = viewid; ret.currentPass.framebuffer.attachments[attIdx].resource = c.m_ImageView[viewid].image; diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index 55b0badcf..ecd73b594 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -1056,6 +1056,22 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p ret.pNext = &dynRenderCreate; } + static VkFormat customResColFormats[16] = {}; + static VkCustomResolveCreateInfoEXT customResCreate = { + VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT, NULL, VK_FALSE, 0, customResColFormats}; + + if(pipeInfo.renderpass == ResourceId() && (pipeInfo.hasCustomResCreateInfo)) + { + customResCreate.customResolve = pipeInfo.customResCreateInfo.customResolve; + customResCreate.colorAttachmentCount = (uint32_t)pipeInfo.customResCreateInfo.colorFormats.size(); + memcpy(customResColFormats, pipeInfo.customResCreateInfo.colorFormats.data(), + pipeInfo.customResCreateInfo.colorFormats.byteSize()); + customResCreate.depthAttachmentFormat = pipeInfo.customResCreateInfo.depthFormat; + customResCreate.stencilAttachmentFormat = pipeInfo.customResCreateInfo.stencilFormat; + customResCreate.pNext = ret.pNext; + ret.pNext = &customResCreate; + } + static VkPipelineDiscardRectangleStateCreateInfoEXT discardRects = { VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT, }; @@ -1343,5 +1359,20 @@ void VulkanShaderCache::MakeShaderObjectInfo(VkShaderCreateInfoEXT &shadCreateIn specInfo.pData = specdata.data(); } + static VkCustomResolveCreateInfoEXT customResCreate = { + VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT, + NULL, + VK_FALSE, + 0, + NULL, + VK_FORMAT_UNDEFINED, + VK_FORMAT_UNDEFINED, + }; + if(shadInfo.hasCustomResCreateInfo) + { + customResCreate.customResolve = shadInfo.customResolve; + customResCreate.pNext = ret.pNext; + ret.pNext = &customResCreate; + } shadCreateInfo = ret; } diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index 29dc14214..88e50aa29 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -42,6 +42,7 @@ struct RenderingInfoStructs VkRenderingFragmentDensityMapAttachmentInfoEXT fragmentDensity; VkRenderingFragmentShadingRateAttachmentInfoKHR shadingRate; VkMultisampledRenderToSingleSampledInfoEXT tileOnlyMSAA; + VkCustomResolveCreateInfoEXT customResolveCreateInfo; }; void setupRenderingInfo(const VulkanRenderState::DynamicRendering &dynamicRendering, @@ -133,6 +134,22 @@ void setupRenderingInfo(const VulkanRenderState::DynamicRendering &dynamicRender structs->tileOnlyMSAA.pNext = info->pNext; info->pNext = &structs->tileOnlyMSAA; } + + structs->customResolveCreateInfo = { + VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT, + NULL, + dynamicRendering.customResolveCreateInfo.customResolve, + (uint32_t)dynamicRendering.customResolveCreateInfo.colorAttachmentFormats.size(), + dynamicRendering.customResolveCreateInfo.colorAttachmentFormats.data(), + dynamicRendering.customResolveCreateInfo.depthAttachmentFormat, + dynamicRendering.customResolveCreateInfo.stencilAttachmentFormat, + }; + + if(dynamicRendering.hasCustomResolveCreateInfo) + { + structs->customResolveCreateInfo.pNext = info->pNext; + info->pNext = &structs->customResolveCreateInfo; + } } } // namespace @@ -267,6 +284,14 @@ void VulkanRenderState::BeginRenderPassAndApplyState(WrappedVulkan *vk, VkComman { dynamicRendering.localRead.SetInputIndices(cmd); } + + if(dynamicRendering.beginCustomResolve) + { + VkBeginCustomResolveInfoEXT beginInfo; + beginInfo.sType = VK_STRUCTURE_TYPE_BEGIN_CUSTOM_RESOLVE_INFO_EXT; + beginInfo.pNext = VK_NULL_HANDLE; + ObjDisp(cmd)->CmdBeginCustomResolveEXT(Unwrap(cmd), &beginInfo); + } } void VulkanRenderState::EndRenderPass(VkCommandBuffer cmd) diff --git a/renderdoc/driver/vulkan/vk_state.h b/renderdoc/driver/vulkan/vk_state.h index c770ab4a2..feca23095 100644 --- a/renderdoc/driver/vulkan/vk_state.h +++ b/renderdoc/driver/vulkan/vk_state.h @@ -353,6 +353,10 @@ struct VulkanRenderState localRead = o.localRead; + beginCustomResolve = o.beginCustomResolve; + hasCustomResolveCreateInfo = o.hasCustomResolveCreateInfo; + customResolveCreateInfo = o.customResolveCreateInfo; + // this will deep copy from the incoming object CopyAttachmentNexts(); @@ -385,6 +389,18 @@ struct VulkanRenderState // VK_KHR_dynamic_rendering_local_read DynamicRenderingLocalRead localRead; + // VK_EXT_custom_resolve + bool beginCustomResolve = false; + + bool hasCustomResolveCreateInfo = false; + struct CustomResolveCreateInfoEXT + { + bool customResolve = false; + rdcarray colorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; + } customResolveCreateInfo; + private: // VK_KHR_unified_image_layouts rdcarray feedbacks; diff --git a/renderdoc/driver/vulkan/vk_stringise.cpp b/renderdoc/driver/vulkan/vk_stringise.cpp index 5e8da8987..8980c8173 100644 --- a/renderdoc/driver/vulkan/vk_stringise.cpp +++ b/renderdoc/driver/vulkan/vk_stringise.cpp @@ -28,7 +28,7 @@ template <> rdcstr DoStringise(const VulkanChunk &el) { - RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1236, "Chunks changed without updating names"); + RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1237, "Chunks changed without updating names"); BEGIN_ENUM_STRINGISE(VulkanChunk) { @@ -266,6 +266,7 @@ rdcstr DoStringise(const VulkanChunk &el) STRINGISE_ENUM_CLASS(vkCmdSetDescriptorBufferOffsets2EXT) STRINGISE_ENUM_CLASS(vkCmdPushDescriptorSet2) STRINGISE_ENUM_CLASS(vkCmdPushDescriptorSetWithTemplate2) + STRINGISE_ENUM_CLASS(vkCmdBeginCustomResolveEXT) STRINGISE_ENUM_CLASS_NAMED(SetCommandAnnotation, "Internal::SetCommandAnnotation"); STRINGISE_ENUM_CLASS_NAMED(SetQueueAnnotation, "Internal::SetQueueAnnotation"); STRINGISE_ENUM_CLASS_NAMED(Max, "Max Chunk"); diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 4abebd448..5cf874374 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -7685,6 +7685,36 @@ bool WrappedVulkan::Serialise_vkCmdBeginRendering(SerialiserType &ser, VkCommand renderstate.dynamicRendering.tileOnlyMSAASampleCount = tileOnlyMSAA->rasterizationSamples; } + renderstate.dynamicRendering.beginCustomResolve = false; + const VkCustomResolveCreateInfoEXT *customResolveCreateInfo = + (const VkCustomResolveCreateInfoEXT *)FindNextStruct( + &RenderingInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); + if(customResolveCreateInfo) + { + renderstate.dynamicRendering.hasCustomResolveCreateInfo = true; + renderstate.dynamicRendering.customResolveCreateInfo.customResolve = + (customResolveCreateInfo->customResolve == VK_TRUE); + for(uint32_t i = 0; i < customResolveCreateInfo->colorAttachmentCount; ++i) + { + renderstate.dynamicRendering.customResolveCreateInfo.colorAttachmentFormats.push_back( + customResolveCreateInfo->pColorAttachmentFormats[i]); + } + renderstate.dynamicRendering.customResolveCreateInfo.depthAttachmentFormat = + customResolveCreateInfo->depthAttachmentFormat; + renderstate.dynamicRendering.customResolveCreateInfo.stencilAttachmentFormat = + customResolveCreateInfo->stencilAttachmentFormat; + } + else + { + renderstate.dynamicRendering.hasCustomResolveCreateInfo = false; + renderstate.dynamicRendering.customResolveCreateInfo.customResolve = false; + renderstate.dynamicRendering.customResolveCreateInfo.colorAttachmentFormats.clear(); + renderstate.dynamicRendering.customResolveCreateInfo.depthAttachmentFormat = + VK_FORMAT_UNDEFINED; + renderstate.dynamicRendering.customResolveCreateInfo.stencilAttachmentFormat = + VK_FORMAT_UNDEFINED; + } + rdcarray attachments; for(size_t i = 0; i < renderstate.dynamicRendering.color.size(); i++) @@ -7875,6 +7905,36 @@ bool WrappedVulkan::Serialise_vkCmdBeginRendering(SerialiserType &ser, VkCommand renderstate.dynamicRendering.tileOnlyMSAASampleCount = tileOnlyMSAA->rasterizationSamples; } + renderstate.dynamicRendering.beginCustomResolve = false; + const VkCustomResolveCreateInfoEXT *customResolveCreateInfo = + (const VkCustomResolveCreateInfoEXT *)FindNextStruct( + &RenderingInfo, VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT); + if(customResolveCreateInfo) + { + renderstate.dynamicRendering.hasCustomResolveCreateInfo = true; + renderstate.dynamicRendering.customResolveCreateInfo.customResolve = + (customResolveCreateInfo->customResolve == VK_TRUE); + for(uint32_t i = 0; i < customResolveCreateInfo->colorAttachmentCount; ++i) + { + renderstate.dynamicRendering.customResolveCreateInfo.colorAttachmentFormats.push_back( + customResolveCreateInfo->pColorAttachmentFormats[i]); + } + renderstate.dynamicRendering.customResolveCreateInfo.depthAttachmentFormat = + customResolveCreateInfo->depthAttachmentFormat; + renderstate.dynamicRendering.customResolveCreateInfo.stencilAttachmentFormat = + customResolveCreateInfo->stencilAttachmentFormat; + } + else + { + renderstate.dynamicRendering.hasCustomResolveCreateInfo = false; + renderstate.dynamicRendering.customResolveCreateInfo.customResolve = false; + renderstate.dynamicRendering.customResolveCreateInfo.colorAttachmentFormats.clear(); + renderstate.dynamicRendering.customResolveCreateInfo.depthAttachmentFormat = + VK_FORMAT_UNDEFINED; + renderstate.dynamicRendering.customResolveCreateInfo.stencilAttachmentFormat = + VK_FORMAT_UNDEFINED; + } + rdcarray attachments; for(size_t i = 0; i < renderstate.dynamicRendering.color.size(); i++) @@ -8228,8 +8288,8 @@ bool WrappedVulkan::Serialise_vkCmdEndRendering(SerialiserType &ser, VkCommandBu for(size_t i = 0; i < dynAtts.size(); i++) { - if(dynAtts[i].resolveMode && dynAtts[i].imageView != VK_NULL_HANDLE && - dynAtts[i].resolveImageView != VK_NULL_HANDLE) + if((dynAtts[i].resolveMode && !(dynAtts[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) && + dynAtts[i].imageView != VK_NULL_HANDLE && dynAtts[i].resolveImageView != VK_NULL_HANDLE) { usage.push_back(make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, EventUsage(eid, ResourceUsage::ResolveSrc))); @@ -8516,8 +8576,8 @@ bool WrappedVulkan::Serialise_vkCmdEndRendering2EXT(SerialiserType &ser, for(size_t i = 0; i < dynAtts.size(); i++) { - if(dynAtts[i].resolveMode && dynAtts[i].imageView != VK_NULL_HANDLE && - dynAtts[i].resolveImageView != VK_NULL_HANDLE) + if((dynAtts[i].resolveMode && !(dynAtts[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) && + dynAtts[i].imageView != VK_NULL_HANDLE && dynAtts[i].resolveImageView != VK_NULL_HANDLE) { usage.push_back(make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, EventUsage(eid, ResourceUsage::ResolveSrc))); @@ -10326,6 +10386,125 @@ void WrappedVulkan::vkCmdPushDescriptorSetWithTemplate2( } } +template +bool WrappedVulkan::Serialise_vkCmdBeginCustomResolveEXT( + SerialiserType &ser, VkCommandBuffer commandBuffer, + const VkBeginCustomResolveInfoEXT *pBeginCustomResolveInfo) +{ + SERIALISE_ELEMENT(commandBuffer); + SERIALISE_ELEMENT_OPT(pBeginCustomResolveInfo).Important(); + + if(IsReplayingAndReading()) + { + m_LastCmdBufferID = GetResID(commandBuffer); + + if(IsActiveReplaying(m_State)) + { + if(InRerecordRange(m_LastCmdBufferID)) + { + commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); + ObjDisp(commandBuffer)->CmdBeginCustomResolveEXT(Unwrap(commandBuffer), pBeginCustomResolveInfo); + + VulkanRenderState &renderstate = GetCmdRenderState(); + renderstate.dynamicRendering.beginCustomResolve = true; + + // The resolve images are now the framebuffer not the color images + rdcarray attachments; + for(size_t i = 0; i < renderstate.dynamicRendering.color.size(); i++) + { + VkRenderingAttachmentInfo &attInfo = renderstate.dynamicRendering.color[i]; + ResourceId resolveImageView = GetResID(attInfo.resolveImageView); + + attachments.push_back(resolveImageView); + + // The resolve images data is undefined and has an implicit store + renderstate.dynamicRendering.color[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + renderstate.dynamicRendering.color[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + const VulkanCreationInfo::ImageView &viewInfo = + m_CreationInfo.m_ImageView[resolveImageView]; + ResourceId imageId = viewInfo.image; + m_BakedCmdBufferInfo[m_LastCmdBufferID].resourceUsage.push_back( + make_rdcpair(imageId, EventUsage(m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID, + ResourceUsage::Discard, resolveImageView))); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + VkImage image = GetResourceManager()->GetHandle(imageId); + GetDebugManager()->FillWithDiscardPattern(commandBuffer, DiscardType::RenderPassLoad, + image, attInfo.resolveImageLayout, + viewInfo.range, renderstate.renderArea); + } + } + + attachments.push_back(GetResID(renderstate.dynamicRendering.depth.resolveImageView)); + attachments.push_back(GetResID(renderstate.dynamicRendering.stencil.resolveImageView)); + + renderstate.SetFramebuffer(ResourceId(), attachments); + } + } + else + { + ObjDisp(commandBuffer)->CmdBeginCustomResolveEXT(Unwrap(commandBuffer), pBeginCustomResolveInfo); + + VulkanRenderState &renderstate = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; + renderstate.dynamicRendering.beginCustomResolve = true; + + // The resolve images are now the framebuffer not the color images + rdcarray attachments; + + for(size_t i = 0; i < renderstate.dynamicRendering.color.size(); i++) + { + ResourceId resolveImageView = + GetResID(renderstate.dynamicRendering.color[i].resolveImageView); + attachments.push_back(resolveImageView); + + // The resolve images data is undefined and has an implicit store + renderstate.dynamicRendering.color[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + renderstate.dynamicRendering.color[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + ResourceId image = m_CreationInfo.m_ImageView[resolveImageView].image; + m_BakedCmdBufferInfo[m_LastCmdBufferID].resourceUsage.push_back( + make_rdcpair(image, EventUsage(m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID, + ResourceUsage::Discard, resolveImageView))); + } + + attachments.push_back(GetResID(renderstate.dynamicRendering.depth.resolveImageView)); + attachments.push_back(GetResID(renderstate.dynamicRendering.stencil.resolveImageView)); + + renderstate.SetFramebuffer(ResourceId(), attachments); + + AddEvent(); + ActionDescription action; + action.customName = "vkCmdBeginCustomResolveEXT()"; + action.flags |= ActionFlags::PassBoundary | ActionFlags::BeginPass | ActionFlags::EndPass; + AddAction(action); + } + } + return true; +} + +void WrappedVulkan::vkCmdBeginCustomResolveEXT( + VkCommandBuffer commandBuffer, const VkBeginCustomResolveInfoEXT *pBeginCustomResolveInfo) +{ + SCOPED_DBG_SINK(); + + SERIALISE_TIME_CALL( + ObjDisp(commandBuffer)->CmdBeginCustomResolveEXT(Unwrap(commandBuffer), pBeginCustomResolveInfo)); + + if(IsCaptureMode(m_State)) + { + VkResourceRecord *record = GetRecord(commandBuffer); + + CACHE_THREAD_SERIALISER(); + ser.SetActionChunk(); + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBeginCustomResolveEXT); + Serialise_vkCmdBeginCustomResolveEXT(ser, commandBuffer, pBeginCustomResolveInfo); + + record->AddChunk(scope.Get(&record->cmdInfo->alloc)); + } +} + INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateCommandPool, VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *, VkCommandPool *pCommandPool); @@ -10553,3 +10732,6 @@ INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdPushDescriptorSet2, VkCommandBuffer c INSTANTIATE_FUNCTION_SERIALISED( void, vkCmdPushDescriptorSetWithTemplate2, VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo *pPushDescriptorSetWithTemplateInfo); + +INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdBeginCustomResolveEXT, VkCommandBuffer commandBuffer, + const VkBeginCustomResolveInfoEXT *pBeginCustomResolveInfo);