| /* |
| * Copyright 2023 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/graphite/vk/VulkanRenderPass.h" |
| |
| #include "include/gpu/graphite/vk/VulkanGraphiteTypes.h" |
| #include "src/gpu/graphite/RenderPassDesc.h" |
| #include "src/gpu/graphite/Texture.h" |
| #include "src/gpu/graphite/vk/VulkanCommandBuffer.h" |
| #include "src/gpu/graphite/vk/VulkanGraphiteUtils.h" |
| #include "src/gpu/graphite/vk/VulkanSharedContext.h" |
| #include "src/gpu/graphite/vk/VulkanTexture.h" |
| |
| #include <limits> |
| |
| static constexpr uint32_t kColorFormatOffset = 0; |
| static constexpr uint32_t kDepthStencilFormatOffset = kColorFormatOffset + 8; |
| static constexpr uint32_t kSampleCountOffset = kDepthStencilFormatOffset + 8; |
| static constexpr uint32_t kColorLoadOpOffset = kSampleCountOffset + 5; |
| static constexpr uint32_t kColorStoreOpOffset = kColorLoadOpOffset + 2; |
| static constexpr uint32_t kResolveLoadOpOffset = kColorStoreOpOffset + 1; |
| static constexpr uint32_t kResolveStoreOpOffset = kResolveLoadOpOffset + 2; |
| static constexpr uint32_t kMSRTTOffset = kResolveStoreOpOffset + 1; |
| |
| static constexpr uint32_t kFormatMask = 0xFF; |
| static constexpr uint32_t kSampleCountMask = 0x1F; |
| static constexpr uint32_t kLoadOpMask = 0x3; |
| static constexpr uint32_t kStoreOpMask = 0x1; |
| static constexpr uint32_t kMSRTTMask = 0x1; |
| |
| namespace skgpu::graphite { |
| |
| uint32_t VulkanRenderPass::GetRenderPassKey(const RenderPassDesc& originalRenderPassDesc, |
| bool compatibleForPipelineKey) { |
| const RenderPassDesc renderPassDesc = |
| compatibleForPipelineKey ? MakePipelineCompatibleRenderPass(originalRenderPassDesc) |
| : originalRenderPassDesc; |
| const AttachmentDesc color = renderPassDesc.fColorAttachment; |
| const AttachmentDesc resolve = renderPassDesc.fColorResolveAttachment; |
| const AttachmentDesc depthStencil = renderPassDesc.fDepthStencilAttachment; |
| |
| const bool hasResolveAttachment = resolve.fFormat != TextureFormat::kUnsupported; |
| const bool isMultisampledRenderToSingleSampled = |
| RenderPassDescWillImplicitlyLoadMSAA(renderPassDesc); |
| // Silence warning about unused variable in release builds. |
| (void)hasResolveAttachment; |
| |
| // Current assumptions in the render pass desc, allowing us to create a smaller key: |
| SkASSERT(color.fFormat != TextureFormat::kUnsupported); |
| SkASSERT(!hasResolveAttachment || resolve.fFormat == color.fFormat); |
| SkASSERT(depthStencil.fFormat == TextureFormat::kUnsupported || |
| depthStencil.fSampleCount == color.fSampleCount); |
| SkASSERT(depthStencil.fFormat == TextureFormat::kUnsupported || |
| depthStencil.fLoadOp == LoadOp::kClear); |
| SkASSERT(depthStencil.fFormat == TextureFormat::kUnsupported || |
| depthStencil.fStoreOp == StoreOp::kDiscard); |
| SkASSERT(!hasResolveAttachment || resolve.fSampleCount == 1); |
| SkASSERT(color.fSampleCount == renderPassDesc.fSampleCount || |
| isMultisampledRenderToSingleSampled); |
| SkASSERT(!hasResolveAttachment || color.fLoadOp == LoadOp::kDiscard || |
| color.fLoadOp == LoadOp::kClear); |
| SkASSERT(!hasResolveAttachment || resolve.fLoadOp == LoadOp::kDiscard || |
| resolve.fLoadOp == LoadOp::kLoad); |
| SkASSERT(!hasResolveAttachment || color.fStoreOp == StoreOp::kDiscard); |
| |
| // The following information uniquely defines the render pass: |
| // |
| // Color format (CF): TextureFormat, fits in 6 bits |
| // Depth/stencil format (DSF): TextureFormat, fits in 6 bits |
| // Sample count (M): Up to 16, fits in 5 bit |
| // Color load op (L): LoadOp, fits in 2 bits |
| // Color store op (S): StoreOp, fits in 1 bit |
| // Whether multisampled data s loaded from resolve attachment: fits in 1 bit |
| // Whether rendering multisampled->single-sampled (MSRTSS): fits in 1 bit |
| // |
| // Note that technically, renderable color formats can fit in 5 bits and depth/stencil formats |
| // in 3 bits if more packing is needed. Sample count can also be `log()`'ed to reduce the bit |
| // count. |
| // |
| // Depth/stencil load op is always kClear, and store op is always kDiscard. |
| // Color load op is either found in fColorAttachment if no resolve attachment, or can be derived |
| // from a combination of the load ops specified in the color and resolve attachments: |
| // * If color attachment is kClear, load op is kClear |
| // * Otherwise, if resolve attachment is kLoad, load op is kLoad |
| // * Otherwise, it's kDiscard |
| // Color store op is either found in fColorAttachment if no resolve attachment, or it's found in |
| // fColorResolveAttachment. |
| // |
| // There are currently lots of free bits, so with regards to load/store ops, we don't try too |
| // hard to pack things. Including the color and resolve's load and store ops obviates the need |
| // to store a "load MSAA from resolve" bit. The format of the key is thus: |
| // |
| // LSB MSB |
| // +----+-----+---+---+---+------------+------------+--------+---+ |
| // | CF | DSF | M | L | S | L(resolve) | S(resolve) | MSRTSS | 0 | |
| // +----+-----+---+---+---+------------+------------+--------+---+ |
| // bits 8 8 5 2 1 2 1 1 4 |
| // |
| SkASSERT(renderPassDesc.fSampleCount < (1 << 5)); |
| static_assert(static_cast<uint32_t>(TextureFormat::kLast) < (1 << 8)); |
| static_assert(static_cast<uint32_t>(LoadOp::kLast) < (1 << 2)); |
| static_assert(static_cast<uint32_t>(StoreOp::kLast) < (1 << 1)); |
| |
| const uint32_t key = |
| (static_cast<uint32_t>(color.fFormat) << kColorFormatOffset) | |
| (static_cast<uint32_t>(depthStencil.fFormat) << kDepthStencilFormatOffset) | |
| (static_cast<uint32_t>(renderPassDesc.fSampleCount) << kSampleCountOffset) | |
| (static_cast<uint32_t>(color.fLoadOp) << kColorLoadOpOffset) | |
| (static_cast<uint32_t>(color.fStoreOp) << kColorStoreOpOffset) | |
| (static_cast<uint32_t>(resolve.fLoadOp) << kResolveLoadOpOffset) | |
| (static_cast<uint32_t>(resolve.fStoreOp) << kResolveStoreOpOffset) | |
| (static_cast<uint32_t>(isMultisampledRenderToSingleSampled) << kMSRTTOffset); |
| |
| return key; |
| } |
| |
| void VulkanRenderPass::ExtractRenderPassDesc(uint32_t key, |
| Swizzle writeSwizzle, |
| DstReadStrategy dstReadStrategy, |
| RenderPassDesc* renderPassDesc) { |
| // See comment in GetRenderPassKey() describing the format of the key. |
| const TextureFormat colorFormat = |
| SkTo<TextureFormat>((key >> kColorFormatOffset) & kFormatMask); |
| const TextureFormat depthStencilFormat = |
| SkTo<TextureFormat>((key >> kDepthStencilFormatOffset) & kFormatMask); |
| const uint8_t sampleCount = SkTo<uint8_t>((key >> kSampleCountOffset) & kSampleCountMask); |
| const LoadOp colorLoadOp = SkTo<LoadOp>((key >> kColorLoadOpOffset) & kLoadOpMask); |
| const StoreOp colorStoreOp = SkTo<StoreOp>((key >> kColorStoreOpOffset) & kStoreOpMask); |
| const LoadOp resolveLoadOp = SkTo<LoadOp>((key >> kResolveLoadOpOffset) & kLoadOpMask); |
| const StoreOp resolveStoreOp = SkTo<StoreOp>((key >> kResolveStoreOpOffset) & kStoreOpMask); |
| const bool isMultisampledRenderToSingleSampled = SkTo<bool>((key >> kMSRTTOffset) & kMSRTTMask); |
| |
| // Relationship between render pass sample count, attachment's sample counts and resolve |
| // attachments: |
| // |
| // | RP Samples == 1 | RP Samples > 1 && MSRTSS | RP Samples > 1 && !MSRTSS | |
| // +-----------------+--------------------------+---------------------------+ |
| // Has Resolve? | No | No | Yes | |
| // +-----------------+--------------------------+---------------------------+ |
| // Resolve Format | Unsupported | Unsupported | Color Format | |
| // +-----------------+--------------------------+---------------------------+ |
| // Color Samples | RP Samples (1) | 1 | RP Samples | |
| // +-----------------+--------------------------+---------------------------+ |
| // D/S Samples | RP Samples (1) | 1 | RP Samples | |
| // +-----------------+--------------------------+---------------------------+ |
| // |
| // The color and resolve attachment's load/store op are already stored in the key. For |
| // depth/stencil, load op is always Clear and store op is always Discard. |
| const uint8_t attachmentSamples = isMultisampledRenderToSingleSampled ? 1 : sampleCount; |
| |
| *renderPassDesc = {}; |
| renderPassDesc->fColorAttachment = {colorFormat, colorLoadOp, colorStoreOp, attachmentSamples}; |
| renderPassDesc->fDepthStencilAttachment = { |
| depthStencilFormat, LoadOp::kClear, StoreOp::kDiscard, attachmentSamples}; |
| if (attachmentSamples > 1 && !isMultisampledRenderToSingleSampled) { |
| renderPassDesc->fColorResolveAttachment = {colorFormat, |
| resolveLoadOp, |
| resolveStoreOp, |
| /*fSampleCount=*/1}; |
| } |
| renderPassDesc->fSampleCount = sampleCount; |
| renderPassDesc->fWriteSwizzle = writeSwizzle; |
| renderPassDesc->fDstReadStrategy = dstReadStrategy; |
| } |
| |
| namespace { |
| |
| template <typename AttachmentDescription> |
| void setup_vk_attachment_description(AttachmentDescription* outAttachment, |
| const AttachmentDesc& desc, |
| const AttachmentDescription& defaultAttachmentDescription, |
| bool isColor) { |
| static_assert((int) LoadOp::kLoad == (int) VK_ATTACHMENT_LOAD_OP_LOAD); |
| static_assert((int) LoadOp::kClear == (int) VK_ATTACHMENT_LOAD_OP_CLEAR); |
| static_assert((int) LoadOp::kDiscard == (int) VK_ATTACHMENT_LOAD_OP_DONT_CARE); |
| static_assert((int) StoreOp::kStore == (int) VK_ATTACHMENT_STORE_OP_STORE); |
| static_assert((int) StoreOp::kDiscard == (int) VK_ATTACHMENT_STORE_OP_DONT_CARE); |
| |
| VkAttachmentLoadOp vkLoadOp = static_cast<VkAttachmentLoadOp>(desc.fLoadOp); |
| VkAttachmentStoreOp vkStoreOp = static_cast<VkAttachmentStoreOp>(desc.fStoreOp); |
| |
| *outAttachment = defaultAttachmentDescription; |
| outAttachment->format = TextureFormatToVkFormat(desc.fFormat); |
| VkSampleCountFlagBits sampleCount; |
| SkAssertResult(SampleCountToVkSampleCount(desc.fSampleCount, &sampleCount)); |
| outAttachment->samples = sampleCount; |
| |
| outAttachment->loadOp = vkLoadOp; |
| outAttachment->storeOp = vkStoreOp; |
| if (isColor) { |
| outAttachment->initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| outAttachment->finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| outAttachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| outAttachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| } else { |
| outAttachment->initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| outAttachment->finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| outAttachment->stencilLoadOp = vkLoadOp; |
| outAttachment->stencilStoreOp = vkStoreOp; |
| } |
| } |
| |
| template <typename AttachmentDescription, typename AttachmentReference> |
| void populate_attachment_refs(const RenderPassDesc& renderPassDesc, |
| bool needLoadMSAAFromResolveSubpass, |
| skia_private::TArray<AttachmentDescription>& descs, |
| AttachmentReference& colorRef, |
| AttachmentReference& resolveRef, |
| AttachmentReference& resolveLoadInputRef, |
| AttachmentReference& depthStencilRef, |
| const AttachmentDescription& defaultAttachmentDescription) { |
| // Note: See assumptions regarding RenderPassDesc structure in AddToKey(). |
| const AttachmentDesc color = renderPassDesc.fColorAttachment; |
| const AttachmentDesc resolve = renderPassDesc.fColorResolveAttachment; |
| const AttachmentDesc depthStencil = renderPassDesc.fDepthStencilAttachment; |
| SkASSERT(color.fFormat != TextureFormat::kUnsupported); |
| // If MSAA data needs to be loaded from the resolve attachment, there must be a resolve |
| // attachment! |
| SkASSERT(!needLoadMSAAFromResolveSubpass || resolve.fFormat != TextureFormat::kUnsupported); |
| |
| // If reading from the dst as an input attachment, we must use VK_IMAGE_LAYOUT_GENERAL |
| // for the color attachment description. Use a general image layout for all renderpasses to |
| // support this. |
| // |
| // As long as the initial/final layouts are VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, the |
| // choice of the VK_IMAGE_LAYOUT_GENERAL for the subpass layout is efficient; the few GPUs |
| // that treat VK_IMAGE_LAYOUT_GENERAL differently recognize this pattern and keep the |
| // internal layout optimal. |
| colorRef.layout = VK_IMAGE_LAYOUT_GENERAL; |
| colorRef.attachment = descs.size(); |
| AttachmentDescription& vkColorAttachDesc = descs.push_back(); |
| setup_vk_attachment_description(&vkColorAttachDesc, |
| color, |
| defaultAttachmentDescription, |
| /*isColor=*/true); |
| |
| if (resolve.fFormat != TextureFormat::kUnsupported) { |
| resolveRef.attachment = descs.size(); |
| resolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| if (needLoadMSAAFromResolveSubpass) { |
| resolveLoadInputRef.attachment = resolveRef.attachment; |
| // The resolve attachment is used as input attachment in the first subpass. In that |
| // subpass, it is only read from so the layout could have been read-only. To avoid |
| // special-casing this input attachment though, the VK_IMAGE_LAYOUT_GENERAL layout |
| // is used like with the color attachment. This is no less efficient than the |
| // read-only layout when specified on the attachment ref. |
| resolveLoadInputRef.layout = VK_IMAGE_LAYOUT_GENERAL; |
| } else { |
| resolveLoadInputRef.attachment = VK_ATTACHMENT_UNUSED; |
| } |
| |
| AttachmentDescription& vkResolveAttachDesc = descs.push_back(); |
| setup_vk_attachment_description(&vkResolveAttachDesc, |
| resolve, |
| defaultAttachmentDescription, |
| /*isColor=*/true); |
| } else { |
| resolveRef.attachment = resolveLoadInputRef.attachment = VK_ATTACHMENT_UNUSED; |
| } |
| |
| if (depthStencil.fFormat != TextureFormat::kUnsupported) { |
| depthStencilRef.attachment = descs.size(); |
| depthStencilRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| |
| AttachmentDescription& vkDepthStencilAttachDesc = descs.push_back(); |
| setup_vk_attachment_description(&vkDepthStencilAttachDesc, |
| depthStencil, |
| defaultAttachmentDescription, |
| /*isColor=*/false); |
| } else { |
| depthStencilRef.attachment = VK_ATTACHMENT_UNUSED; |
| } |
| } |
| |
| template <typename SubpassDependency, typename SubpassDependencyArray> |
| void populate_subpass_dependencies(const VulkanSharedContext* context, |
| SubpassDependencyArray& deps, |
| bool needLoadMSAAFromResolveSubpass, |
| const SubpassDependency& defaultSubpassDependency) { |
| const int mainSubpassIdx = needLoadMSAAFromResolveSubpass ? 1 : 0; |
| |
| // Adding a single subpass self-dependency for color attachments is basically free, so apply |
| // one to every RenderPass which has an input attachment on the main subpass. This is useful |
| // because it means that as we perform draw calls, if we encounter a draw that uses a blend |
| // operation requiring a dst read, we can avoid having to switch RenderPasses. |
| |
| const bool hasRasterizationOrderColorAttachmentAccess = |
| context->vulkanCaps().supportsRasterizationOrderColorAttachmentAccess(); |
| const bool hasNonCoherentAdvancedBlend = context->vulkanCaps().blendEquationSupport() == |
| Caps::BlendEquationSupport::kAdvancedNoncoherent; |
| |
| if (!hasRasterizationOrderColorAttachmentAccess || hasNonCoherentAdvancedBlend) { |
| SubpassDependency& selfDependency = deps.push_back(); |
| selfDependency = defaultSubpassDependency; |
| selfDependency.srcSubpass = mainSubpassIdx; |
| selfDependency.dstSubpass = mainSubpassIdx; |
| selfDependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; |
| selfDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| selfDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| selfDependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| selfDependency.dstAccessMask = 0; |
| |
| if (!hasRasterizationOrderColorAttachmentAccess) { |
| selfDependency.dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; |
| selfDependency.dstAccessMask |= VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; |
| } |
| if (hasNonCoherentAdvancedBlend) { |
| selfDependency.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT; |
| } |
| } |
| |
| // If loading MSAA from resolve, enforce that subpass goes first with a subpass dependency. |
| if (needLoadMSAAFromResolveSubpass) { |
| SubpassDependency& dependency = deps.push_back(); |
| dependency = defaultSubpassDependency; |
| dependency.srcSubpass = 0; |
| dependency.dstSubpass = mainSubpassIdx; |
| dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; |
| dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| dependency.dstAccessMask = |
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| } |
| } |
| |
| template <typename SubpassDescription, typename AttachmentReference> |
| void populate_main_subpass_desc(const VulkanCaps& caps, |
| SubpassDescription& mainSubpassDesc, |
| const AttachmentReference& colorRef, |
| const AttachmentReference& resolveRef, |
| const AttachmentReference& depthStencilRef) { |
| mainSubpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| // In the main subpass, the input attachment is the color attachment. We could have excluded |
| // this attachment if unused, but always assigning it to be the color attachment even when |
| // unused allows for compatible-only renderpasses to be shared for pipelines that do read from |
| // the dst and those that do not. |
| mainSubpassDesc.inputAttachmentCount = 1; |
| mainSubpassDesc.pInputAttachments = &colorRef; |
| mainSubpassDesc.colorAttachmentCount = 1; |
| mainSubpassDesc.pColorAttachments = &colorRef; |
| mainSubpassDesc.pResolveAttachments = &resolveRef; |
| mainSubpassDesc.pDepthStencilAttachment = &depthStencilRef; |
| } |
| |
| void populate_subpass_descs(const VulkanCaps& caps, |
| skia_private::TArray<VkSubpassDescription>& descs, |
| const VkAttachmentReference& colorRef, |
| const VkAttachmentReference& resolveRef, |
| const VkAttachmentReference& resolveLoadInputRef, |
| const VkAttachmentReference& depthStencilRef) { |
| // If loading MSAA from resolve, add the additional subpass to do so. |
| if (resolveLoadInputRef.attachment != VK_ATTACHMENT_UNUSED) { |
| VkSubpassDescription& loadSubpassDesc = descs.push_back(); |
| loadSubpassDesc = {}; |
| loadSubpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| loadSubpassDesc.inputAttachmentCount = 1; |
| loadSubpassDesc.pInputAttachments = &resolveLoadInputRef; |
| loadSubpassDesc.colorAttachmentCount = 1; |
| loadSubpassDesc.pColorAttachments = &colorRef; |
| } |
| |
| VkSubpassDescription& mainSubpassDesc = descs.push_back(); |
| mainSubpassDesc = {}; |
| populate_main_subpass_desc(caps, mainSubpassDesc, colorRef, resolveRef, depthStencilRef); |
| |
| if (caps.supportsRasterizationOrderColorAttachmentAccess()) { |
| for (VkSubpassDescription& desc : descs) { |
| desc.flags = VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_EXT; |
| } |
| } |
| } |
| |
| } // anonymous namespace |
| |
| sk_sp<VulkanRenderPass> VulkanRenderPass::Make(const VulkanSharedContext* context, |
| const RenderPassDesc& renderPassDesc) { |
| const bool needLoadMSAAFromResolveSubpass = |
| RenderPassDescWillLoadMSAAFromResolve(renderPassDesc); |
| const bool isMultisampledRenderToSingleSampled = |
| RenderPassDescWillImplicitlyLoadMSAA(renderPassDesc); |
| |
| VkResult result; |
| VkRenderPass renderPass = VK_NULL_HANDLE; |
| |
| // VK_EXT_multisampled_render_to_single_sampled usage requires vkCreateRenderPass2. |
| if (isMultisampledRenderToSingleSampled) { |
| // Set up attachment descriptions + references. Declare them before having a helper populate |
| // their values so we can reference them later during RP creation. |
| SkASSERT(renderPassDesc.fColorResolveAttachment.fFormat == TextureFormat::kUnsupported); |
| SkASSERT(!needLoadMSAAFromResolveSubpass); |
| |
| VkAttachmentDescription2 defaultAttachmentDescription = {}; |
| defaultAttachmentDescription.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; |
| |
| VkAttachmentReference2 defaultAttachmentReference = {}; |
| defaultAttachmentReference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; |
| defaultAttachmentReference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| |
| skia_private::TArray<VkAttachmentDescription2> attachmentDescs; |
| VkAttachmentReference2 colorRef = defaultAttachmentReference; |
| VkAttachmentReference2 resolveRef = defaultAttachmentReference; |
| VkAttachmentReference2 resolveLoadInputRef = defaultAttachmentReference; |
| VkAttachmentReference2 depthStencilRef = defaultAttachmentReference; |
| depthStencilRef.aspectMask = |
| GetVkImageAspectFlags(renderPassDesc.fDepthStencilAttachment.fFormat); |
| |
| populate_attachment_refs(renderPassDesc, |
| /*needLoadMSAAFromResolveSubpass=*/false, |
| attachmentDescs, |
| colorRef, |
| resolveRef, |
| resolveLoadInputRef, |
| depthStencilRef, |
| defaultAttachmentDescription); |
| SkASSERT(resolveLoadInputRef.attachment == VK_ATTACHMENT_UNUSED); |
| |
| // Assemble subpass information before creating the renderpass. Each renderpass has at least |
| // one subpass dependency (self-dependency for reading the dst texture). |
| VkSubpassDescription2 mainSubpassDesc = {}; |
| mainSubpassDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2; |
| populate_main_subpass_desc( |
| context->vulkanCaps(), mainSubpassDesc, colorRef, resolveRef, depthStencilRef); |
| |
| // Enable multisampled render to single-sampled with the render pass's sample count. |
| VkMultisampledRenderToSingleSampledInfoEXT msrtss = {}; |
| msrtss.sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT; |
| msrtss.multisampledRenderToSingleSampledEnable = VK_TRUE; |
| SampleCountToVkSampleCount(renderPassDesc.fSampleCount, &msrtss.rasterizationSamples); |
| AddToPNextChain(&mainSubpassDesc, &msrtss); |
| |
| // Depth/stencil resolve needs additional configuration (even though Graphite always |
| // discards depth/stencil). |
| SkASSERT(renderPassDesc.fDepthStencilAttachment.fStoreOp == StoreOp::kDiscard); |
| VkSubpassDescriptionDepthStencilResolve dsResolve = {}; |
| dsResolve.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE; |
| dsResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; |
| dsResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; |
| AddToPNextChain(&mainSubpassDesc, &dsResolve); |
| |
| VkSubpassDependency2 defaultSubpassDependency = {}; |
| defaultSubpassDependency.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; |
| |
| skia_private::STArray<1, VkSubpassDependency2> dependencies; |
| populate_subpass_dependencies(context, |
| dependencies, |
| /*needLoadMSAAFromResolveSubpass=*/false, |
| defaultSubpassDependency); |
| |
| // Create VkRenderPass |
| VkRenderPassCreateInfo2 renderPassInfo = {}; |
| renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2; |
| renderPassInfo.subpassCount = 1; |
| renderPassInfo.pSubpasses = &mainSubpassDesc; |
| renderPassInfo.dependencyCount = dependencies.size(); |
| renderPassInfo.pDependencies = dependencies.data(); |
| renderPassInfo.attachmentCount = attachmentDescs.size(); |
| renderPassInfo.pAttachments = attachmentDescs.data(); |
| |
| VULKAN_CALL_RESULT( |
| context, |
| result, |
| CreateRenderPass2(context->device(), &renderPassInfo, nullptr, &renderPass)); |
| } else { |
| // Same thing as above, but using Vulkan 1.0 render pass API. Practically every usable |
| // driver supports VK_KHR_create_renderpass2, but that is not yet a minimum requirement of |
| // Graphite. |
| skia_private::TArray<VkAttachmentDescription> attachmentDescs; |
| VkAttachmentReference colorRef = {}; |
| VkAttachmentReference resolveRef = {}; |
| VkAttachmentReference resolveLoadInputRef = {}; |
| VkAttachmentReference depthStencilRef = {}; |
| populate_attachment_refs(renderPassDesc, |
| needLoadMSAAFromResolveSubpass, |
| attachmentDescs, |
| colorRef, |
| resolveRef, |
| resolveLoadInputRef, |
| depthStencilRef, |
| /*defaultAttachmentDescription=*/{}); |
| |
| // If loading MSAA from resolve, an extra subpass and an additional dependency is needed. |
| skia_private::STArray<2, VkSubpassDescription> subpassDescs; |
| populate_subpass_descs(context->vulkanCaps(), |
| subpassDescs, |
| colorRef, |
| resolveRef, |
| resolveLoadInputRef, |
| depthStencilRef); |
| skia_private::STArray<2, VkSubpassDependency> dependencies; |
| populate_subpass_dependencies(context, |
| dependencies, |
| needLoadMSAAFromResolveSubpass, |
| /*defaultSubpassDependency=*/VkSubpassDependency{}); |
| |
| // Create VkRenderPass |
| VkRenderPassCreateInfo renderPassInfo = {}; |
| renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
| renderPassInfo.subpassCount = subpassDescs.size(); |
| renderPassInfo.pSubpasses = subpassDescs.begin(); |
| renderPassInfo.dependencyCount = dependencies.size(); |
| renderPassInfo.pDependencies = dependencies.data(); |
| renderPassInfo.attachmentCount = attachmentDescs.size(); |
| renderPassInfo.pAttachments = attachmentDescs.data(); |
| |
| VULKAN_CALL_RESULT( |
| context, |
| result, |
| CreateRenderPass(context->device(), &renderPassInfo, nullptr, &renderPass)); |
| } |
| |
| if (result != VK_SUCCESS) { |
| return nullptr; |
| } |
| VkExtent2D granularity; |
| VULKAN_CALL(context->interface(), GetRenderAreaGranularity(context->device(), |
| renderPass, |
| &granularity)); |
| return sk_sp<VulkanRenderPass>(new VulkanRenderPass(context, renderPass, granularity)); |
| } |
| |
| VulkanRenderPass::VulkanRenderPass(const VulkanSharedContext* context, |
| VkRenderPass renderPass, |
| VkExtent2D granularity) |
| : Resource(context, |
| Ownership::kOwned, |
| /*gpuMemorySize=*/0) |
| , fSharedContext(context) |
| , fRenderPass(renderPass) |
| , fGranularity(granularity) {} |
| |
| void VulkanRenderPass::freeGpuData() { |
| VULKAN_CALL(fSharedContext->interface(), |
| DestroyRenderPass(fSharedContext->device(), fRenderPass, nullptr)); |
| } |
| |
| } // namespace skgpu::graphite |