// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= VulkanState.h: Vulkan state definitions. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "RHI.h" #include "VulkanRHIPrivate.h" #include "VulkanResources.h" #include "VulkanPendingState.h" class FVulkanCommandListContext; template struct FVulkanAttachmentReference : public TAttachmentReferenceType { FVulkanAttachmentReference() { ZeroStruct(); } FVulkanAttachmentReference(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { SetAttachment(AttachmentReferenceIn, AspectMask); } inline void SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { checkNoEntry(); } inline void SetAttachment(const FVulkanAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { *this = AttachmentReferenceIn; } inline void SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { checkNoEntry(); } inline void ZeroStruct() {} inline void SetAspect(uint32 Aspect) {} }; template <> inline void FVulkanAttachmentReference::SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { attachment = AttachmentReferenceIn.attachment; layout = AttachmentReferenceIn.layout; } template <> inline void FVulkanAttachmentReference::SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; pNext = nullptr; attachment = AttachmentReferenceIn.attachment; layout = AttachmentReferenceIn.layout; aspectMask = AspectMask; } template<> inline void FVulkanAttachmentReference::SetAttachment(const FVulkanAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; pNext = nullptr; attachment = AttachmentReferenceIn.attachment; layout = AttachmentReferenceIn.layout; aspectMask = AspectMask; } template <> inline void FVulkanAttachmentReference::SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { attachment = AttachmentReferenceIn.attachment; const VkImageLayout StencilLayout = StencilReference ? StencilReference->stencilLayout : VK_IMAGE_LAYOUT_UNDEFINED; layout = VulkanRHI::GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilLayout); } template <> inline void FVulkanAttachmentReference::SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; pNext = (bSupportsParallelRendering && StencilReference && StencilReference->stencilLayout != VK_IMAGE_LAYOUT_UNDEFINED) ? StencilReference : nullptr; attachment = AttachmentReferenceIn.attachment; layout = bSupportsParallelRendering ? AttachmentReferenceIn.layout : VulkanRHI::GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilReference->stencilLayout); aspectMask = AspectMask; } template<> inline void FVulkanAttachmentReference::ZeroStruct() { attachment = 0; layout = VK_IMAGE_LAYOUT_UNDEFINED; } template<> inline void FVulkanAttachmentReference::ZeroStruct() { sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; pNext = nullptr; attachment = 0; layout = VK_IMAGE_LAYOUT_UNDEFINED; aspectMask = 0; } template<> inline void FVulkanAttachmentReference::SetAspect(uint32 Aspect) { aspectMask = Aspect; } template class FVulkanSubpassDescription { }; template<> struct FVulkanSubpassDescription : public VkSubpassDescription { FVulkanSubpassDescription() { FMemory::Memzero(this, sizeof(VkSubpassDescription)); pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; } void SetColorAttachments(const TArray>& ColorAttachmentReferences, int OverrideCount = -1) { colorAttachmentCount = (OverrideCount == -1) ? ColorAttachmentReferences.Num() : OverrideCount; pColorAttachments = ColorAttachmentReferences.GetData(); } void SetResolveAttachments(const TArrayView>& ResolveAttachmentReferences) { if (ResolveAttachmentReferences.Num() > 0) { check(colorAttachmentCount == ResolveAttachmentReferences.Num()); pResolveAttachments = ResolveAttachmentReferences.GetData(); } } void SetDepthStencilAttachment(FVulkanAttachmentReference* DepthStencilAttachmentReference) { pDepthStencilAttachment = static_cast(DepthStencilAttachmentReference); } void SetInputAttachments(FVulkanAttachmentReference* InputAttachmentReferences, uint32 NumInputAttachmentReferences) { pInputAttachments = static_cast(InputAttachmentReferences); inputAttachmentCount = NumInputAttachmentReferences; } void SetDepthStencilResolveAttachment(VkSubpassDescriptionDepthStencilResolveKHR* DepthStencilResolveAttachmentDesc) { // No-op without VK_KHR_create_renderpass2 } void SetShadingRateAttachment(VkFragmentShadingRateAttachmentInfoKHR* /* ShadingRateAttachmentInfo */) { // No-op without VK_KHR_create_renderpass2 } void SetMultiViewMask(uint32_t Mask) { // No-op without VK_KHR_create_renderpass2 } }; template<> struct FVulkanSubpassDescription : public VkSubpassDescription2 { FVulkanSubpassDescription() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2); pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; viewMask = 0; } void SetColorAttachments(const TArray>& ColorAttachmentReferences, int OverrideCount = -1) { colorAttachmentCount = OverrideCount == -1 ? ColorAttachmentReferences.Num() : OverrideCount; pColorAttachments = ColorAttachmentReferences.GetData(); } void SetResolveAttachments(const TArrayView>& ResolveAttachmentReferences) { if (ResolveAttachmentReferences.Num() > 0) { check(colorAttachmentCount == ResolveAttachmentReferences.Num()); pResolveAttachments = ResolveAttachmentReferences.GetData(); } } void SetDepthStencilAttachment(FVulkanAttachmentReference* DepthStencilAttachmentReference) { pDepthStencilAttachment = static_cast(DepthStencilAttachmentReference); } void SetInputAttachments(FVulkanAttachmentReference* InputAttachmentReferences, uint32 NumInputAttachmentReferences) { pInputAttachments = static_cast(InputAttachmentReferences); inputAttachmentCount = NumInputAttachmentReferences; } void SetDepthStencilResolveAttachment(VkSubpassDescriptionDepthStencilResolveKHR* DepthStencilResolveAttachmentDesc) { const void* Next = pNext; pNext = DepthStencilResolveAttachmentDesc; DepthStencilResolveAttachmentDesc->pNext = Next; } void SetShadingRateAttachment(VkFragmentShadingRateAttachmentInfoKHR* ShadingRateAttachmentInfo) { const void* Next = pNext; pNext = ShadingRateAttachmentInfo; ShadingRateAttachmentInfo->pNext = Next; } void SetMultiViewMask(uint32_t Mask) { viewMask = Mask; } }; template struct FVulkanSubpassDependency : public TSubpassDependencyType { }; template<> struct FVulkanSubpassDependency : public VkSubpassDependency { FVulkanSubpassDependency() { FMemory::Memzero(this, sizeof(VkSubpassDependency)); } }; template<> struct FVulkanSubpassDependency : public VkSubpassDependency2 { FVulkanSubpassDependency() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2); viewOffset = 0; // According to the Vulkan spec: "If dependencyFlags does not include VK_DEPENDENCY_VIEW_LOCAL_BIT, viewOffset must be 0" } }; template struct FVulkanAttachmentDescription { }; template<> struct FVulkanAttachmentDescription : public VkAttachmentDescription { FVulkanAttachmentDescription() { FMemory::Memzero(this, sizeof(VkAttachmentDescription)); } FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc) { flags = InDesc.flags; format = InDesc.format; samples = InDesc.samples; loadOp = InDesc.loadOp; storeOp = InDesc.storeOp; stencilLoadOp = InDesc.stencilLoadOp; stencilStoreOp = InDesc.stencilStoreOp; initialLayout = InDesc.initialLayout; finalLayout = InDesc.finalLayout; } FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering) { flags = InDesc.flags; format = InDesc.format; samples = InDesc.samples; loadOp = InDesc.loadOp; storeOp = InDesc.storeOp; stencilLoadOp = InDesc.stencilLoadOp; stencilStoreOp = InDesc.stencilStoreOp; const bool bHasStencilLayout = VulkanRHI::VulkanFormatHasStencil(InDesc.format) && (InStencilDesc != nullptr); const VkImageLayout StencilInitialLayout = bHasStencilLayout ? InStencilDesc->stencilInitialLayout : VK_IMAGE_LAYOUT_UNDEFINED; initialLayout = VulkanRHI::GetMergedDepthStencilLayout(InDesc.initialLayout, StencilInitialLayout); const VkImageLayout StencilFinalLayout = bHasStencilLayout ? InStencilDesc->stencilFinalLayout : VK_IMAGE_LAYOUT_UNDEFINED; finalLayout = VulkanRHI::GetMergedDepthStencilLayout(InDesc.finalLayout, StencilFinalLayout); } }; template<> struct FVulkanAttachmentDescription : public VkAttachmentDescription2 { FVulkanAttachmentDescription() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2); } FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc) { sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; pNext = nullptr; flags = InDesc.flags; format = InDesc.format; samples = InDesc.samples; loadOp = InDesc.loadOp; storeOp = InDesc.storeOp; stencilLoadOp = InDesc.stencilLoadOp; stencilStoreOp = InDesc.stencilStoreOp; initialLayout = InDesc.initialLayout; finalLayout = InDesc.finalLayout; } FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering) { const bool bHasStencilLayout = bSupportsParallelRendering && VulkanRHI::VulkanFormatHasStencil(InDesc.format) && (InStencilDesc != nullptr); sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; pNext = (bHasStencilLayout && (InStencilDesc->stencilFinalLayout != VK_IMAGE_LAYOUT_UNDEFINED)) ? InStencilDesc : nullptr; flags = InDesc.flags; format = InDesc.format; samples = InDesc.samples; loadOp = InDesc.loadOp; storeOp = InDesc.storeOp; stencilLoadOp = InDesc.stencilLoadOp; stencilStoreOp = InDesc.stencilStoreOp; initialLayout = bSupportsParallelRendering ? InDesc.initialLayout : VulkanRHI::GetMergedDepthStencilLayout(InDesc.initialLayout, InStencilDesc->stencilInitialLayout); finalLayout = bSupportsParallelRendering ? InDesc.finalLayout : VulkanRHI::GetMergedDepthStencilLayout(InDesc.finalLayout, InStencilDesc->stencilFinalLayout); } }; template struct FVulkanRenderPassCreateInfo {}; template<> struct FVulkanRenderPassCreateInfo : public VkRenderPassCreateInfo { FVulkanRenderPassCreateInfo() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO); } void SetCorrelationMask(const uint32_t* MaskPtr) { // No-op without VK_KHR_create_renderpass2 } VkRenderPass Create(FVulkanDevice& Device) { VkRenderPass Handle = VK_NULL_HANDLE; VERIFYVULKANRESULT_EXPANDED(VulkanRHI::vkCreateRenderPass(Device.GetInstanceHandle(), this, VULKAN_CPU_ALLOCATOR, &Handle)); return Handle; } }; struct FVulkanRenderPassFragmentDensityMapCreateInfoEXT : public VkRenderPassFragmentDensityMapCreateInfoEXT { FVulkanRenderPassFragmentDensityMapCreateInfoEXT() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT); } }; struct FVulkanRenderPassMultiviewCreateInfo : public VkRenderPassMultiviewCreateInfo { FVulkanRenderPassMultiviewCreateInfo() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO); } }; template<> struct FVulkanRenderPassCreateInfo : public VkRenderPassCreateInfo2 { FVulkanRenderPassCreateInfo() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2); } void SetCorrelationMask(const uint32_t* MaskPtr) { correlatedViewMaskCount = 1; pCorrelatedViewMasks = MaskPtr; } VkRenderPass Create(FVulkanDevice& Device) { VkRenderPass Handle = VK_NULL_HANDLE; VERIFYVULKANRESULT_EXPANDED(VulkanRHI::vkCreateRenderPass2KHR(Device.GetInstanceHandle(), this, VULKAN_CPU_ALLOCATOR, &Handle)); return Handle; } }; struct FVulkanFragmentShadingRateAttachmentInfo : public VkFragmentShadingRateAttachmentInfoKHR { FVulkanFragmentShadingRateAttachmentInfo() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR); // For now, just use the smallest tile-size available. TODO: Add a setting to allow prioritizing either higher resolution/larger shading rate attachment targets // or lower-resolution/smaller attachments. shadingRateAttachmentTexelSize = { (uint32)GRHIVariableRateShadingImageTileMinWidth, (uint32)GRHIVariableRateShadingImageTileMinHeight }; } void SetReference(FVulkanAttachmentReference* AttachmentReference) { pFragmentShadingRateAttachment = AttachmentReference; } }; struct FVulkanDepthStencilResolveSubpassDesc : public VkSubpassDescriptionDepthStencilResolveKHR { FVulkanDepthStencilResolveSubpassDesc() { ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE); } void SetResolveModes(VkResolveModeFlagBits DepthMode, VkResolveModeFlagBits StencilMode) { depthResolveMode = DepthMode; stencilResolveMode = StencilMode; } void SetReference(FVulkanAttachmentReference* AttachmentReference) { pDepthStencilResolveAttachment = AttachmentReference; } }; extern int32 GVulkanInputAttachmentShaderRead; template class FVulkanRenderPassBuilder { public: FVulkanRenderPassBuilder(FVulkanDevice& InDevice) : Device(InDevice) , CorrelationMask(0) {} void BuildCreateInfo(const FVulkanRenderTargetLayout& RTLayout) { uint32 NumSubpasses = 0; uint32 NumDependencies = 0; //0b11 for 2, 0b1111 for 4, and so on uint32 MultiviewMask = (0b1 << RTLayout.GetMultiViewCount()) - 1; const bool bDeferredShadingSubpass = RTLayout.GetSubpassHint() == ESubpassHint::DeferredShadingSubpass; const bool bApplyFragmentShadingRate = GRHISupportsAttachmentVariableRateShading && RTLayout.GetFragmentDensityAttachmentReference() != nullptr && Device.GetOptionalExtensions().HasKHRFragmentShadingRate && Device.GetOptionalExtensionProperties().FragmentShadingRateFeatures.attachmentFragmentShadingRate == VK_TRUE; const bool bResolveDepth = GRHISupportsDepthStencilResolve && Device.GetOptionalExtensions().HasKHRDepthStencilResolve && RTLayout.GetHasDepthStencilResolve(); const bool bCustomResolveSubpass = RTLayout.GetSubpassHint() == ESubpassHint::CustomResolveSubpass; const bool bDepthReadSubpass = bCustomResolveSubpass || (RTLayout.GetSubpassHint() == ESubpassHint::DepthReadSubpass); const bool bHasDepthStencilAttachmentReference = (RTLayout.GetDepthAttachmentReference() != nullptr); if (bApplyFragmentShadingRate) { ShadingRateAttachmentReference.SetAttachment(*RTLayout.GetFragmentDensityAttachmentReference(), VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT); FragmentShadingRateAttachmentInfo.SetReference(&ShadingRateAttachmentReference); } if (bResolveDepth) { DepthStencilResolveAttachmentReference.SetAttachment(*RTLayout.GetDepthStencilResolveAttachmentReference(), VkImageAspectFlagBits::VK_IMAGE_ASPECT_NONE); // Using zero bit because it is always supported if the extension is supported, from spec: "The VK_RESOLVE_MODE_SAMPLE_ZERO_BIT mode // is the only mode that is required of all implementations (that support the extension or support Vulkan 1.2 or higher)." DepthStencilResolveSubpassDesc.SetResolveModes(VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT); DepthStencilResolveSubpassDesc.SetReference(&DepthStencilResolveAttachmentReference); } // Grab (and optionally convert) attachment references. uint32 NumColorAttachments = RTLayout.GetNumColorAttachments(); for (uint32 ColorAttachment = 0; ColorAttachment < NumColorAttachments; ++ColorAttachment) { ColorAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetColorAttachmentReferences()[ColorAttachment], 0)); if (RTLayout.GetResolveAttachmentReferences() != nullptr) { ResolveAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetResolveAttachmentReferences()[ColorAttachment], 0)); } } // CustomResolveSubpass has an additional color attachment that should not be used by main and depth subpasses if (bCustomResolveSubpass && (NumColorAttachments > 1)) { NumColorAttachments--; } uint32_t DepthInputAttachment = VK_ATTACHMENT_UNUSED; VkImageLayout DepthInputAttachmentLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageAspectFlags DepthInputAspectMask = 0; if (bHasDepthStencilAttachmentReference) { DepthStencilAttachmentReference.SetDepthStencilAttachment(*RTLayout.GetDepthAttachmentReference(), RTLayout.GetStencilAttachmentReference(), 0, Device.SupportsParallelRendering()); if (bDepthReadSubpass || bDeferredShadingSubpass) { DepthStencilAttachment.attachment = RTLayout.GetDepthAttachmentReference()->attachment; DepthStencilAttachment.SetAspect(VK_IMAGE_ASPECT_DEPTH_BIT); // @todo? // FIXME: checking a Depth layout is not correct in all cases // PSO cache can create a PSO for subpass 1 or 2 first, where depth is read-only but that does not mean depth pre-pass is enabled if (false && RTLayout.GetDepthAttachmentReference()->layout == VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL) { // Depth is read only and is expected to be sampled as a regular texture DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL; } else { // lights write to stencil for culling, so stencil is expected to be writebale while depth is read-only DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL; DepthInputAttachment = DepthStencilAttachment.attachment; DepthInputAttachmentLayout = DepthStencilAttachment.layout; DepthInputAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; } } } // main sub-pass { TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++]; SubpassDesc.SetColorAttachments(ColorAttachmentReferences, NumColorAttachments); if (bHasDepthStencilAttachmentReference) { SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachmentReference); } if (!bDepthReadSubpass && bResolveDepth) { SubpassDesc.SetDepthStencilResolveAttachment(&DepthStencilResolveSubpassDesc); } if (bApplyFragmentShadingRate) { SubpassDesc.SetShadingRateAttachment(&FragmentShadingRateAttachmentInfo); } SubpassDesc.SetMultiViewMask(MultiviewMask); } // Color write and depth read sub-pass if (bDepthReadSubpass) { TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++]; SubpassDesc.SetColorAttachments(ColorAttachmentReferences, 1); check(RTLayout.GetDepthAttachmentReference()); // Depth as Input0 InputAttachments1[0].attachment = DepthInputAttachment; InputAttachments1[0].layout = DepthInputAttachmentLayout; InputAttachments1[0].SetAspect(DepthInputAspectMask); SubpassDesc.SetInputAttachments(InputAttachments1, InputAttachment1Count); // depth attachment is same as input attachment SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment); if (bResolveDepth && !bCustomResolveSubpass) { SubpassDesc.SetDepthStencilResolveAttachment(&DepthStencilResolveSubpassDesc); } if (bApplyFragmentShadingRate) { SubpassDesc.SetShadingRateAttachment(&FragmentShadingRateAttachmentInfo); } SubpassDesc.SetMultiViewMask(MultiviewMask); TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++]; SubpassDep.srcSubpass = 0; SubpassDep.dstSubpass = 1; SubpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; SubpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; } // Two subpasses for deferred shading if (bDeferredShadingSubpass) { //const VkAttachmentReference* ColorRef = RTLayout.GetColorAttachmentReferences(); //uint32 NumColorAttachments = RTLayout.GetNumColorAttachments(); //check(RTLayout.GetNumColorAttachments() == 5); //current layout is SceneColor, GBufferA/B/C/D // 1. Write to SceneColor and GBuffer, input DepthStencil { TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++]; SubpassDesc.SetColorAttachments(ColorAttachmentReferences); SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment); InputAttachments1[0].attachment = DepthInputAttachment; InputAttachments1[0].layout = DepthInputAttachmentLayout; InputAttachments1[0].SetAspect(DepthInputAspectMask); SubpassDesc.SetInputAttachments(InputAttachments1, InputAttachment1Count); if (bApplyFragmentShadingRate) { SubpassDesc.SetShadingRateAttachment(&FragmentShadingRateAttachmentInfo); } SubpassDesc.SetMultiViewMask(MultiviewMask); // Depth as Input0 TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++]; SubpassDep.srcSubpass = 0; SubpassDep.dstSubpass = 1; SubpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; SubpassDep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; } // 2. Write to SceneColor, input GBuffer and DepthStencil { TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++]; SubpassDesc.SetColorAttachments(ColorAttachmentReferences, 1); // SceneColor only SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment); // Depth as Input0 InputAttachments2[0].attachment = DepthInputAttachment; InputAttachments2[0].layout = DepthInputAttachmentLayout; InputAttachments2[0].SetAspect(DepthInputAspectMask); // SceneColor write only InputAttachments2[1].attachment = VK_ATTACHMENT_UNUSED; InputAttachments2[1].layout = VK_IMAGE_LAYOUT_UNDEFINED; InputAttachments2[1].SetAspect(0); // GBufferA/B/C/D as Input2/3/4/5 int32 NumColorInputs = ColorAttachmentReferences.Num() - 1; for (int32 i = 2; i < (NumColorInputs + 2); ++i) { InputAttachments2[i].attachment = ColorAttachmentReferences[i - 1].attachment; InputAttachments2[i].layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; InputAttachments2[i].SetAspect(VK_IMAGE_ASPECT_COLOR_BIT); } SubpassDesc.SetInputAttachments(InputAttachments2, NumColorInputs + 2); if (bApplyFragmentShadingRate) { SubpassDesc.SetShadingRateAttachment(&FragmentShadingRateAttachmentInfo); } SubpassDesc.SetMultiViewMask(MultiviewMask); TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++]; SubpassDep.srcSubpass = 1; SubpassDep.dstSubpass = 2; SubpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; SubpassDep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; if (GVulkanInputAttachmentShaderRead == 1) { // this is not required, but might flicker on some devices without SubpassDep.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT; } SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; } } // Custom resolve subpass if (bCustomResolveSubpass) { TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++]; ColorAttachments3[0].attachment = ColorAttachmentReferences[1].attachment; ColorAttachments3[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; ColorAttachments3[0].SetAspect(VK_IMAGE_ASPECT_COLOR_BIT); InputAttachments3[0].attachment = VK_ATTACHMENT_UNUSED; // The subpass fetch logic expects depth in first attachment. InputAttachments3[0].layout = VK_IMAGE_LAYOUT_UNDEFINED; InputAttachments3[0].SetAspect(0); InputAttachments3[1].attachment = ColorAttachmentReferences[0].attachment; // SceneColor as input InputAttachments3[1].layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; InputAttachments3[1].SetAspect(VK_IMAGE_ASPECT_COLOR_BIT); SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment); if (bResolveDepth) { SubpassDesc.SetDepthStencilResolveAttachment(&DepthStencilResolveSubpassDesc); } SubpassDesc.SetInputAttachments(InputAttachments3, 2); SubpassDesc.colorAttachmentCount = 1; SubpassDesc.pColorAttachments = ColorAttachments3; SubpassDesc.SetMultiViewMask(MultiviewMask); TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++]; SubpassDep.srcSubpass = 1; SubpassDep.dstSubpass = 2; SubpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; SubpassDep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; } // CustomResolveSubpass does a custom resolve into second color target and does not need resolve attachments if (!bCustomResolveSubpass) { if (bDepthReadSubpass && ResolveAttachmentReferences.Num() > 1) { // Handle SceneDepthAux resolve: // The depth read subpass has only a single color attachment (using more would not be compatible dual source blending), so make SceneDepthAux a resolve attachment of the first subpass instead ResolveAttachmentReferences.Add(TAttachmentReferenceClass{}); ResolveAttachmentReferences.Last().attachment = VK_ATTACHMENT_UNUSED; Swap(ResolveAttachmentReferences.Last(), ResolveAttachmentReferences[0]); SubpassDescriptions[0].SetResolveAttachments(TArrayView(ResolveAttachmentReferences.GetData(), ResolveAttachmentReferences.Num() - 1)); SubpassDescriptions[NumSubpasses - 1].SetResolveAttachments(TArrayView(&ResolveAttachmentReferences.Last(), 1)); } else { // Only set resolve attachment on the last subpass SubpassDescriptions[NumSubpasses - 1].SetResolveAttachments(ResolveAttachmentReferences); } } for (uint32 Attachment = 0; Attachment < RTLayout.GetNumAttachmentDescriptions(); ++Attachment) { if (bHasDepthStencilAttachmentReference && (Attachment == DepthStencilAttachmentReference.attachment)) { AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDesc(), Device.SupportsParallelRendering())); } else { AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment])); } } CreateInfo.attachmentCount = AttachmentDescriptions.Num(); CreateInfo.pAttachments = AttachmentDescriptions.GetData(); CreateInfo.subpassCount = NumSubpasses; CreateInfo.pSubpasses = SubpassDescriptions; CreateInfo.dependencyCount = NumDependencies; CreateInfo.pDependencies = SubpassDependencies; /* Bit mask that specifies which view rendering is broadcast to 0011 = Broadcast to first and second view (layer) */ ViewMask[0] = MultiviewMask; ViewMask[1] = MultiviewMask; /* Bit mask that specifices correlation between views An implementation may use this for optimizations (concurrent render) */ CorrelationMask = MultiviewMask; if (RTLayout.GetIsMultiView()) { if (Device.GetOptionalExtensions().HasKHRRenderPass2) { CreateInfo.SetCorrelationMask(&CorrelationMask); } else { checkf(Device.GetOptionalExtensions().HasKHRMultiview, TEXT("Layout is multiview but extension is not supported!")); MultiviewInfo.subpassCount = NumSubpasses; MultiviewInfo.pViewMasks = ViewMask; MultiviewInfo.dependencyCount = 0; MultiviewInfo.pViewOffsets = nullptr; MultiviewInfo.correlationMaskCount = 1; MultiviewInfo.pCorrelationMasks = &CorrelationMask; MultiviewInfo.pNext = CreateInfo.pNext; CreateInfo.pNext = &MultiviewInfo; } } if (Device.GetOptionalExtensions().HasEXTFragmentDensityMap && RTLayout.GetHasFragmentDensityAttachment()) { FragDensityCreateInfo.fragmentDensityMapAttachment = *RTLayout.GetFragmentDensityAttachmentReference(); // Chain fragment density info onto create info and the rest of the pNexts // onto the fragment density info FragDensityCreateInfo.pNext = CreateInfo.pNext; CreateInfo.pNext = &FragDensityCreateInfo; } } VkRenderPass Create(const FVulkanRenderTargetLayout& RTLayout) { BuildCreateInfo(RTLayout); return CreateInfo.Create(Device); } TRenderPassCreateInfoClass& GetCreateInfo() { return CreateInfo; } private: TSubpassDescriptionClass SubpassDescriptions[8]; TSubpassDependencyClass SubpassDependencies[8]; TArray ColorAttachmentReferences; TArray ResolveAttachmentReferences; // Color write and depth read sub-pass static const uint32 InputAttachment1Count = 1; TAttachmentReferenceClass InputAttachments1[InputAttachment1Count]; // Two subpasses for deferred shading TAttachmentReferenceClass InputAttachments2[MaxSimultaneousRenderTargets + 1]; TAttachmentReferenceClass DepthStencilAttachment; TAttachmentReferenceClass DepthStencilAttachmentReference; TArray AttachmentDescriptions; // Tonemap subpass TAttachmentReferenceClass InputAttachments3[MaxSimultaneousRenderTargets + 1]; TAttachmentReferenceClass ColorAttachments3[MaxSimultaneousRenderTargets + 1]; FVulkanAttachmentReference ShadingRateAttachmentReference; FVulkanFragmentShadingRateAttachmentInfo FragmentShadingRateAttachmentInfo; // Depth stencil resolve FVulkanAttachmentReference DepthStencilResolveAttachmentReference; FVulkanDepthStencilResolveSubpassDesc DepthStencilResolveSubpassDesc; FVulkanRenderPassFragmentDensityMapCreateInfoEXT FragDensityCreateInfo; FVulkanRenderPassMultiviewCreateInfo MultiviewInfo; TRenderPassCreateInfoClass CreateInfo; FVulkanDevice& Device; uint32 ViewMask[2]; uint32 CorrelationMask; }; VkRenderPass CreateVulkanRenderPass(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout); struct FVulkanBeginRenderPassInfo { FVulkanRenderPass& RenderPass; FVulkanFramebuffer& Framebuffer; bool bIsParallelRenderPass; }; class FVulkanRenderPassManager { public: FVulkanRenderPassManager(FVulkanDevice& InDevice) : Device(InDevice) {} ~FVulkanRenderPassManager(); FVulkanFramebuffer* GetOrCreateFramebuffer(const FRHISetRenderTargetsInfo& RenderTargetsInfo, const FVulkanRenderTargetLayout& RTLayout, FVulkanRenderPass* RenderPass); FVulkanRenderPass* GetOrCreateRenderPass(const FVulkanRenderTargetLayout& RTLayout) { const uint32 RenderPassHash = RTLayout.GetRenderPassFullHash(); { FRWScopeLock ScopedReadLock(RenderPassesLock, SLT_ReadOnly); FVulkanRenderPass** FoundRenderPass = RenderPasses.Find(RenderPassHash); if (FoundRenderPass) { return *FoundRenderPass; } } FVulkanRenderPass* RenderPass = new FVulkanRenderPass(Device, RTLayout); { FRWScopeLock ScopedWriteLock(RenderPassesLock, SLT_Write); FVulkanRenderPass** FoundRenderPass = RenderPasses.Find(RenderPassHash); if (FoundRenderPass) { delete RenderPass; return *FoundRenderPass; } RenderPasses.Add(RenderPassHash, RenderPass); } return RenderPass; } void BeginRenderPass(FVulkanCommandListContext& Context, const FRHIRenderPassInfo& RPInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanBeginRenderPassInfo& BeginRenderPassInfo); void EndRenderPass(FVulkanCommandListContext& Context); FRWLock RenderPassesLock; FRWLock FramebuffersLock; void NotifyDeletedRenderTarget(VkImage Image); private: TMap RenderPasses; struct FFramebufferList { TArray Framebuffer; }; TMap Framebuffers; FVulkanDevice& Device; };