/*
 * Copyright © 2020 Valve Corporation
 * Copyright © 2025 Arm Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "vk_render_pass.h"

#include "vk_alloc.h"
#include "vk_command_buffer.h"
#include "vk_common_entrypoints.h"
#include "vk_device.h"
#include "vk_format.h"
#include "vk_framebuffer.h"
#include "vk_image.h"
#include "vk_util.h"
#include "vk_android.h"

#include "util/log.h"

static void
translate_references(VkAttachmentReference2 **reference_ptr,
                     uint32_t reference_count,
                     const VkAttachmentReference *reference,
                     const VkRenderPassCreateInfo *pass_info,
                     bool is_input_attachment)
{
   VkAttachmentReference2 *reference2 = *reference_ptr;
   *reference_ptr += reference_count;
   for (uint32_t i = 0; i < reference_count; i++) {
      reference2[i] = (VkAttachmentReference2) {
         .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
         .pNext = NULL,
         .attachment = reference[i].attachment,
         .layout = reference[i].layout,
      };

      if (is_input_attachment &&
          reference2[i].attachment != VK_ATTACHMENT_UNUSED) {
         assert(reference2[i].attachment < pass_info->attachmentCount);
         const VkAttachmentDescription *att =
            &pass_info->pAttachments[reference2[i].attachment];
         reference2[i].aspectMask = vk_format_aspects(att->format);
      }
   }
}

VKAPI_ATTR VkResult VKAPI_CALL
vk_common_CreateRenderPass(VkDevice _device,
                           const VkRenderPassCreateInfo *pCreateInfo,
                           const VkAllocationCallbacks *pAllocator,
                           VkRenderPass *pRenderPass)
{
   VK_FROM_HANDLE(vk_device, device, _device);

   uint32_t reference_count = 0;
   for (uint32_t i = 0; i < pCreateInfo->subpassCount; i++) {
      reference_count += pCreateInfo->pSubpasses[i].inputAttachmentCount;
      reference_count += pCreateInfo->pSubpasses[i].colorAttachmentCount;
      if (pCreateInfo->pSubpasses[i].pResolveAttachments)
         reference_count += pCreateInfo->pSubpasses[i].colorAttachmentCount;
      if (pCreateInfo->pSubpasses[i].pDepthStencilAttachment)
         reference_count += 1;
   }

   VK_MULTIALLOC(ma);
   VK_MULTIALLOC_DECL(&ma, VkRenderPassCreateInfo2, create_info, 1);
   VK_MULTIALLOC_DECL(&ma, VkSubpassDescription2, subpasses,
                           pCreateInfo->subpassCount);
   VK_MULTIALLOC_DECL(&ma, VkAttachmentDescription2, attachments,
                           pCreateInfo->attachmentCount);
   VK_MULTIALLOC_DECL(&ma, VkSubpassDependency2, dependencies,
                           pCreateInfo->dependencyCount);
   VK_MULTIALLOC_DECL(&ma, VkAttachmentReference2, references,
                           reference_count);
   if (!vk_multialloc_alloc2(&ma, &device->alloc, pAllocator,
                             VK_SYSTEM_ALLOCATION_SCOPE_COMMAND))
      return VK_ERROR_OUT_OF_HOST_MEMORY;

   VkAttachmentReference2 *reference_ptr = references;

   const VkRenderPassMultiviewCreateInfo *multiview_info = NULL;
   const VkRenderPassInputAttachmentAspectCreateInfo *aspect_info = NULL;
   vk_foreach_struct_const(ext, pCreateInfo->pNext) {
      switch (ext->sType) {
      case VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO:
         aspect_info = (const VkRenderPassInputAttachmentAspectCreateInfo *)ext;
         /* We don't care about this information */
         break;

      case VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO:
         multiview_info = (const VkRenderPassMultiviewCreateInfo*) ext;
         break;

      case VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT:
         /* pass this through to CreateRenderPass2 */
         break;

      default:
         mesa_logd("%s: ignored VkStructureType %u\n", __func__, ext->sType);
         break;
      }
   }

   for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
      attachments[i] = (VkAttachmentDescription2) {
         .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
         .pNext = NULL,
         .flags = pCreateInfo->pAttachments[i].flags,
         .format = pCreateInfo->pAttachments[i].format,
         .samples = pCreateInfo->pAttachments[i].samples,
         .loadOp = pCreateInfo->pAttachments[i].loadOp,
         .storeOp = pCreateInfo->pAttachments[i].storeOp,
         .stencilLoadOp = pCreateInfo->pAttachments[i].stencilLoadOp,
         .stencilStoreOp = pCreateInfo->pAttachments[i].stencilStoreOp,
         .initialLayout = pCreateInfo->pAttachments[i].initialLayout,
         .finalLayout = pCreateInfo->pAttachments[i].finalLayout,
      };
   }

   for (uint32_t i = 0; i < pCreateInfo->subpassCount; i++) {
      subpasses[i] = (VkSubpassDescription2) {
         .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
         .pNext = NULL,
         .flags = pCreateInfo->pSubpasses[i].flags,
         .pipelineBindPoint = pCreateInfo->pSubpasses[i].pipelineBindPoint,
         .viewMask = 0,
         .inputAttachmentCount = pCreateInfo->pSubpasses[i].inputAttachmentCount,
         .colorAttachmentCount = pCreateInfo->pSubpasses[i].colorAttachmentCount,
         .preserveAttachmentCount = pCreateInfo->pSubpasses[i].preserveAttachmentCount,
         .pPreserveAttachments = pCreateInfo->pSubpasses[i].pPreserveAttachments,
      };

      if (multiview_info && multiview_info->subpassCount) {
         assert(multiview_info->subpassCount == pCreateInfo->subpassCount);
         subpasses[i].viewMask = multiview_info->pViewMasks[i];
      }

      subpasses[i].pInputAttachments = reference_ptr;
      translate_references(&reference_ptr,
                           subpasses[i].inputAttachmentCount,
                           pCreateInfo->pSubpasses[i].pInputAttachments,
                           pCreateInfo, true);
      subpasses[i].pColorAttachments = reference_ptr;
      translate_references(&reference_ptr,
                           subpasses[i].colorAttachmentCount,
                           pCreateInfo->pSubpasses[i].pColorAttachments,
                           pCreateInfo, false);
      subpasses[i].pResolveAttachments = NULL;
      if (pCreateInfo->pSubpasses[i].pResolveAttachments) {
         subpasses[i].pResolveAttachments = reference_ptr;
         translate_references(&reference_ptr,
                              subpasses[i].colorAttachmentCount,
                              pCreateInfo->pSubpasses[i].pResolveAttachments,
                              pCreateInfo, false);
      }
      subpasses[i].pDepthStencilAttachment = NULL;
      if (pCreateInfo->pSubpasses[i].pDepthStencilAttachment) {
         subpasses[i].pDepthStencilAttachment = reference_ptr;
         translate_references(&reference_ptr, 1,
                              pCreateInfo->pSubpasses[i].pDepthStencilAttachment,
                              pCreateInfo, false);
      }
   }

   assert(reference_ptr == references + reference_count);

   if (aspect_info != NULL) {
      for (uint32_t i = 0; i < aspect_info->aspectReferenceCount; i++) {
         const VkInputAttachmentAspectReference *ref =
            &aspect_info->pAspectReferences[i];

         assert(ref->subpass < pCreateInfo->subpassCount);
         VkSubpassDescription2 *subpass = &subpasses[ref->subpass];

         assert(ref->inputAttachmentIndex < subpass->inputAttachmentCount);
         VkAttachmentReference2 *att = (VkAttachmentReference2 *)
            &subpass->pInputAttachments[ref->inputAttachmentIndex];

         att->aspectMask = ref->aspectMask;
      }
   }

   for (uint32_t i = 0; i < pCreateInfo->dependencyCount; i++) {
      dependencies[i] = (VkSubpassDependency2) {
         .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
         .pNext = NULL,
         .srcSubpass = pCreateInfo->pDependencies[i].srcSubpass,
         .dstSubpass = pCreateInfo->pDependencies[i].dstSubpass,
         .srcStageMask = pCreateInfo->pDependencies[i].srcStageMask,
         .dstStageMask = pCreateInfo->pDependencies[i].dstStageMask,
         .srcAccessMask = pCreateInfo->pDependencies[i].srcAccessMask,
         .dstAccessMask = pCreateInfo->pDependencies[i].dstAccessMask,
         .dependencyFlags = pCreateInfo->pDependencies[i].dependencyFlags,
         .viewOffset = 0,
      };

      if (multiview_info && multiview_info->dependencyCount) {
         assert(multiview_info->dependencyCount == pCreateInfo->dependencyCount);
         dependencies[i].viewOffset = multiview_info->pViewOffsets[i];
      }
   }

   *create_info = (VkRenderPassCreateInfo2) {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
      .pNext = pCreateInfo->pNext,
      .flags = pCreateInfo->flags,
      .attachmentCount = pCreateInfo->attachmentCount,
      .pAttachments = attachments,
      .subpassCount = pCreateInfo->subpassCount,
      .pSubpasses = subpasses,
      .dependencyCount = pCreateInfo->dependencyCount,
      .pDependencies = dependencies,
   };

   if (multiview_info && multiview_info->correlationMaskCount > 0) {
      create_info->correlatedViewMaskCount = multiview_info->correlationMaskCount;
      create_info->pCorrelatedViewMasks = multiview_info->pCorrelationMasks;
   }

   VkResult result =
      device->dispatch_table.CreateRenderPass2(_device, create_info,
                                               pAllocator, pRenderPass);

   vk_free2(&device->alloc, pAllocator, create_info);

   return result;
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdBeginRenderPass(VkCommandBuffer commandBuffer,
                             const VkRenderPassBeginInfo* pRenderPassBegin,
                             VkSubpassContents contents)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);

   VkSubpassBeginInfo info = {
      .sType = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO,
      .contents = contents,
   };

   cmd_buffer->base.device->dispatch_table.CmdBeginRenderPass2(
      commandBuffer, pRenderPassBegin, &info);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdEndRenderPass(VkCommandBuffer commandBuffer)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);

   VkSubpassEndInfo info = {
      .sType = VK_STRUCTURE_TYPE_SUBPASS_END_INFO,
   };

   cmd_buffer->base.device->dispatch_table.CmdEndRenderPass2(commandBuffer, &info);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdNextSubpass(VkCommandBuffer commandBuffer,
                         VkSubpassContents contents)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);

   VkSubpassBeginInfo begin_info = {
      .sType = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO,
      .contents = contents,
   };

   VkSubpassEndInfo end_info = {
      .sType = VK_STRUCTURE_TYPE_SUBPASS_END_INFO,
   };

   cmd_buffer->base.device->dispatch_table.CmdNextSubpass2(
      commandBuffer, &begin_info, &end_info);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdEndRendering(VkCommandBuffer commandBuffer)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);

   VkRenderingEndInfoEXT info = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_END_INFO_EXT,
   };

   cmd_buffer->base.device->dispatch_table.CmdEndRendering2EXT(commandBuffer, &info);
}

static unsigned
num_subpass_attachments2(const VkSubpassDescription2 *desc)
{
   bool has_depth_stencil_attachment =
      desc->pDepthStencilAttachment != NULL &&
      desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED;

   const VkSubpassDescriptionDepthStencilResolve *ds_resolve =
      vk_find_struct_const(desc->pNext,
                           SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE);

   bool has_depth_stencil_resolve_attachment =
      ds_resolve != NULL && ds_resolve->pDepthStencilResolveAttachment &&
      ds_resolve->pDepthStencilResolveAttachment->attachment != VK_ATTACHMENT_UNUSED;

   const VkFragmentShadingRateAttachmentInfoKHR *fsr_att_info =
      vk_find_struct_const(desc->pNext,
                           FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR);

   bool has_fragment_shading_rate_attachment =
      fsr_att_info && fsr_att_info->pFragmentShadingRateAttachment &&
      fsr_att_info->pFragmentShadingRateAttachment->attachment != VK_ATTACHMENT_UNUSED;

   return desc->inputAttachmentCount +
          desc->colorAttachmentCount +
          (desc->pResolveAttachments ? desc->colorAttachmentCount : 0) +
          has_depth_stencil_attachment +
          has_depth_stencil_resolve_attachment +
          has_fragment_shading_rate_attachment;
}

static void
vk_render_pass_attachment_init(struct vk_render_pass_attachment *att,
                               const VkAttachmentDescription2 *desc)
{
   *att = (struct vk_render_pass_attachment) {
      .format                 = desc->format,
      .flags                  = desc->flags,
      .aspects                = vk_format_aspects(desc->format),
      .samples                = desc->samples,
      .view_mask              = 0,
      .load_op                = desc->loadOp,
      .store_op               = desc->storeOp,
      .stencil_load_op        = desc->stencilLoadOp,
      .stencil_store_op       = desc->stencilStoreOp,
      .initial_layout         = desc->initialLayout,
      .final_layout           = desc->finalLayout,
      .initial_stencil_layout = vk_att_desc_stencil_layout(desc, false),
      .final_stencil_layout   = vk_att_desc_stencil_layout(desc, true),
      .has_external_format =
         vk_android_rp_attachment_has_external_format(desc),
   };

   /* We require separate stencil layouts */
   if (att->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {
      att->initial_layout = vk_image_layout_depth_only(att->initial_layout);
      att->final_layout = vk_image_layout_depth_only(att->final_layout);
   } else if (att->aspects == VK_IMAGE_ASPECT_STENCIL_BIT) {
      att->initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
      att->final_layout = VK_IMAGE_LAYOUT_UNDEFINED;
   }

   if (att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {
      att->initial_stencil_layout =
         vk_image_layout_stencil_only(att->initial_stencil_layout);
      att->final_stencil_layout =
         vk_image_layout_stencil_only(att->final_stencil_layout);
   } else {
      assert(att->initial_stencil_layout == VK_IMAGE_LAYOUT_UNDEFINED);
      assert(att->final_stencil_layout == VK_IMAGE_LAYOUT_UNDEFINED);
   }
}

static void
vk_subpass_attachment_init(struct vk_subpass_attachment *att,
                           struct vk_render_pass *pass,
                           uint32_t subpass_idx,
                           const VkAttachmentReference2 *ref,
                           const VkAttachmentDescription2 *attachments,
                           VkImageUsageFlagBits usage)
{
   if (ref->attachment >= pass->attachment_count) {
      assert(ref->attachment == VK_ATTACHMENT_UNUSED);
      *att = (struct vk_subpass_attachment) {
         .attachment = VK_ATTACHMENT_UNUSED,
      };
      return;
   }

   struct vk_render_pass_attachment *pass_att =
      &pass->attachments[ref->attachment];

   *att = (struct vk_subpass_attachment) {
      .attachment =     ref->attachment,
      .aspects =        vk_format_aspects(pass_att->format),
      .usage =          usage,
      .layout =         ref->layout,
      .stencil_layout = vk_att_ref_stencil_layout(ref, attachments),
   };

   /* We require separate stencil layouts */
   if (att->aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
      att->layout = vk_image_layout_depth_only(att->layout);
   else if (att->aspects == VK_IMAGE_ASPECT_STENCIL_BIT)
      att->layout = VK_IMAGE_LAYOUT_UNDEFINED;

   if (att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
      att->stencil_layout = vk_image_layout_stencil_only(att->stencil_layout);
   else
      assert(att->stencil_layout == VK_IMAGE_LAYOUT_UNDEFINED);

   switch (usage) {
   case VK_IMAGE_USAGE_TRANSFER_DST_BIT:
      break; /* No special aspect requirements */

   case VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT:
      /* From the Vulkan 1.2.184 spec:
       *
       *    "aspectMask is ignored when this structure is used to describe
       *    anything other than an input attachment reference."
       */
      assert(!(ref->aspectMask & ~att->aspects));
      att->aspects = ref->aspectMask;
      break;

   case VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT:
   case VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR:
      assert(att->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
      break;

   case VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT:
      assert(!(att->aspects & ~(VK_IMAGE_ASPECT_DEPTH_BIT |
                                VK_IMAGE_ASPECT_STENCIL_BIT)));
      break;

   default:
      UNREACHABLE("Invalid subpass attachment usage");
   }
}

static void
vk_subpass_attachment_link_resolve(struct vk_subpass_attachment *att,
                                   struct vk_subpass_attachment *resolve,
                                   const VkRenderPassCreateInfo2 *info)
{
   if (resolve->attachment == VK_ATTACHMENT_UNUSED)
      return;

   assert(att->attachment != VK_ATTACHMENT_UNUSED);
   att->resolve = resolve;
}

VKAPI_ATTR VkResult VKAPI_CALL
vk_common_CreateRenderPass2(VkDevice _device,
                            const VkRenderPassCreateInfo2 *pCreateInfo,
                            const VkAllocationCallbacks *pAllocator,
                            VkRenderPass *pRenderPass)
{
   VK_FROM_HANDLE(vk_device, device, _device);

   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2);

   VK_MULTIALLOC(ma);
   VK_MULTIALLOC_DECL(&ma, struct vk_render_pass, pass, 1);
   VK_MULTIALLOC_DECL(&ma, struct vk_render_pass_attachment, attachments,
                           pCreateInfo->attachmentCount);
   VK_MULTIALLOC_DECL(&ma, struct vk_subpass, subpasses,
                           pCreateInfo->subpassCount);
   VK_MULTIALLOC_DECL(&ma, struct vk_subpass_dependency, dependencies,
                           pCreateInfo->dependencyCount);

   uint32_t subpass_attachment_count = 0;
   uint32_t subpass_color_attachment_count = 0;
   for (uint32_t i = 0; i < pCreateInfo->subpassCount; i++) {
      subpass_attachment_count +=
         num_subpass_attachments2(&pCreateInfo->pSubpasses[i]);
      subpass_color_attachment_count +=
         pCreateInfo->pSubpasses[i].colorAttachmentCount;
   }
   VK_MULTIALLOC_DECL(&ma, struct vk_subpass_attachment, subpass_attachments,
                      subpass_attachment_count);
   VK_MULTIALLOC_DECL(&ma, VkFormat, subpass_color_formats,
                      subpass_color_attachment_count);
   VK_MULTIALLOC_DECL(&ma, VkSampleCountFlagBits, subpass_color_samples,
                      subpass_color_attachment_count);

   if (!vk_object_multizalloc(device, &ma, pAllocator,
                              VK_OBJECT_TYPE_RENDER_PASS))
      return VK_ERROR_OUT_OF_HOST_MEMORY;

   pass->attachment_count = pCreateInfo->attachmentCount;
   pass->attachments = attachments;
   pass->subpass_count = pCreateInfo->subpassCount;
   pass->subpasses = subpasses;
   pass->dependency_count = pCreateInfo->dependencyCount;
   pass->dependencies = dependencies;

   for (uint32_t a = 0; a < pCreateInfo->attachmentCount; a++) {
      vk_render_pass_attachment_init(&pass->attachments[a],
                                     &pCreateInfo->pAttachments[a]);
   }

   struct vk_subpass_attachment *next_subpass_attachment = subpass_attachments;
   VkFormat *next_subpass_color_format = subpass_color_formats;
   VkSampleCountFlagBits *next_subpass_color_samples = subpass_color_samples;
   for (uint32_t s = 0; s < pCreateInfo->subpassCount; s++) {
      const VkSubpassDescription2 *desc = &pCreateInfo->pSubpasses[s];
      struct vk_subpass *subpass = &pass->subpasses[s];
      const VkMultisampledRenderToSingleSampledInfoEXT *mrtss =
            vk_find_struct_const(desc->pNext, MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT);
      if (mrtss && !mrtss->multisampledRenderToSingleSampledEnable)
         mrtss = NULL;

      subpass->attachment_count = num_subpass_attachments2(desc);
      subpass->attachments = next_subpass_attachment;

      if (device->enabled_features.legacyDithering) {
         subpass->legacy_dithering_enabled =
            desc->flags & VK_SUBPASS_DESCRIPTION_ENABLE_LEGACY_DITHERING_BIT_EXT;
      }

      /* From the Vulkan 1.3.204 spec:
       *
       *    VUID-VkRenderPassCreateInfo2-viewMask-03058
       *
       *    "The VkSubpassDescription2::viewMask member of all elements of
       *    pSubpasses must either all be 0, or all not be 0"
       */
      if (desc->viewMask)
         pass->is_multiview = true;
      assert(pass->is_multiview == (desc->viewMask != 0));

      /* For all view masks in the vk_render_pass data structure, we use a
       * mask of 1 for non-multiview instead of a mask of 0.
       */
      subpass->view_mask = desc->viewMask ? desc->viewMask : 1;
      pass->view_mask |= subpass->view_mask;

      subpass->input_count = desc->inputAttachmentCount;
      if (desc->inputAttachmentCount > 0) {
         subpass->input_attachments = next_subpass_attachment;
         next_subpass_attachment += desc->inputAttachmentCount;

         for (uint32_t a = 0; a < desc->inputAttachmentCount; a++) {
            vk_subpass_attachment_init(&subpass->input_attachments[a],
                                       pass, s,
                                       &desc->pInputAttachments[a],
                                       pCreateInfo->pAttachments,
                                       VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
         }
      }

      subpass->color_count = desc->colorAttachmentCount;
      if (desc->colorAttachmentCount > 0) {
         subpass->color_attachments = next_subpass_attachment;
         next_subpass_attachment += desc->colorAttachmentCount;

         for (uint32_t a = 0; a < desc->colorAttachmentCount; a++) {
            vk_subpass_attachment_init(&subpass->color_attachments[a],
                                       pass, s,
                                       &desc->pColorAttachments[a],
                                       pCreateInfo->pAttachments,
                                       VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
         }
      }

      if (desc->pResolveAttachments) {
         subpass->color_resolve_count = desc->colorAttachmentCount;
         subpass->color_resolve_attachments = next_subpass_attachment;
         next_subpass_attachment += desc->colorAttachmentCount;

         for (uint32_t a = 0; a < desc->colorAttachmentCount; a++) {
            vk_subpass_attachment_init(&subpass->color_resolve_attachments[a],
                                       pass, s,
                                       &desc->pResolveAttachments[a],
                                       pCreateInfo->pAttachments,
                                       VK_IMAGE_USAGE_TRANSFER_DST_BIT);
            vk_subpass_attachment_link_resolve(&subpass->color_attachments[a],
                                               &subpass->color_resolve_attachments[a],
                                               pCreateInfo);
         }
      }

      if (desc->pDepthStencilAttachment &&
          desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) {
         subpass->depth_stencil_attachment = next_subpass_attachment++;

         vk_subpass_attachment_init(subpass->depth_stencil_attachment,
                                    pass, s,
                                    desc->pDepthStencilAttachment,
                                    pCreateInfo->pAttachments,
                                    VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
      }

      const VkSubpassDescriptionDepthStencilResolve *ds_resolve =
         vk_find_struct_const(desc->pNext,
                              SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE);

      if (ds_resolve) {
         if (ds_resolve->pDepthStencilResolveAttachment &&
             ds_resolve->pDepthStencilResolveAttachment->attachment != VK_ATTACHMENT_UNUSED) {
            subpass->depth_stencil_resolve_attachment = next_subpass_attachment++;

            vk_subpass_attachment_init(subpass->depth_stencil_resolve_attachment,
                                       pass, s,
                                       ds_resolve->pDepthStencilResolveAttachment,
                                       pCreateInfo->pAttachments,
                                       VK_IMAGE_USAGE_TRANSFER_DST_BIT);
            vk_subpass_attachment_link_resolve(subpass->depth_stencil_attachment,
                                               subpass->depth_stencil_resolve_attachment,
                                               pCreateInfo);
         }
         if (subpass->depth_stencil_resolve_attachment || mrtss) {
            /* From the Vulkan 1.3.204 spec:
             *
             *    VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03178
             *
             *    "If pDepthStencilResolveAttachment is not NULL and does not
             *    have the value VK_ATTACHMENT_UNUSED, depthResolveMode and
             *    stencilResolveMode must not both be VK_RESOLVE_MODE_NONE"
             */
            assert(ds_resolve->depthResolveMode != VK_RESOLVE_MODE_NONE ||
                   ds_resolve->stencilResolveMode != VK_RESOLVE_MODE_NONE);

            subpass->depth_resolve_mode = ds_resolve->depthResolveMode;
            subpass->stencil_resolve_mode = ds_resolve->stencilResolveMode;
         }
      }

      const VkFragmentShadingRateAttachmentInfoKHR *fsr_att_info =
         vk_find_struct_const(desc->pNext,
                              FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR);

      if (fsr_att_info && fsr_att_info->pFragmentShadingRateAttachment &&
          fsr_att_info->pFragmentShadingRateAttachment->attachment != VK_ATTACHMENT_UNUSED) {
         subpass->fragment_shading_rate_attachment = next_subpass_attachment++;
         vk_subpass_attachment_init(subpass->fragment_shading_rate_attachment,
                                    pass, s,
                                    fsr_att_info->pFragmentShadingRateAttachment,
                                    pCreateInfo->pAttachments,
                                    VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR);
         subpass->fragment_shading_rate_attachment_texel_size =
            fsr_att_info->shadingRateAttachmentTexelSize;
         subpass->pipeline_flags |=
            VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
      }

      /* Figure out any self-dependencies */
      assert(desc->colorAttachmentCount <= 32);
      for (uint32_t a = 0; a < desc->inputAttachmentCount; a++) {
         if (desc->pInputAttachments[a].attachment == VK_ATTACHMENT_UNUSED)
            continue;

         for (uint32_t c = 0; c < desc->colorAttachmentCount; c++) {
            if (desc->pColorAttachments[c].attachment ==
                desc->pInputAttachments[a].attachment) {
               subpass->input_attachments[a].layout =
                  VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;
               subpass->color_attachments[c].layout =
                  VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;
               subpass->pipeline_flags |=
                  VK_PIPELINE_CREATE_COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;
            }
         }

         if (desc->pDepthStencilAttachment != NULL &&
             desc->pDepthStencilAttachment->attachment ==
                desc->pInputAttachments[a].attachment) {
            VkImageAspectFlags aspects =
               subpass->input_attachments[a].aspects;
            if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {
               subpass->input_attachments[a].layout =
                  VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;
               subpass->depth_stencil_attachment->layout =
                  VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;
               subpass->pipeline_flags |=
                  VK_PIPELINE_CREATE_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;
            }
            if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {
               subpass->input_attachments[a].stencil_layout =
                  VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;
               subpass->depth_stencil_attachment->stencil_layout =
                  VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;
               subpass->pipeline_flags |=
                  VK_PIPELINE_CREATE_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;
            }
         }
      }

      VkFormat *color_formats = NULL;
      VkSampleCountFlagBits *color_samples = NULL;
      VkSampleCountFlagBits samples = 0;
      if (desc->colorAttachmentCount > 0) {
         color_formats = next_subpass_color_format;
         color_samples = next_subpass_color_samples;
         for (uint32_t a = 0; a < desc->colorAttachmentCount; a++) {
            const VkAttachmentReference2 *ref = &desc->pColorAttachments[a];
            if (ref->attachment >= pCreateInfo->attachmentCount) {
               color_formats[a] = VK_FORMAT_UNDEFINED;
               color_samples[a] = VK_SAMPLE_COUNT_1_BIT;
            } else {
               const VkAttachmentDescription2 *att =
                  &pCreateInfo->pAttachments[ref->attachment];

               color_formats[a] = att->format;
               color_samples[a] = att->samples;

               samples |= att->samples;
            }
         }
         next_subpass_color_format += desc->colorAttachmentCount;
         next_subpass_color_samples += desc->colorAttachmentCount;
      }

      subpass->ial.depth = VK_ATTACHMENT_UNUSED;
      subpass->ial.stencil = VK_ATTACHMENT_UNUSED;

      VkFormat depth_format = VK_FORMAT_UNDEFINED;
      VkFormat stencil_format = VK_FORMAT_UNDEFINED;
      VkSampleCountFlagBits depth_stencil_samples = VK_SAMPLE_COUNT_1_BIT;
      if (desc->pDepthStencilAttachment != NULL) {
         const VkAttachmentReference2 *ref = desc->pDepthStencilAttachment;
         if (ref->attachment < pCreateInfo->attachmentCount) {
            const VkAttachmentDescription2 *att =
               &pCreateInfo->pAttachments[ref->attachment];
            uint32_t ia_idx = VK_ATTACHMENT_UNUSED;

            for (uint32_t j = 0; j < subpass->input_count; j++) {
               if (subpass->input_attachments[j].attachment == ref->attachment)
                  ia_idx = j;
            }

            if (vk_format_has_depth(att->format)) {
               depth_format = att->format;
               subpass->ial.depth = ia_idx;
            }

            if (vk_format_has_stencil(att->format)) {
               stencil_format = att->format;
               subpass->ial.stencil = ia_idx;
            }

            depth_stencil_samples = att->samples;

            samples |= att->samples;
         }
      }

      subpass->sample_count_info_amd = (VkAttachmentSampleCountInfoAMD) {
         .sType = VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD,
         .pNext = NULL,
         .colorAttachmentCount = desc->colorAttachmentCount,
         .pColorAttachmentSamples = color_samples,
         .depthStencilAttachmentSamples = depth_stencil_samples,
      };

      subpass->ial.info = (VkRenderingInputAttachmentIndexInfo) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_INPUT_ATTACHMENT_INDEX_INFO,
         .pNext = &subpass->sample_count_info_amd,
         .colorAttachmentCount = subpass->color_count,
         .pColorAttachmentInputIndices = subpass->ial.colors,
         /* From the Vulkan 1.3.204 spec:
          *
          *    VUID-vkCmdDraw-OpTypeImage-07468
          *
          *    "If any shader executed by this pipeline accesses an OpTypeImage
          *     variable with a Dim operand of SubpassData, it must be decorated
          *     with an InputAttachmentIndex that corresponds to a valid input
          *     attachment in the current subpass."
          *
          * So we don't have to worry about the missing InputAttachmentIndex
          * decoration (AKA NO_INDEX) here, the depth/stencil attachment is
          * either not used as an input attachment, or it has an explicit
          * index.
          */
         .pDepthInputAttachmentIndex = &subpass->ial.depth,
         .pStencilInputAttachmentIndex = &subpass->ial.stencil,
      };

      /* Build the color -> input attachment map. */
      for (uint32_t i = 0; i < subpass->color_count; i++) {
         subpass->ial.colors[i] = VK_ATTACHMENT_UNUSED;

         if (subpass->color_attachments[i].attachment == VK_ATTACHMENT_UNUSED)
            continue;

         for (uint32_t j = 0; j < subpass->input_count; j++) {
            if (subpass->input_attachments[j].attachment ==
                   subpass->color_attachments[i].attachment) {
               subpass->ial.colors[i] = j;
            }
         }
      }

      subpass->pipeline_info = (VkPipelineRenderingCreateInfo) {
         .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
         .pNext = &subpass->ial.info,
         .viewMask = desc->viewMask,
         .colorAttachmentCount = desc->colorAttachmentCount,
         .pColorAttachmentFormats = color_formats,
         .depthAttachmentFormat = depth_format,
         .stencilAttachmentFormat = stencil_format,
      };

      subpass->inheritance_info = (VkCommandBufferInheritanceRenderingInfo) {
         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO,
         .pNext = &subpass->ial.info,
         /* If we're inheriting, the contents are clearly in secondaries */
         .flags = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT,
         .viewMask = desc->viewMask,
         .colorAttachmentCount = desc->colorAttachmentCount,
         .pColorAttachmentFormats = color_formats,
         .depthAttachmentFormat = depth_format,
         .stencilAttachmentFormat = stencil_format,
         .rasterizationSamples = samples,
      };

      if (mrtss) {
         assert(mrtss->multisampledRenderToSingleSampledEnable);
         subpass->mrtss = (VkMultisampledRenderToSingleSampledInfoEXT) {
            .sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT,
            .multisampledRenderToSingleSampledEnable = VK_TRUE,
            .rasterizationSamples = mrtss->rasterizationSamples,
         };
      }
   }
   assert(next_subpass_attachment ==
          subpass_attachments + subpass_attachment_count);
   assert(next_subpass_color_format ==
          subpass_color_formats + subpass_color_attachment_count);
   assert(next_subpass_color_samples ==
          subpass_color_samples + subpass_color_attachment_count);

   /* Walk forwards over the subpasses to compute first_subpass masks for all
    * attachments.
    */
   for (uint32_t s = 0; s < pCreateInfo->subpassCount; s++) {
      struct vk_subpass *subpass = &pass->subpasses[s];

      /* First, compute first_subpass for all the attachments */
      for (uint32_t a = 0; a < subpass->attachment_count; a++) {
         struct vk_subpass_attachment *att = &subpass->attachments[a];
         if (att->attachment == VK_ATTACHMENT_UNUSED)
            continue;

         assert(att->attachment < pass->attachment_count);
         const struct vk_render_pass_attachment *pass_att =
            &pass->attachments[att->attachment];

         att->first_subpass = subpass->view_mask & ~pass_att->view_mask;
      }

      /* Then compute pass_att->view_mask.  We do the two separately so that
       * we end up with the right first_subpass even if the same attachment is
       * used twice within a subpass.
       */
      for (uint32_t a = 0; a < subpass->attachment_count; a++) {
         const struct vk_subpass_attachment *att = &subpass->attachments[a];
         if (att->attachment == VK_ATTACHMENT_UNUSED)
            continue;

         assert(att->attachment < pass->attachment_count);
         struct vk_render_pass_attachment *pass_att =
            &pass->attachments[att->attachment];

         pass_att->view_mask |= subpass->view_mask;
      }
   }

   /* Zero the view-masks, as we'll need to fill them again when looking for
    * last_subpass
    */
   for (uint32_t a = 0; a < pass->attachment_count; a++) {
      struct vk_render_pass_attachment *pass_att =
         &pass->attachments[a];
      pass_att->view_mask = 0;
   }

   /* Walk backwards over the subpasses to compute view masks and
    * last_subpass masks for all attachments.
    */
   for (uint32_t s = 0; s < pCreateInfo->subpassCount; s++) {
      struct vk_subpass *subpass =
         &pass->subpasses[(pCreateInfo->subpassCount - 1) - s];

      /* First, compute last_subpass for all the attachments */
      for (uint32_t a = 0; a < subpass->attachment_count; a++) {
         struct vk_subpass_attachment *att = &subpass->attachments[a];
         if (att->attachment == VK_ATTACHMENT_UNUSED)
            continue;

         assert(att->attachment < pass->attachment_count);
         const struct vk_render_pass_attachment *pass_att =
            &pass->attachments[att->attachment];

         att->last_subpass = subpass->view_mask & ~pass_att->view_mask;
      }

      /* Then compute pass_att->view_mask.  We do the two separately so that
       * we end up with the right last_subpass even if the same attachment is
       * used twice within a subpass.
       */
      for (uint32_t a = 0; a < subpass->attachment_count; a++) {
         const struct vk_subpass_attachment *att = &subpass->attachments[a];
         if (att->attachment == VK_ATTACHMENT_UNUSED)
            continue;

         assert(att->attachment < pass->attachment_count);
         struct vk_render_pass_attachment *pass_att =
            &pass->attachments[att->attachment];

         pass_att->view_mask |= subpass->view_mask;
      }
   }

   pass->dependency_count = pCreateInfo->dependencyCount;
   for (uint32_t d = 0; d < pCreateInfo->dependencyCount; d++) {
      const VkSubpassDependency2 *dep = &pCreateInfo->pDependencies[d];

      pass->dependencies[d] = (struct vk_subpass_dependency) {
         .flags = dep->dependencyFlags,
         .src_subpass = dep->srcSubpass,
         .dst_subpass = dep->dstSubpass,
         .src_stage_mask = (VkPipelineStageFlags2)dep->srcStageMask,
         .dst_stage_mask = (VkPipelineStageFlags2)dep->dstStageMask,
         .src_access_mask = (VkAccessFlags2)dep->srcAccessMask,
         .dst_access_mask = (VkAccessFlags2)dep->dstAccessMask,
         .view_offset = dep->viewOffset,
      };

      /* From the Vulkan 1.3.204 spec:
       *
       *    "If a VkMemoryBarrier2 is included in the pNext chain,
       *    srcStageMask, dstStageMask, srcAccessMask, and dstAccessMask
       *    parameters are ignored. The synchronization and access scopes
       *    instead are defined by the parameters of VkMemoryBarrier2."
       */
      const VkMemoryBarrier2 *barrier =
         vk_find_struct_const(dep->pNext, MEMORY_BARRIER_2);
      if (barrier != NULL) {
         pass->dependencies[d].src_stage_mask = barrier->srcStageMask;
         pass->dependencies[d].dst_stage_mask = barrier->dstStageMask;
         pass->dependencies[d].src_access_mask = barrier->srcAccessMask;
         pass->dependencies[d].dst_access_mask = barrier->dstAccessMask;
      }
   }

   const VkRenderPassFragmentDensityMapCreateInfoEXT *fdm_info =
      vk_find_struct_const(pCreateInfo->pNext,
                           RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT);
   if (fdm_info) {
      pass->fragment_density_map = fdm_info->fragmentDensityMapAttachment;
   } else {
      pass->fragment_density_map.attachment = VK_ATTACHMENT_UNUSED;
      pass->fragment_density_map.layout = VK_IMAGE_LAYOUT_UNDEFINED;
   }

   *pRenderPass = vk_render_pass_to_handle(pass);

   return VK_SUCCESS;
}

const VkPipelineRenderingCreateInfo *
vk_get_pipeline_rendering_create_info(const VkGraphicsPipelineCreateInfo *info)
{
   VK_FROM_HANDLE(vk_render_pass, render_pass, info->renderPass);
   if (render_pass != NULL) {
      assert(info->subpass < render_pass->subpass_count);
      return &render_pass->subpasses[info->subpass].pipeline_info;
   }

   return vk_find_struct_const(info->pNext, PIPELINE_RENDERING_CREATE_INFO);
}

const VkRenderingInputAttachmentIndexInfo *
vk_get_pipeline_rendering_ial_info(const VkGraphicsPipelineCreateInfo *info)
{
   VK_FROM_HANDLE(vk_render_pass, render_pass, info->renderPass);
   if (render_pass != NULL) {
      assert(info->subpass < render_pass->subpass_count);
      return &render_pass->subpasses[info->subpass].ial.info;
   }

   return vk_find_struct_const(info->pNext,
                               RENDERING_INPUT_ATTACHMENT_INDEX_INFO_KHR);
}

VkPipelineCreateFlags2KHR
vk_get_pipeline_rendering_flags(const VkGraphicsPipelineCreateInfo *info)
{
   VkPipelineCreateFlags2KHR rendering_flags = 0;

   VK_FROM_HANDLE(vk_render_pass, render_pass, info->renderPass);
   if (render_pass != NULL) {
      rendering_flags |= render_pass->subpasses[info->subpass].pipeline_flags;
      if (render_pass->fragment_density_map.attachment != VK_ATTACHMENT_UNUSED)
         rendering_flags |=
            VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT;
   }

   return rendering_flags;
}

const VkAttachmentSampleCountInfoAMD *
vk_get_pipeline_sample_count_info_amd(const VkGraphicsPipelineCreateInfo *info)
{
   VK_FROM_HANDLE(vk_render_pass, render_pass, info->renderPass);
   if (render_pass != NULL) {
      assert(info->subpass < render_pass->subpass_count);
      return &render_pass->subpasses[info->subpass].sample_count_info_amd;
   }

   return vk_find_struct_const(info->pNext, ATTACHMENT_SAMPLE_COUNT_INFO_AMD);
}

const VkCommandBufferInheritanceRenderingInfo *
vk_get_command_buffer_inheritance_rendering_info(
   VkCommandBufferLevel level,
   const VkCommandBufferBeginInfo *pBeginInfo)
{
   /* From the Vulkan 1.3.204 spec:
    *
    *    "VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT specifies that a
    *    secondary command buffer is considered to be entirely inside a render
    *    pass. If this is a primary command buffer, then this bit is ignored."
    *
    * Since we're only concerned with the continue case here, we can ignore
    * any primary command buffers.
    */
   if (level == VK_COMMAND_BUFFER_LEVEL_PRIMARY)
      return NULL;

   if (!(pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT))
      return NULL;

   const VkCommandBufferInheritanceInfo *inheritance =
      pBeginInfo->pInheritanceInfo;

   /* From the Vulkan 1.3.204 spec:
    *
    *    "If VkCommandBufferInheritanceInfo::renderPass is not VK_NULL_HANDLE,
    *    or VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT is not specified
    *    in VkCommandBufferBeginInfo::flags, parameters of this structure are
    *    ignored."
    *
    * If we have a render pass that wins, even if a
    * VkCommandBufferInheritanceRenderingInfo struct is included in the pNext
    * chain.
    */
   VK_FROM_HANDLE(vk_render_pass, render_pass, inheritance->renderPass);
   if (render_pass != NULL) {
      assert(inheritance->subpass < render_pass->subpass_count);
      return &render_pass->subpasses[inheritance->subpass].inheritance_info;
   }

   return vk_find_struct_const(inheritance->pNext,
                               COMMAND_BUFFER_INHERITANCE_RENDERING_INFO);
}

VkRenderingAttachmentFlagsKHR
vk_get_rendering_attachment_flags(const VkRenderingAttachmentInfo *att)
{
   const VkRenderingAttachmentFlagsInfoKHR *flags_info =
      vk_find_struct_const(att->pNext, RENDERING_ATTACHMENT_FLAGS_INFO_KHR);

   return flags_info != NULL ? flags_info->flags : 0;
}

static VkRenderingAttachmentFlagBitsKHR
vk_attachment_description_flags_to_rendering_flags(VkAttachmentDescriptionFlags flags)
{
   VkRenderingAttachmentFlagBitsKHR ret = 0;
   if (flags & VK_ATTACHMENT_DESCRIPTION_RESOLVE_SKIP_TRANSFER_FUNCTION_BIT_KHR)
      ret |= VK_RENDERING_ATTACHMENT_RESOLVE_SKIP_TRANSFER_FUNCTION_BIT_KHR;
   if (flags & VK_ATTACHMENT_DESCRIPTION_RESOLVE_ENABLE_TRANSFER_FUNCTION_BIT_KHR)
      ret |= VK_RENDERING_ATTACHMENT_RESOLVE_ENABLE_TRANSFER_FUNCTION_BIT_KHR;
   return ret;
}

const VkRenderingInfo *
vk_get_command_buffer_inheritance_as_rendering_resume(
   VkCommandBufferLevel level,
   const VkCommandBufferBeginInfo *pBeginInfo,
   void *stack_data)
{
   struct vk_gcbiarr_data *data = stack_data;

   /* From the Vulkan 1.3.204 spec:
    *
    *    "VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT specifies that a
    *    secondary command buffer is considered to be entirely inside a render
    *    pass. If this is a primary command buffer, then this bit is ignored."
    *
    * Since we're only concerned with the continue case here, we can ignore
    * any primary command buffers.
    */
   if (level == VK_COMMAND_BUFFER_LEVEL_PRIMARY)
      return NULL;

   if (!(pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT))
      return NULL;

   const VkCommandBufferInheritanceInfo *inheritance =
      pBeginInfo->pInheritanceInfo;

   VK_FROM_HANDLE(vk_render_pass, pass, inheritance->renderPass);
   if (pass == NULL)
      return NULL;

   assert(inheritance->subpass < pass->subpass_count);
   const struct vk_subpass *subpass = &pass->subpasses[inheritance->subpass];

   VK_FROM_HANDLE(vk_framebuffer, fb, inheritance->framebuffer);
   if (fb == NULL || (fb->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT))
      return NULL;

   data->rendering = (VkRenderingInfo) {
      .sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
      .flags = VK_RENDERING_RESUMING_BIT,
      .renderArea = {
         .offset = { 0, 0 },
         .extent = { fb->width, fb->height },
      },
      .layerCount = fb->layers,
      .viewMask = pass->is_multiview ? subpass->view_mask : 0,
   };

   VkRenderingAttachmentInfo *attachments = data->attachments;
   VkRenderingAttachmentFlagsInfoKHR *attachments_flags = (VkRenderingAttachmentFlagsInfoKHR *)
      (data->attachments + subpass->color_count +
       2 * (subpass->depth_stencil_attachment != NULL));

   for (unsigned i = 0; i < subpass->color_count; i++) {
      const struct vk_subpass_attachment *sp_att =
         &subpass->color_attachments[i];
      if (sp_att->attachment == VK_ATTACHMENT_UNUSED) {
         attachments[i] = (VkRenderingAttachmentInfo) {
            .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
            .imageView = VK_NULL_HANDLE,
         };
         continue;
      }

      assert(sp_att->attachment < pass->attachment_count);

      attachments_flags[i] = (VkRenderingAttachmentFlagsInfoKHR) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR,
         .flags = vk_attachment_description_flags_to_rendering_flags(
            pass->attachments[sp_att->attachment].flags),
      };

      attachments[i] = (VkRenderingAttachmentInfo) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
         .pNext = &attachments_flags[i],
         .imageView = fb->attachments[sp_att->attachment],
         .imageLayout = sp_att->layout,
         .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
         .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
      };
   }
   data->rendering.colorAttachmentCount = subpass->color_count;
   data->rendering.pColorAttachments = attachments;
   attachments += subpass->color_count;

   if (subpass->depth_stencil_attachment) {
      const struct vk_subpass_attachment *sp_att =
         subpass->depth_stencil_attachment;
      assert(sp_att->attachment < pass->attachment_count);

      VK_FROM_HANDLE(vk_image_view, iview, fb->attachments[sp_att->attachment]);
      if (iview->image->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {
         *attachments = (VkRenderingAttachmentInfo) {
            .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
            .imageView = vk_image_view_to_handle(iview),
            .imageLayout = sp_att->layout,
            .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
         };
         data->rendering.pDepthAttachment = attachments++;
      }

      if (iview->image->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {
         *attachments = (VkRenderingAttachmentInfo) {
            .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
            .imageView = vk_image_view_to_handle(iview),
            .imageLayout = sp_att->stencil_layout,
            .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
         };
         data->rendering.pStencilAttachment = attachments++;
      }
   }

   if (subpass->fragment_shading_rate_attachment) {
      const struct vk_subpass_attachment *sp_att =
         subpass->fragment_shading_rate_attachment;
      assert(sp_att->attachment < pass->attachment_count);

      data->fsr_att = (VkRenderingFragmentShadingRateAttachmentInfoKHR) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR,
         .imageView = fb->attachments[sp_att->attachment],
         .imageLayout = sp_att->layout,
         .shadingRateAttachmentTexelSize =
            subpass->fragment_shading_rate_attachment_texel_size,
      };
      __vk_append_struct(&data->rendering, &data->fsr_att);
   }

   /* Append this one last because it lives in the subpass and we don't want
    * to be changed by appending other structures later.
    */
   if (subpass->mrtss.multisampledRenderToSingleSampledEnable)
      __vk_append_struct(&data->rendering, (void *)&subpass->mrtss);

   return &data->rendering;
}

const VkRenderingAttachmentLocationInfoKHR *
vk_get_command_buffer_rendering_attachment_location_info(
   VkCommandBufferLevel level,
   const VkCommandBufferBeginInfo *pBeginInfo)
{
   /* From the Vulkan 1.3.295 spec:
    *
    *    "VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT specifies that a
    *    secondary command buffer is considered to be entirely inside a render
    *    pass. If this is a primary command buffer, then this bit is ignored."
    *
    * Since we're only concerned with the continue case here, we can ignore
    * any primary command buffers.
    */
   if (level == VK_COMMAND_BUFFER_LEVEL_PRIMARY)
      return NULL;

   /* From the Vulkan 1.3.295 spec:
    *
    *    "This structure can be included in the pNext chain of a
    *    VkCommandBufferInheritanceInfo structure to specify inherited state
    *    from the primary command buffer. If
    *    VkCommandBufferInheritanceInfo::renderPass is not VK_NULL_HANDLE, or
    *    VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT is not specified in
    *    VkCommandBufferBeginInfo::flags, members of this structure are
    *    ignored."
    *
    * For the case where a render pass is provided and we're emulating it on
    * behalf of the driver, the default NULL behavior is sufficient:
    *
    *    "If this structure is not included in the pNext chain of
    *    VkCommandBufferInheritanceInfo, it is equivalent to specifying this
    *    structure with the following properties:
    *
    *     - colorAttachmentCount set to
    *       VkCommandBufferInheritanceRenderingInfo::colorAttachmentCount.
    *
    *     - pColorAttachmentLocations set to NULL."
    */
   if (!(pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT))
      return NULL;

   if (pBeginInfo->pInheritanceInfo->renderPass != VK_NULL_HANDLE)
      return NULL;

   return vk_find_struct_const(pBeginInfo,
                               RENDERING_ATTACHMENT_LOCATION_INFO_KHR);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_DestroyRenderPass(VkDevice _device,
                            VkRenderPass renderPass,
                            const VkAllocationCallbacks *pAllocator)
{
   VK_FROM_HANDLE(vk_device, device, _device);
   VK_FROM_HANDLE(vk_render_pass, pass, renderPass);

   if (!pass)
      return;

   vk_object_free(device, pAllocator, pass);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_GetRenderAreaGranularity(VkDevice device,
                                   VkRenderPass renderPass,
                                   VkExtent2D *pGranularity)
{
   *pGranularity = (VkExtent2D){1, 1};
}

VKAPI_ATTR void VKAPI_CALL
vk_common_GetRenderingAreaGranularityKHR(
   VkDevice _device, const VkRenderingAreaInfoKHR *pRenderingAreaInfo,
   VkExtent2D *pGranularity)
{
   *pGranularity = (VkExtent2D) { 1, 1 };
}

static VkRenderPassSampleLocationsBeginInfoEXT *
clone_rp_sample_locations(const VkRenderPassSampleLocationsBeginInfoEXT *loc)
{
   uint32_t sl_count = 0;

   for (uint32_t i = 0; i < loc->attachmentInitialSampleLocationsCount; i++) {
      const VkAttachmentSampleLocationsEXT *att_sl_in =
         &loc->pAttachmentInitialSampleLocations[i];
      sl_count += att_sl_in->sampleLocationsInfo.sampleLocationsCount;
   }
   for (uint32_t i = 0; i < loc->postSubpassSampleLocationsCount; i++) {
      const VkSubpassSampleLocationsEXT *sp_sl_in =
         &loc->pPostSubpassSampleLocations[i];
      sl_count += sp_sl_in->sampleLocationsInfo.sampleLocationsCount;
   }

   VK_MULTIALLOC(ma);
   VK_MULTIALLOC_DECL(&ma, VkRenderPassSampleLocationsBeginInfoEXT, new_loc, 1);
   VK_MULTIALLOC_DECL(&ma, VkAttachmentSampleLocationsEXT, new_att_sl,
                      loc->attachmentInitialSampleLocationsCount);
   VK_MULTIALLOC_DECL(&ma, VkSubpassSampleLocationsEXT, new_sp_sl,
                      loc->postSubpassSampleLocationsCount);
   VK_MULTIALLOC_DECL(&ma, VkSampleLocationEXT, sl, sl_count);
   if (!vk_multialloc_alloc(&ma, vk_default_allocator(),
                            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT))
      return NULL;

   VkSampleLocationEXT *next_sl = sl;
   for (uint32_t i = 0; i < loc->attachmentInitialSampleLocationsCount; i++) {
      const VkAttachmentSampleLocationsEXT *att_sl_in =
         &loc->pAttachmentInitialSampleLocations[i];
      const VkSampleLocationsInfoEXT *sli_in = &att_sl_in->sampleLocationsInfo;

      typed_memcpy(next_sl, sli_in->pSampleLocations,
                   sli_in->sampleLocationsCount);

      new_att_sl[i] = (VkAttachmentSampleLocationsEXT) {
         .attachmentIndex = att_sl_in->attachmentIndex,
         .sampleLocationsInfo = {
            .sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT,
            .sampleLocationsPerPixel = sli_in->sampleLocationsPerPixel,
            .sampleLocationGridSize = sli_in->sampleLocationGridSize,
            .sampleLocationsCount = sli_in->sampleLocationsCount,
            .pSampleLocations = next_sl,
         },
      };

      next_sl += sli_in->sampleLocationsCount;
   }

   for (uint32_t i = 0; i < loc->postSubpassSampleLocationsCount; i++) {
      const VkSubpassSampleLocationsEXT *sp_sl_in =
         &loc->pPostSubpassSampleLocations[i];
      const VkSampleLocationsInfoEXT *sli_in = &sp_sl_in->sampleLocationsInfo;

      typed_memcpy(next_sl, sli_in->pSampleLocations,
                   sli_in->sampleLocationsCount);

      new_sp_sl[i] = (VkSubpassSampleLocationsEXT) {
         .subpassIndex = sp_sl_in->subpassIndex,
         .sampleLocationsInfo = {
            .sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT,
            .sampleLocationsPerPixel = sli_in->sampleLocationsPerPixel,
            .sampleLocationGridSize = sli_in->sampleLocationGridSize,
            .sampleLocationsCount = sli_in->sampleLocationsCount,
            .pSampleLocations = next_sl,
         },
      };

      next_sl += sli_in->sampleLocationsCount;
   }

   assert(next_sl == sl + sl_count);

   *new_loc = (VkRenderPassSampleLocationsBeginInfoEXT) {
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT,
      .attachmentInitialSampleLocationsCount = loc->attachmentInitialSampleLocationsCount,
      .pAttachmentInitialSampleLocations = new_att_sl,
      .postSubpassSampleLocationsCount = loc->postSubpassSampleLocationsCount,
      .pPostSubpassSampleLocations = new_sp_sl,
   };

   return new_loc;
}

static const VkSampleLocationsInfoEXT *
get_subpass_sample_locations(const VkRenderPassSampleLocationsBeginInfoEXT *loc,
                             uint32_t subpass_idx)
{
   for (uint32_t i = 0; i < loc->postSubpassSampleLocationsCount; i++) {
      if (loc->pPostSubpassSampleLocations[i].subpassIndex == subpass_idx)
         return &loc->pPostSubpassSampleLocations[i].sampleLocationsInfo;
   }

   return NULL;
}

static bool
vk_image_layout_supports_input_attachment(VkImageLayout layout)
{
   switch (layout) {
   case VK_IMAGE_LAYOUT_GENERAL:
   case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
   case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
   case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
   case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
   case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL:
   case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL:
   case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR:
   case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT:
      return true;
   default:
      return false;
   }
}

struct stage_access {
   VkPipelineStageFlagBits2 stages;
   VkAccessFlagBits2 access;
};

static bool
vk_image_layout_are_all_aspects_read_only(VkImageLayout layout,
                                          VkImageAspectFlags aspects)
{
   u_foreach_bit(a, aspects) {
      VkImageAspectFlagBits aspect = 1u << a;
      if (!vk_image_layout_is_read_only(layout, aspect))
         return false;
   }
   return true;
}

static struct stage_access
stage_access_for_layout(VkImageLayout layout, VkImageAspectFlags aspects)
{
   VkPipelineStageFlagBits2 stages = 0;
   VkAccessFlagBits2 access = 0;

   if (vk_image_layout_supports_input_attachment(layout)) {
      stages |= VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
      access |= VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT;
   }

   if (aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
      stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
                VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
      access |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
      if (!vk_image_layout_are_all_aspects_read_only(layout, aspects)) {
         access |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

         /* It might be a resolve attachment */
         stages |= VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT;
         access |= VK_ACCESS_2_TRANSFER_WRITE_BIT;
      }
   } else {
      /* Color */
      if (!vk_image_layout_are_all_aspects_read_only(layout, aspects)) {
         /* There are no read-only color attachments */
         stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
         access |= VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT |
                   VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;

         /* It might be a resolve attachment */
         stages |= VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT;
         access |= VK_ACCESS_2_TRANSFER_WRITE_BIT;
      }
   }

   return (struct stage_access) {
      .stages = stages,
      .access = access,
   };
}

static void
transition_image_range(const struct vk_image_view *image_view,
                       VkImageSubresourceRange range,
                       VkImageLayout old_layout,
                       VkImageLayout new_layout,
                       VkImageLayout old_stencil_layout,
                       VkImageLayout new_stencil_layout,
                       const VkSampleLocationsInfoEXT *sample_locations,
                       uint32_t *barrier_count,
                       uint32_t max_barrier_count,
                       VkImageMemoryBarrier2 *barriers)
{
   VkImageAspectFlags aspects_left = range.aspectMask;
   while (aspects_left) {
      range.aspectMask = aspects_left;

      /* If we have a depth/stencil image and one of the layouts doesn't match
       * between depth and stencil, we need two barriers.  Restrict to depth
       * and we'll pick up stencil on the next iteration.
       */
      if (range.aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT |
                               VK_IMAGE_ASPECT_STENCIL_BIT) &&
          (old_layout != old_stencil_layout ||
           new_layout != new_stencil_layout))
         range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;

      if (range.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
         /* We're down to a single aspect bit so this is going to be the last
          * iteration and it's fine to stomp the input variables here.
          */
         old_layout = old_stencil_layout;
         new_layout = new_stencil_layout;
      }

      if (new_layout != old_layout) {
         /* We could go about carefully calculating every possible way the
          * attachment may have been used in the render pass or we can break
          * out the big hammer and throw in any stage and access flags
          * possible for the given layouts.
          */
         struct stage_access src_sa, dst_sa;
         src_sa = stage_access_for_layout(old_layout, range.aspectMask);
         dst_sa = stage_access_for_layout(new_layout, range.aspectMask);

         assert(*barrier_count < max_barrier_count);
         barriers[(*barrier_count)++] = (VkImageMemoryBarrier2) {
            .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
            .pNext = sample_locations,
            .srcStageMask = src_sa.stages,
            .srcAccessMask = src_sa.access,
            .dstStageMask = dst_sa.stages,
            .dstAccessMask = dst_sa.access,
            .oldLayout = old_layout,
            .newLayout = new_layout,
            .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
            .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
            .image = vk_image_to_handle(image_view->image),
            .subresourceRange = range,
         };
      }

      aspects_left &= ~range.aspectMask;
   }
}

static bool
can_use_attachment_initial_layout(struct vk_command_buffer *cmd_buffer,
                                  uint32_t att_idx,
                                  uint32_t view_mask,
                                  VkImageLayout *layout_out,
                                  VkImageLayout *stencil_layout_out)
{
   const struct vk_render_pass *pass = cmd_buffer->render_pass;
   const struct vk_framebuffer *framebuffer = cmd_buffer->framebuffer;
   const struct vk_render_pass_attachment *rp_att = &pass->attachments[att_idx];
   struct vk_attachment_state *att_state = &cmd_buffer->attachments[att_idx];
   const struct vk_image_view *image_view = att_state->image_view;

   if ((rp_att->aspects & ~VK_IMAGE_ASPECT_STENCIL_BIT) &&
       rp_att->load_op != VK_ATTACHMENT_LOAD_OP_CLEAR)
      return false;

   if ((rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) &&
       rp_att->stencil_load_op != VK_ATTACHMENT_LOAD_OP_CLEAR)
      return false;

   if (cmd_buffer->render_area.offset.x != 0 ||
       cmd_buffer->render_area.offset.y != 0 ||
       cmd_buffer->render_area.extent.width != image_view->extent.width ||
       cmd_buffer->render_area.extent.height != image_view->extent.height)
      return false;

   if (image_view->image->image_type == VK_IMAGE_TYPE_3D) {
      /* For 3D images, the view has to be the whole thing */
      if (image_view->base_array_layer != 0)
         return false;

      if (pass->is_multiview) {
         if (!util_is_power_of_two_or_zero(view_mask + 1) ||
             util_last_bit(view_mask) != image_view->layer_count)
            return false;
      } else {
         if (framebuffer->layers != image_view->layer_count)
            return false;
      }
   }

   /* Finally, check if the entire thing is undefined.  It's ok to smash the
    * view_mask now as the only thing using it will be the loop below.
    */

   /* 3D is stupidly special. See transition_view_mask() */
   if (image_view->image->image_type == VK_IMAGE_TYPE_3D)
      view_mask = 1;

   VkImageLayout layout = VK_IMAGE_LAYOUT_MAX_ENUM;
   VkImageLayout stencil_layout = VK_IMAGE_LAYOUT_MAX_ENUM;

   assert(view_mask != 0);
   u_foreach_bit(view, view_mask) {
      assert(view >= 0 && view < MESA_VK_MAX_MULTIVIEW_VIEW_COUNT);
      struct vk_attachment_view_state *att_view_state = &att_state->views[view];

      if (rp_att->aspects & ~VK_IMAGE_ASPECT_STENCIL_BIT) {
         if (layout == VK_IMAGE_LAYOUT_MAX_ENUM)
            layout = att_view_state->layout;
         else if (layout != att_view_state->layout)
            return false;
      }

      if (rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {
         if (stencil_layout == VK_IMAGE_LAYOUT_MAX_ENUM)
            stencil_layout = att_view_state->stencil_layout;
         else if (stencil_layout != att_view_state->stencil_layout)
            return false;
      }
   }

   if (layout != VK_IMAGE_LAYOUT_MAX_ENUM)
      *layout_out = layout;
   else if (layout_out != NULL)
      *layout_out = VK_IMAGE_LAYOUT_UNDEFINED;

   if (stencil_layout != VK_IMAGE_LAYOUT_MAX_ENUM)
      *stencil_layout_out = stencil_layout;
   else if (stencil_layout_out != NULL)
      *stencil_layout_out = VK_IMAGE_LAYOUT_UNDEFINED;

   return true;
}

uint32_t
vk_command_buffer_get_attachment_layout(const struct vk_command_buffer *cmd_buffer,
                                        const struct vk_image *image,
                                        VkImageLayout *out_layout,
                                        VkImageLayout *out_stencil_layout)
{
   const struct vk_render_pass *render_pass = cmd_buffer->render_pass;
   assert(render_pass != NULL);

   const struct vk_subpass *subpass =
      &render_pass->subpasses[cmd_buffer->subpass_idx];
   int first_view = ffs(subpass->view_mask) - 1;

   for (uint32_t a = 0; a < render_pass->attachment_count; a++) {
      if (cmd_buffer->attachments[a].image_view->image == image) {
         *out_layout = cmd_buffer->attachments[a].views[first_view].layout;
         *out_stencil_layout =
            cmd_buffer->attachments[a].views[first_view].stencil_layout;
         return a;
      }
   }
   UNREACHABLE("Image not found in attachments");
}

void
vk_command_buffer_set_attachment_layout(struct vk_command_buffer *cmd_buffer,
                                        uint32_t att_idx,
                                        VkImageLayout layout,
                                        VkImageLayout stencil_layout)
{
   const struct vk_render_pass *render_pass = cmd_buffer->render_pass;
   const struct vk_subpass *subpass =
      &render_pass->subpasses[cmd_buffer->subpass_idx];
   uint32_t view_mask = subpass->view_mask;
   struct vk_attachment_state *att_state = &cmd_buffer->attachments[att_idx];

   u_foreach_bit(view, view_mask) {
      assert(view >= 0 && view < MESA_VK_MAX_MULTIVIEW_VIEW_COUNT);
      struct vk_attachment_view_state *att_view_state = &att_state->views[view];

      att_view_state->layout = layout;
      att_view_state->stencil_layout = stencil_layout;
   }
}

static uint32_t
transition_view_mask(struct vk_command_buffer *cmd_buffer,
                     uint32_t att_idx,
                     uint32_t view_mask,
                     VkImageLayout layout,
                     VkImageLayout stencil_layout)
{
   struct vk_attachment_state *att_state = &cmd_buffer->attachments[att_idx];
   const struct vk_image_view *image_view = att_state->image_view;

   uint32_t transition_view_mask = 0;

   /* 3D is stupidly special.  From the Vulkan 1.3.204 spec:
    *
    *    "When the VkImageSubresourceRange structure is used to select a
    *    subset of the slices of a 3D image’s mip level in order to create
    *    a 2D or 2D array image view of a 3D image created with
    *    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT, baseArrayLayer and
    *    layerCount specify the first slice index and the number of slices
    *    to include in the created image view. Such an image view can be
    *    used as a framebuffer attachment that refers only to the specified
    *    range of slices of the selected mip level. However, any layout
    *    transitions performed on such an attachment view during a render
    *    pass instance still apply to the entire subresource referenced
    *    which includes all the slices of the selected mip level."
    *
    * To deal with this, we expand out the layer range to include the
    * entire 3D image and treat them as having only a single view even when
    * multiview is enabled.  This later part means that we effectively only
    * track one image layout for the entire attachment rather than one per
    * view like we do for all the others.
    */
   if (image_view->image->image_type == VK_IMAGE_TYPE_3D)
      view_mask = 1;

   u_foreach_bit(view, view_mask) {
      assert(view >= 0 && view < MESA_VK_MAX_MULTIVIEW_VIEW_COUNT);
      struct vk_attachment_view_state *att_view_state = &att_state->views[view];

      if (att_view_state->layout != layout ||
          att_view_state->stencil_layout != stencil_layout)
         transition_view_mask |= BITFIELD_BIT(view);
   }

   return transition_view_mask;
}

static uint32_t
transition_attachment(struct vk_command_buffer *cmd_buffer,
                      uint32_t att_idx,
                      uint32_t view_mask,
                      VkImageLayout layout,
                      VkImageLayout stencil_layout,
                      uint32_t *barrier_count,
                      uint32_t max_barrier_count,
                      VkImageMemoryBarrier2 *barriers)
{
   const struct vk_render_pass *pass = cmd_buffer->render_pass;
   const struct vk_framebuffer *framebuffer = cmd_buffer->framebuffer;
   const struct vk_render_pass_attachment *pass_att =
      &pass->attachments[att_idx];
   struct vk_attachment_state *att_state = &cmd_buffer->attachments[att_idx];
   const struct vk_image_view *image_view = att_state->image_view;

   /* Get a mask of views that need a layout transition. */
   view_mask = transition_view_mask(cmd_buffer, att_idx, view_mask, layout,
                                    stencil_layout);

   u_foreach_bit(view, view_mask) {
      assert(view >= 0 && view < MESA_VK_MAX_MULTIVIEW_VIEW_COUNT);
      struct vk_attachment_view_state *att_view_state = &att_state->views[view];

      assert(att_view_state->layout != layout ||
             att_view_state->stencil_layout != stencil_layout);

      VkImageSubresourceRange range = {
         .aspectMask = pass_att->aspects,
         .baseMipLevel = image_view->base_mip_level,
         .levelCount = 1,
      };

      /* From the Vulkan 1.3.207 spec:
       *
       *    "Automatic layout transitions apply to the entire image
       *    subresource attached to the framebuffer. If multiview is not
       *    enabled and the attachment is a view of a 1D or 2D image, the
       *    automatic layout transitions apply to the number of layers
       *    specified by VkFramebufferCreateInfo::layers. If multiview is
       *    enabled and the attachment is a view of a 1D or 2D image, the
       *    automatic layout transitions apply to the layers corresponding to
       *    views which are used by some subpass in the render pass, even if
       *    that subpass does not reference the given attachment. If the
       *    attachment view is a 2D or 2D array view of a 3D image, even if
       *    the attachment view only refers to a subset of the slices of the
       *    selected mip level of the 3D image, automatic layout transitions
       *    apply to the entire subresource referenced which is the entire mip
       *    level in this case."
       */
      if (image_view->image->image_type == VK_IMAGE_TYPE_3D) {
         /* 3D is stupidly special. See transition_view_mask() */
         assert(view == 0);
         if (image_view->view_type == VK_IMAGE_VIEW_TYPE_2D &&
             cmd_buffer->base.device->enabled_features.maintenance9) {
            range.baseArrayLayer = image_view->base_array_layer;
            range.layerCount = image_view->layer_count;
         } else {
            range.baseArrayLayer = 0;
            range.layerCount = image_view->extent.depth;
         }
      } else if (pass->is_multiview) {
         range.baseArrayLayer = image_view->base_array_layer + view;
         range.layerCount = 1;
      } else {
         assert(view == 0);
         range.baseArrayLayer = image_view->base_array_layer;
         range.layerCount = framebuffer->layers;
      }

      transition_image_range(image_view, range,
                             att_view_state->layout, layout,
                             att_view_state->stencil_layout, stencil_layout,
                             att_view_state->sample_locations,
                             barrier_count, max_barrier_count, barriers);

      att_view_state->layout = layout;
      att_view_state->stencil_layout = stencil_layout;
   }

   return view_mask;
}

static void
load_attachment(struct vk_command_buffer *cmd_buffer,
                uint32_t att_idx, uint32_t view_mask,
                VkImageLayout layout, VkImageLayout stencil_layout)
{
   const struct vk_render_pass *pass = cmd_buffer->render_pass;
   const struct vk_framebuffer *framebuffer = cmd_buffer->framebuffer;
   const struct vk_render_pass_attachment *rp_att = &pass->attachments[att_idx];
   struct vk_attachment_state *att_state = &cmd_buffer->attachments[att_idx];
   struct vk_device_dispatch_table *disp =
      &cmd_buffer->base.device->dispatch_table;

   /* Don't load any views we've already loaded */
   view_mask &= ~att_state->views_loaded;
   if (view_mask == 0)
      return;

   /* From here on, if we return, we loaded the views */
   att_state->views_loaded |= view_mask;

   /* We only need to load/store if there's a clear */
   bool need_load_store = false;
   if ((rp_att->aspects & ~VK_IMAGE_ASPECT_STENCIL_BIT) &&
       rp_att->load_op == VK_ATTACHMENT_LOAD_OP_CLEAR)
      need_load_store = true;

   if ((rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) &&
       rp_att->stencil_load_op == VK_ATTACHMENT_LOAD_OP_CLEAR)
      need_load_store = true;

   if (!need_load_store)
      return;

   const VkRenderingAttachmentFlagsInfoKHR att_flags = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR,
      .flags = vk_attachment_description_flags_to_rendering_flags(rp_att->flags),
   };

   const VkRenderingAttachmentInfo att = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
      .pNext = &att_flags,
      .imageView = vk_image_view_to_handle(att_state->image_view),
      .imageLayout = layout,
      .loadOp = rp_att->load_op,
      .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
      .clearValue = att_state->clear_value,
   };

   const VkRenderingAttachmentInfo stencil_att = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
      .pNext = &att_flags,
      .imageView = vk_image_view_to_handle(att_state->image_view),
      .imageLayout = stencil_layout,
      .loadOp = rp_att->stencil_load_op,
      .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
      .clearValue = att_state->clear_value,
   };

   VkRenderingInfo render = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
      .flags = VK_RENDERING_LOCAL_READ_CONCURRENT_ACCESS_CONTROL_BIT_KHR,
      .renderArea = cmd_buffer->render_area,
      .layerCount = pass->is_multiview ? 1 : framebuffer->layers,
      .viewMask = pass->is_multiview ? view_mask : 0,
   };

   if (rp_att->aspects & (VK_IMAGE_ASPECT_DEPTH_BIT |
                          VK_IMAGE_ASPECT_STENCIL_BIT)) {
      if (rp_att->aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
         render.pDepthAttachment = &att;
      if (rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
         render.pStencilAttachment = &stencil_att;
   } else {
      render.colorAttachmentCount = 1;
      render.pColorAttachments = &att;
   }

   disp->CmdBeginRendering(vk_command_buffer_to_handle(cmd_buffer), &render);
   disp->CmdEndRendering(vk_command_buffer_to_handle(cmd_buffer));
}

static void
begin_subpass(struct vk_command_buffer *cmd_buffer,
              const VkSubpassBeginInfo *begin_info)
{
   const struct vk_render_pass *pass = cmd_buffer->render_pass;
   const struct vk_framebuffer *framebuffer = cmd_buffer->framebuffer;
   const uint32_t subpass_idx = cmd_buffer->subpass_idx;
   assert(subpass_idx < pass->subpass_count);
   const struct vk_subpass *subpass = &pass->subpasses[subpass_idx];
   struct vk_device_dispatch_table *disp =
      &cmd_buffer->base.device->dispatch_table;

   /* First, we figure out all our attachments and attempt to handle image
    * layout transitions and load ops as part of vkCmdBeginRendering if we
    * can.  For any we can't handle this way, we'll need explicit barriers
    * or quick vkCmdBegin/EndRendering to do the load op.
    */

   STACK_ARRAY(VkRenderingAttachmentFlagsInfoKHR, color_attachments_flags,
               subpass->color_count);
   STACK_ARRAY(VkRenderingAttachmentInfo, color_attachments,
               subpass->color_count);
   STACK_ARRAY(VkRenderingAttachmentInitialLayoutInfoMESA,
               color_attachment_initial_layouts,
               subpass->color_count);

   for (uint32_t i = 0; i < subpass->color_count; i++) {
      const struct vk_subpass_attachment *sp_att =
         &subpass->color_attachments[i];
      VkRenderingAttachmentFlagsInfoKHR *color_attachment_flags =
         &color_attachments_flags[i];
      VkRenderingAttachmentInfo *color_attachment = &color_attachments[i];

      if (sp_att->attachment == VK_ATTACHMENT_UNUSED) {
         *color_attachment = (VkRenderingAttachmentInfo) {
            .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
            .imageView = VK_NULL_HANDLE,
         };
         continue;
      }

      assert(sp_att->attachment < pass->attachment_count);
      const struct vk_render_pass_attachment *rp_att =
         &pass->attachments[sp_att->attachment];
      struct vk_attachment_state *att_state =
         &cmd_buffer->attachments[sp_att->attachment];

      *color_attachment_flags = (VkRenderingAttachmentFlagsInfoKHR) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR,
      };

      *color_attachment = (VkRenderingAttachmentInfo) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
         .pNext = color_attachment_flags,
         .imageView = vk_image_view_to_handle(att_state->image_view),
         .imageLayout = sp_att->layout,
      };

      if (!(subpass->view_mask & att_state->views_loaded)) {
         /* None of these views have been used before */
         color_attachment->loadOp = rp_att->load_op;
         color_attachment->clearValue = att_state->clear_value;
         att_state->views_loaded |= subpass->view_mask;

         VkImageLayout initial_layout;
         if (can_use_attachment_initial_layout(cmd_buffer,
                                               sp_att->attachment,
                                               subpass->view_mask,
                                               &initial_layout, NULL) &&
             sp_att->layout != initial_layout) {
            assert(color_attachment->loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR);

            VkRenderingAttachmentInitialLayoutInfoMESA *color_initial_layout =
               &color_attachment_initial_layouts[i];
            *color_initial_layout = (VkRenderingAttachmentInitialLayoutInfoMESA) {
               .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INITIAL_LAYOUT_INFO_MESA,
               .initialLayout = initial_layout,
            };
            __vk_append_struct(color_attachment, color_initial_layout);

            vk_command_buffer_set_attachment_layout(cmd_buffer,
                                                    sp_att->attachment,
                                                    sp_att->layout,
                                                    VK_IMAGE_LAYOUT_UNDEFINED);
         }
      } else {
         /* We've seen at least one of the views of this attachment before so
          * we need to LOAD_OP_LOAD.
          */
         color_attachment->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
      }

      if (!(subpass->view_mask & ~sp_att->last_subpass)) {
         /* This is the last subpass for every view */
         color_attachment->storeOp = rp_att->store_op;
      } else {
         /* For at least one of our views, this isn't the last subpass
          *
          * In the edge case where we have lots of weird overlap between view
          * masks of different subThis may mean that we get STORE_OP_STORE in
          * some places where it may have wanted STORE_OP_NONE but that should
          * be harmless.
          */
         color_attachment->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
      }

      if (sp_att->resolve != NULL) {
         assert(sp_att->resolve->attachment < pass->attachment_count);
         struct vk_attachment_state *res_att_state =
            &cmd_buffer->attachments[sp_att->resolve->attachment];

         /* Resolve attachments are entirely overwritten by the resolve
          * operation so the load op really doesn't matter.  We can consider
          * the resolve as being the load.
          */
         res_att_state->views_loaded |= subpass->view_mask;

         const struct vk_render_pass_attachment *resolve_att =
            &pass->attachments[sp_att->resolve->attachment];
         if (resolve_att->has_external_format)
            color_attachment->resolveMode = VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_BIT_ANDROID;
         else if (vk_format_is_int(res_att_state->image_view->format))
            color_attachment->resolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
         else
            color_attachment->resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;

         color_attachment->resolveImageView =
            vk_image_view_to_handle(res_att_state->image_view);
         color_attachment->resolveImageLayout = sp_att->resolve->layout;

         color_attachment_flags->flags =
            vk_attachment_description_flags_to_rendering_flags(resolve_att->flags);
      } else if (subpass->mrtss.multisampledRenderToSingleSampledEnable &&
                 rp_att->samples == VK_SAMPLE_COUNT_1_BIT) {
         if (vk_format_is_int(att_state->image_view->format))
            color_attachment->resolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
         else
            color_attachment->resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;
      }
   }

   VkRenderingAttachmentInfo depth_attachment = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
   };
   VkRenderingAttachmentInfo stencil_attachment = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
   };
   VkRenderingAttachmentFlagsInfoKHR depth_attachment_flags = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR,
   };
   VkRenderingAttachmentFlagsInfoKHR stencil_attachment_flags = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR,
   };
   VkRenderingAttachmentInitialLayoutInfoMESA depth_initial_layout = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INITIAL_LAYOUT_INFO_MESA,
   };
   VkRenderingAttachmentInitialLayoutInfoMESA stencil_initial_layout = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INITIAL_LAYOUT_INFO_MESA,
   };

   const VkSampleLocationsInfoEXT *sample_locations = NULL;
   if (subpass->depth_stencil_attachment != NULL) {
      const struct vk_subpass_attachment *sp_att =
         subpass->depth_stencil_attachment;

      assert(sp_att->attachment < pass->attachment_count);
      const struct vk_render_pass_attachment *rp_att =
         &pass->attachments[sp_att->attachment];
      struct vk_attachment_state *att_state =
         &cmd_buffer->attachments[sp_att->attachment];

      assert(sp_att->aspects == rp_att->aspects);
      if (rp_att->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {
         depth_attachment.imageView =
            vk_image_view_to_handle(att_state->image_view);
         depth_attachment.imageLayout = sp_att->layout;
         depth_attachment_flags.flags =
            vk_attachment_description_flags_to_rendering_flags(rp_att->flags);
      }

      if (rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {
         stencil_attachment.imageView =
            vk_image_view_to_handle(att_state->image_view);
         stencil_attachment.imageLayout = sp_att->stencil_layout;
         stencil_attachment_flags.flags =
            vk_attachment_description_flags_to_rendering_flags(rp_att->flags);
      }

      __vk_append_struct(&depth_attachment,
                         &depth_attachment_flags);
      __vk_append_struct(&stencil_attachment,
                         &stencil_attachment_flags);

      if (!(subpass->view_mask & att_state->views_loaded)) {
         /* None of these views have been used before */
         depth_attachment.loadOp = rp_att->load_op;
         depth_attachment.clearValue = att_state->clear_value;
         stencil_attachment.loadOp = rp_att->stencil_load_op;
         stencil_attachment.clearValue = att_state->clear_value;
         att_state->views_loaded |= subpass->view_mask;

         VkImageLayout initial_layout, initial_stencil_layout;
         if (can_use_attachment_initial_layout(cmd_buffer,
                                               sp_att->attachment,
                                               subpass->view_mask,
                                               &initial_layout,
                                               &initial_stencil_layout)) {
            if ((rp_att->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) &&
                sp_att->layout != initial_layout) {
               assert(depth_attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR);
               depth_initial_layout.initialLayout = initial_layout;
               __vk_append_struct(&depth_attachment,
                                  &depth_initial_layout);
            }

            if ((rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) &&
                sp_att->stencil_layout != initial_stencil_layout) {
               assert(stencil_attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR);
               stencil_initial_layout.initialLayout = initial_stencil_layout;
               __vk_append_struct(&stencil_attachment,
                                  &stencil_initial_layout);
            }

            vk_command_buffer_set_attachment_layout(cmd_buffer,
                                                    sp_att->attachment,
                                                    sp_att->layout,
                                                    sp_att->stencil_layout);
         }
      } else {
         /* We've seen at least one of the views of this attachment before so
          * we need to LOAD_OP_LOAD.
          */
         depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
         stencil_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
      }

      if (!(subpass->view_mask & ~sp_att->last_subpass)) {
         /* This is the last subpass for every view */
         depth_attachment.storeOp = rp_att->store_op;
         stencil_attachment.storeOp = rp_att->stencil_store_op;
      } else {
         /* For at least one of our views, this isn't the last subpass
          *
          * In the edge case where we have lots of weird overlap between view
          * masks of different subThis may mean that we get STORE_OP_STORE in
          * some places where it may have wanted STORE_OP_NONE but that should
          * be harmless.
          */
         depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
         stencil_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
      }

      /* From the Vulkan 1.3.212 spec:
       *
       *    "If the current render pass does not use the attachment as a
       *    depth/stencil attachment in any subpass that happens-before, the
       *    automatic layout transition uses the sample locations state
       *    specified in the sampleLocationsInfo member of the element of the
       *    VkRenderPassSampleLocationsBeginInfoEXT::pAttachmentInitialSampleLocations
       *    array for which the attachmentIndex member equals the attachment
       *    index of the attachment, if one is specified. Otherwise, the
       *    automatic layout transition uses the sample locations state
       *    specified in the sampleLocationsInfo member of the element of the
       *    VkRenderPassSampleLocationsBeginInfoEXT::pPostSubpassSampleLocations
       *    array for which the subpassIndex member equals the index of the
       *    subpass that last used the attachment as a depth/stencil
       *    attachment, if one is specified."
       *
       * Unfortunately, this says nothing whatsoever about multiview.
       * However, since multiview render passes are described as a single-view
       * render pass repeated per-view, we assume this is per-view.
       */
      if (cmd_buffer->pass_sample_locations != NULL &&
          (att_state->image_view->image->create_flags &
           VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT)) {
         sample_locations =
            get_subpass_sample_locations(cmd_buffer->pass_sample_locations,
                                         subpass_idx);

         u_foreach_bit(view, subpass->view_mask)
            att_state->views[view].sample_locations = sample_locations;
      }

      if (sp_att->resolve != NULL ||
          (subpass->mrtss.multisampledRenderToSingleSampledEnable &&
           rp_att->samples == VK_SAMPLE_COUNT_1_BIT)) {
         const struct vk_subpass_attachment *res_sp_att = sp_att->resolve ? sp_att->resolve : sp_att;
         assert(res_sp_att->attachment < pass->attachment_count);
         const struct vk_render_pass_attachment *res_rp_att =
            &pass->attachments[res_sp_att->attachment];
         struct vk_attachment_state *res_att_state =
            &cmd_buffer->attachments[res_sp_att->attachment];

         /* From the Vulkan 1.3.204 spec:
          *
          *    "VkSubpassDescriptionDepthStencilResolve::depthResolveMode is
          *    ignored if the VkFormat of the pDepthStencilResolveAttachment
          *    does not have a depth component. Similarly,
          *    VkSubpassDescriptionDepthStencilResolve::stencilResolveMode is
          *    ignored if the VkFormat of the pDepthStencilResolveAttachment
          *    does not have a stencil component."
          *
          * TODO: Should we handle this here or when we create the render
          * pass?  Handling it here makes load ops "correct" in the sense
          * that, if we resolve to the wrong aspect, we will still consider
          * it bound and clear it if requested.
          */
         VkResolveModeFlagBits depth_resolve_mode = VK_RESOLVE_MODE_NONE;
         if (res_rp_att->aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
            depth_resolve_mode = subpass->depth_resolve_mode;

         VkResolveModeFlagBits stencil_resolve_mode = VK_RESOLVE_MODE_NONE;
         if (res_rp_att->aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
            stencil_resolve_mode = subpass->stencil_resolve_mode;

         VkImageAspectFlags resolved_aspects = 0;

         if (depth_resolve_mode != VK_RESOLVE_MODE_NONE) {
            depth_attachment.resolveMode = depth_resolve_mode;
            if (sp_att->resolve) {
               depth_attachment.resolveImageView =
                  vk_image_view_to_handle(res_att_state->image_view);
               depth_attachment.resolveImageLayout =
                  sp_att->resolve->layout;
            }

            resolved_aspects |= VK_IMAGE_ASPECT_DEPTH_BIT;
         }

         if (stencil_resolve_mode != VK_RESOLVE_MODE_NONE) {
            stencil_attachment.resolveMode = stencil_resolve_mode;
            if (sp_att->resolve) {
               stencil_attachment.resolveImageView =
                  vk_image_view_to_handle(res_att_state->image_view);
               stencil_attachment.resolveImageLayout =
                  sp_att->resolve->stencil_layout;
            }

            resolved_aspects |= VK_IMAGE_ASPECT_STENCIL_BIT;
         }

         if (sp_att->resolve && resolved_aspects == rp_att->aspects) {
            /* The resolve attachment is entirely overwritten by the
             * resolve operation so the load op really doesn't matter.
             * We can consider the resolve as being the load.
             */
            res_att_state->views_loaded |= subpass->view_mask;
         }
      }
   }

   /* Next, handle any barriers we need.  This may include general
    * VkMemoryBarriers for subpass dependencies and it may include some
    * number of VkImageMemoryBarriers for layout transitions.
    */

   /* At most all dependencies will need a barrier, and we might have an
    * implicit one. */
   STACK_ARRAY(VkMemoryBarrier2, mem_barriers, pass->dependency_count + 1);
   uint32_t mem_barrier_count = 0;
   bool external_dependency = false;
   for (uint32_t d = 0; d < pass->dependency_count; d++) {
      const struct vk_subpass_dependency *dep = &pass->dependencies[d];
      if (dep->dst_subpass != subpass_idx)
         continue;

      if (dep->flags & VK_DEPENDENCY_VIEW_LOCAL_BIT) {
         /* From the Vulkan 1.3.204 spec:
          *
          *    VUID-VkSubpassDependency2-dependencyFlags-03091
          *
          *    "If dependencyFlags includes VK_DEPENDENCY_VIEW_LOCAL_BIT,
          *    dstSubpass must not be equal to VK_SUBPASS_EXTERNAL"
          */
         assert(dep->src_subpass != VK_SUBPASS_EXTERNAL);

         assert(dep->src_subpass < pass->subpass_count);
         const struct vk_subpass *src_subpass =
            &pass->subpasses[dep->src_subpass];

         /* Figure out the set of views in the source subpass affected by this
          * dependency.
          */
         uint32_t src_dep_view_mask = subpass->view_mask;
         if (dep->view_offset >= 0)
            src_dep_view_mask <<= dep->view_offset;
         else
            src_dep_view_mask >>= -dep->view_offset;

         /* From the Vulkan 1.3.204 spec:
          *
          *    "If the dependency is view-local, then each view (dstView) in
          *    the destination subpass depends on the view dstView +
          *    pViewOffsets[dependency] in the source subpass. If there is not
          *    such a view in the source subpass, then this dependency does
          *    not affect that view in the destination subpass."
          */
         if (!(src_subpass->view_mask & src_dep_view_mask))
            continue;
      }

      mem_barriers[mem_barrier_count++] = (VkMemoryBarrier2){
         .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
         .srcStageMask = dep->src_stage_mask,
         .srcAccessMask = dep->src_access_mask,
         .dstStageMask = dep->dst_stage_mask,
         .dstAccessMask = dep->dst_access_mask,
      };
      external_dependency |= (dep->src_subpass == VK_SUBPASS_EXTERNAL);
   }

   uint32_t max_image_barrier_count = 0;
   for (uint32_t a = 0; a < subpass->attachment_count; a++) {
      const struct vk_subpass_attachment *sp_att = &subpass->attachments[a];
      if (sp_att->attachment == VK_ATTACHMENT_UNUSED)
         continue;

      assert(sp_att->attachment < pass->attachment_count);
      const struct vk_render_pass_attachment *rp_att =
         &pass->attachments[sp_att->attachment];

      max_image_barrier_count += util_bitcount(subpass->view_mask) *
                                 util_bitcount(rp_att->aspects);
   }
   if (pass->fragment_density_map.attachment != VK_ATTACHMENT_UNUSED)
      max_image_barrier_count += util_bitcount(subpass->view_mask);
   STACK_ARRAY(VkImageMemoryBarrier2, image_barriers, max_image_barrier_count);
   uint32_t image_barrier_count = 0;
   bool has_layout_transition = false;

   for (uint32_t a = 0; a < subpass->attachment_count; a++) {
      const struct vk_subpass_attachment *sp_att = &subpass->attachments[a];
      if (sp_att->attachment == VK_ATTACHMENT_UNUSED)
         continue;

      /* If we're using an initial layout, the attachment will already be
       * marked as transitioned and this will be a no-op.
       */
      uint32_t transitioned_views =
         transition_attachment(cmd_buffer, sp_att->attachment,
                               subpass->view_mask,
                               sp_att->layout, sp_att->stencil_layout,
                               &image_barrier_count,
                               max_image_barrier_count,
                               image_barriers);

      has_layout_transition |= (sp_att->first_subpass & transitioned_views) != 0;
   }
   if (pass->fragment_density_map.attachment != VK_ATTACHMENT_UNUSED) {
      transition_attachment(cmd_buffer, pass->fragment_density_map.attachment,
                            subpass->view_mask,
                            pass->fragment_density_map.layout,
                            VK_IMAGE_LAYOUT_UNDEFINED,
                            &image_barrier_count,
                            max_image_barrier_count,
                            image_barriers);
   }
   assert(image_barrier_count <= max_image_barrier_count);

   if (has_layout_transition && !external_dependency) {
      /* From the Vulkan 1.3.232 spec:
       *
       *    "If there is no subpass dependency from VK_SUBPASS_EXTERNAL to the
       *    first subpass that uses an attachment, then an implicit subpass
       *    dependency exists from VK_SUBPASS_EXTERNAL to the first subpass it
       *    is used in. The implicit subpass dependency only exists if there
       *    exists an automatic layout transition away from initialLayout. The
       *    subpass dependency operates as if defined with the following
       *    parameters:
       *
       *    VkSubpassDependency implicitDependency = {
       *        .srcSubpass = VK_SUBPASS_EXTERNAL;
       *        .dstSubpass = firstSubpass; // First subpass attachment is used in
       *        .srcStageMask = VK_PIPELINE_STAGE_NONE;
       *        .dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
       *        .srcAccessMask = 0;
       *        .dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
       *                         VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
       *                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
       *                         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
       *                         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
       *        .dependencyFlags = 0;
       *    };"
       */
      mem_barriers[mem_barrier_count++] = (VkMemoryBarrier2){
         .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
         .srcStageMask = VK_PIPELINE_STAGE_2_NONE,
         .srcAccessMask = VK_ACCESS_2_NONE,
         .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
         .dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT |
                          VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT |
                          VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
                          VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
                          VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
      };
   }

   if (mem_barrier_count > 0 || image_barrier_count > 0) {
      const VkDependencyInfo dependency_info = {
         .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
         .dependencyFlags = 0,
         .memoryBarrierCount = mem_barrier_count,
         .pMemoryBarriers = mem_barrier_count > 0 ? mem_barriers : NULL,
         .imageMemoryBarrierCount = image_barrier_count,
         .pImageMemoryBarriers = image_barrier_count > 0 ? image_barriers : NULL,
      };
      cmd_buffer->runtime_rp_barrier = true;
      disp->CmdPipelineBarrier2(vk_command_buffer_to_handle(cmd_buffer),
                                &dependency_info);
      cmd_buffer->runtime_rp_barrier = false;
   }

   STACK_ARRAY_FINISH(image_barriers);
   STACK_ARRAY_FINISH(mem_barriers);

   /* Next, handle any VK_ATTACHMENT_LOAD_OP_CLEAR that we couldn't handle
    * directly by emitting a quick vkCmdBegin/EndRendering to do the load.
    */
   for (uint32_t a = 0; a < subpass->attachment_count; a++) {
      const struct vk_subpass_attachment *sp_att = &subpass->attachments[a];
      if (sp_att->attachment == VK_ATTACHMENT_UNUSED)
         continue;

      load_attachment(cmd_buffer, sp_att->attachment, subpass->view_mask,
                      sp_att->layout, sp_att->stencil_layout);
   }

   /* TODO: Handle preserve attachments
    *
    * For immediate renderers, this isn't a big deal as LOAD_OP_LOAD and
    * STORE_OP_STORE are effectively free.  However, before this gets used on
    * a tiling GPU, we should really hook up preserve attachments and use them
    * to determine when we can use LOAD/STORE_OP_DONT_CARE between subpasses.
    */

   VkRenderingInfo rendering = {
      .sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
      .flags = VK_RENDERING_LOCAL_READ_CONCURRENT_ACCESS_CONTROL_BIT_KHR,
      .renderArea = cmd_buffer->render_area,
      .layerCount = pass->is_multiview ? 1 : framebuffer->layers,
      .viewMask = pass->is_multiview ? subpass->view_mask : 0,
      .colorAttachmentCount = subpass->color_count,
      .pColorAttachments = color_attachments,
      .pDepthAttachment = &depth_attachment,
      .pStencilAttachment = &stencil_attachment,
   };

   if (subpass->legacy_dithering_enabled)
      rendering.flags |= VK_RENDERING_ENABLE_LEGACY_DITHERING_BIT_EXT;

   VkRenderingFragmentShadingRateAttachmentInfoKHR fsr_attachment;
   if (subpass->fragment_shading_rate_attachment) {
      const struct vk_subpass_attachment *sp_att =
         subpass->fragment_shading_rate_attachment;

      assert(sp_att->attachment < pass->attachment_count);
      struct vk_attachment_state *att_state =
         &cmd_buffer->attachments[sp_att->attachment];

      /* Fragment shading rate attachments have no loadOp (it's implicitly
       * LOAD_OP_LOAD) so we need to ensure the load op happens.
       */
      load_attachment(cmd_buffer, sp_att->attachment, subpass->view_mask,
                      sp_att->layout, sp_att->stencil_layout);

      fsr_attachment = (VkRenderingFragmentShadingRateAttachmentInfoKHR) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR,
         .imageView = vk_image_view_to_handle(att_state->image_view),
         .imageLayout = sp_att->layout,
         .shadingRateAttachmentTexelSize =
            subpass->fragment_shading_rate_attachment_texel_size,
      };
      __vk_append_struct(&rendering, &fsr_attachment);
   }

   VkRenderingFragmentDensityMapAttachmentInfoEXT fdm_attachment;
   if (pass->fragment_density_map.attachment != VK_ATTACHMENT_UNUSED) {
      assert(pass->fragment_density_map.attachment < pass->attachment_count);
      struct vk_attachment_state *att_state =
         &cmd_buffer->attachments[pass->fragment_density_map.attachment];

      /* From the Vulkan 1.3.125 spec:
       *
       *    VUID-VkRenderPassFragmentDensityMapCreateInfoEXT-fragmentDensityMapAttachment-02550
       *
       *    If fragmentDensityMapAttachment is not VK_ATTACHMENT_UNUSED,
       *    fragmentDensityMapAttachment must reference an attachment with a
       *    loadOp equal to VK_ATTACHMENT_LOAD_OP_LOAD or
       *    VK_ATTACHMENT_LOAD_OP_DONT_CARE
       *
       * This means we don't have to implement the load op.
       */

      fdm_attachment = (VkRenderingFragmentDensityMapAttachmentInfoEXT) {
         .sType = VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT,
         .imageView = vk_image_view_to_handle(att_state->image_view),
         .imageLayout = pass->fragment_density_map.layout,
      };
      __vk_append_struct(&rendering, &fdm_attachment);
   }

   VkSampleLocationsInfoEXT sample_locations_tmp;
   if (sample_locations) {
      sample_locations_tmp = *sample_locations;
      __vk_append_struct(&rendering, &sample_locations_tmp);
   }

   /* Append this one last because it lives in the subpass and we don't want
    * to be changed by appending other structures later.
    */
   if (subpass->mrtss.multisampledRenderToSingleSampledEnable)
      __vk_append_struct(&rendering, (void *)&subpass->mrtss);

   disp->CmdBeginRendering(vk_command_buffer_to_handle(cmd_buffer),
                           &rendering);

   if (disp->CmdSetRenderingInputAttachmentIndices) {
      /* From the Vulkan 1.4.312 spec:
       * "
       * Until this command is called, mappings in the command buffer state
       * are treated as each color attachment specified in vkCmdBeginRendering
       * mapping to subpass inputs with a InputAttachmentIndex equal to its
       * index in VkRenderingInfo::pColorAttachments, and depth/stencil
       * attachments mapping to input attachments without these decorations.
       * This state is reset whenever vkCmdBeginRendering is called.
       * "
       *
       * In practice, CmdBindPipeline() should apply exactly the same
       * state to the vk_command_buffer dynamic state, and that's exactly
       * what the Vulkan spec wants:
       *
       * "
       * This command sets the input attachment index mappings for subsequent
       * drawing commands, and must match the mappings provided to the bound
       * pipeline, if one is bound, which can be set by chaining
       * VkRenderingInputAttachmentIndexInfo to VkGraphicsPipelineCreateInfo.
       * "
       *
       * So I'm not sure this CmdSetRenderingInputAttachmentIndices() is
       * really needed, but let's keep it to play by the rules.
       */
      disp->CmdSetRenderingInputAttachmentIndices(vk_command_buffer_to_handle(cmd_buffer),
                                                  &subpass->ial.info);
   }

   STACK_ARRAY_FINISH(color_attachments);
   STACK_ARRAY_FINISH(color_attachment_initial_layouts);
}

static void
end_subpass(struct vk_command_buffer *cmd_buffer,
            const VkSubpassEndInfo *end_info)
{
   const struct vk_render_pass *pass = cmd_buffer->render_pass;
   const uint32_t subpass_idx = cmd_buffer->subpass_idx;
   assert(subpass_idx < pass->subpass_count);
   const struct vk_subpass *subpass = &pass->subpasses[subpass_idx];
   struct vk_device_dispatch_table *disp =
      &cmd_buffer->base.device->dispatch_table;

   disp->CmdEndRendering(vk_command_buffer_to_handle(cmd_buffer));

   /* At most all dependencies will need a barrier, and we might have an
    * implicit one. */
   STACK_ARRAY(VkMemoryBarrier2, mem_barriers, pass->dependency_count + 1);
   uint32_t mem_barrier_count = 0;
   for (uint32_t d = 0; d < pass->dependency_count; d++) {
      const struct vk_subpass_dependency *dep = &pass->dependencies[d];
      if (dep->src_subpass != subpass_idx)
         continue;

      if (dep->dst_subpass != VK_SUBPASS_EXTERNAL)
         continue;

      mem_barriers[mem_barrier_count++] = (VkMemoryBarrier2){
         .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
         .srcStageMask = dep->src_stage_mask,
         .srcAccessMask = dep->src_access_mask,
         .dstStageMask = dep->dst_stage_mask,
         .dstAccessMask = dep->dst_access_mask,
      };
   }

   /* If we have a barrier, we have an external dependency */
   bool external_dependency = mem_barrier_count > 0;

   if (!external_dependency) {
      bool has_layout_transition = false;
      for (uint32_t a = 0; a < subpass->attachment_count; a++) {
         const struct vk_subpass_attachment *sp_att = &subpass->attachments[a];
         if (sp_att->attachment == VK_ATTACHMENT_UNUSED)
            continue;

         const struct vk_render_pass_attachment *rp_att =
            &pass->attachments[sp_att->attachment];
         uint32_t view_mask = transition_view_mask(cmd_buffer, sp_att->attachment,
                                                   subpass->view_mask,
                                                   rp_att->final_layout,
                                                   rp_att->final_stencil_layout);

         has_layout_transition |= (sp_att->last_subpass & view_mask) != 0;
      }

      /* From the Vulkan 1.3.232 spec:
       *
       *    "Similarly, if there is no subpass dependency from the last
       *    subpass that uses an attachment to VK_SUBPASS_EXTERNAL, then an
       *    implicit subpass dependency exists from the last subpass it is
       *    used in to VK_SUBPASS_EXTERNAL. The implicit subpass dependency
       *    only exists if there exists an automatic layout transition into
       *    finalLayout. The subpass dependency operates as if defined with
       *    the following parameters:
       *
       *    VkSubpassDependency implicitDependency = {
       *        .srcSubpass = lastSubpass; // Last subpass attachment is used in
       *        .dstSubpass = VK_SUBPASS_EXTERNAL;
       *        .srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
       *        .dstStageMask = VK_PIPELINE_STAGE_NONE;
       *        .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
       *                         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
       *        .dstAccessMask = 0;
       *        .dependencyFlags = 0;
       *    };"
       */
      if (has_layout_transition) {
         mem_barriers[mem_barrier_count++] = (VkMemoryBarrier2){
            .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
            .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
            .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
                             VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
            .dstStageMask = VK_PIPELINE_STAGE_2_NONE,
            .dstAccessMask = VK_ACCESS_2_NONE,
         };
      }
   }

   if (mem_barrier_count > 0) {
      const VkDependencyInfo dependency_info = {
         .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
         .dependencyFlags = 0,
         .memoryBarrierCount = mem_barrier_count,
         .pMemoryBarriers = mem_barriers,
      };
      cmd_buffer->runtime_rp_barrier = true;
      disp->CmdPipelineBarrier2(vk_command_buffer_to_handle(cmd_buffer),
                                &dependency_info);
      cmd_buffer->runtime_rp_barrier = false;
   }

   STACK_ARRAY_FINISH(mem_barriers);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdBeginRenderPass2(VkCommandBuffer commandBuffer,
                              const VkRenderPassBeginInfo *pRenderPassBeginInfo,
                              const VkSubpassBeginInfo *pSubpassBeginInfo)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);
   VK_FROM_HANDLE(vk_render_pass, pass, pRenderPassBeginInfo->renderPass);
   VK_FROM_HANDLE(vk_framebuffer, framebuffer,
                  pRenderPassBeginInfo->framebuffer);

   assert(cmd_buffer->render_pass == NULL);
   cmd_buffer->render_pass = pass;
   cmd_buffer->subpass_idx = 0;

   assert(cmd_buffer->framebuffer == NULL);
   cmd_buffer->framebuffer = framebuffer;

   cmd_buffer->render_area = pRenderPassBeginInfo->renderArea;

   assert(cmd_buffer->attachments == NULL);
   if (pass->attachment_count > ARRAY_SIZE(cmd_buffer->_attachments)) {
      cmd_buffer->attachments = malloc(pass->attachment_count *
                                       sizeof(*cmd_buffer->attachments));
   } else {
      cmd_buffer->attachments = cmd_buffer->_attachments;
   }

   const VkRenderPassAttachmentBeginInfo *attach_begin =
      vk_find_struct_const(pRenderPassBeginInfo,
                           RENDER_PASS_ATTACHMENT_BEGIN_INFO);
   if (!attach_begin)
      assert(pass->attachment_count == framebuffer->attachment_count);

   const VkImageView *image_views;
   if (attach_begin && attach_begin->attachmentCount != 0) {
      assert(attach_begin->attachmentCount == pass->attachment_count);
      image_views = attach_begin->pAttachments;
   } else {
      assert(framebuffer->attachment_count >= pass->attachment_count);
      image_views = framebuffer->attachments;
   }

   for (uint32_t a = 0; a < pass->attachment_count; ++a) {
      VK_FROM_HANDLE(vk_image_view, image_view, image_views[a]);
      const struct vk_render_pass_attachment *pass_att = &pass->attachments[a];
      struct vk_attachment_state *att_state = &cmd_buffer->attachments[a];

      /* From the Vulkan 1.3.204 spec:
       *
       *    VUID-VkFramebufferCreateInfo-pAttachments-00880
       *
       *    "If renderpass is not VK_NULL_HANDLE and flags does not include
       *    VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each element of pAttachments
       *    must have been created with a VkFormat value that matches the
       *    VkFormat specified by the corresponding VkAttachmentDescription in
       *    renderPass"
       *
       * and
       *
       *    VUID-VkRenderPassBeginInfo-framebuffer-03216
       *
       *    "If framebuffer was created with a VkFramebufferCreateInfo::flags
       *    value that included VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each
       *    element of the pAttachments member of a
       *    VkRenderPassAttachmentBeginInfo structure included in the pNext
       *    chain must be a VkImageView of an image created with a value of
       *    VkImageViewCreateInfo::format equal to the corresponding value of
       *    VkAttachmentDescription::format in renderPass"
       */
      if (!pass_att->has_external_format)
         assert(image_view->format == pass_att->format);

      /* From the Vulkan 1.3.204 spec:
       *
       *    VUID-VkFramebufferCreateInfo-pAttachments-00881
       *
       *    "If renderpass is not VK_NULL_HANDLE and flags does not include
       *    VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each element of pAttachments
       *    must have been created with a samples value that matches the
       *    samples value specified by the corresponding
       *    VkAttachmentDescription in renderPass"
       *
       * and
       *
       *    UID-VkRenderPassBeginInfo-framebuffer-03217
       *
       *    "If framebuffer was created with a VkFramebufferCreateInfo::flags
       *    value that included VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each
       *    element of the pAttachments member of a
       *    VkRenderPassAttachmentBeginInfo structure included in the pNext
       *    chain must be a VkImageView of an image created with a value of
       *    VkImageCreateInfo::samples equal to the corresponding value of
       *    VkAttachmentDescription::samples in renderPass"
       */
      assert(image_view->image->samples == pass_att->samples);

      /* From the Vulkan 1.3.204 spec:
       *
       *    If multiview is enabled and the shading rate attachment has
       *    multiple layers, the shading rate attachment texel is selected
       *    from the layer determined by the ViewIndex built-in. If multiview
       *    is disabled, and both the shading rate attachment and the
       *    framebuffer have multiple layers, the shading rate attachment
       *    texel is selected from the layer determined by the Layer built-in.
       *    Otherwise, the texel is unconditionally selected from the first
       *    layer of the attachment.
       */
      if (!(image_view->usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR))
         assert(util_last_bit(pass_att->view_mask) <= image_view->layer_count);

      *att_state = (struct vk_attachment_state) {
         .image_view = image_view,
         .views_loaded = 0,
      };

      for (uint32_t v = 0; v < MESA_VK_MAX_MULTIVIEW_VIEW_COUNT; v++) {
         att_state->views[v] = (struct vk_attachment_view_state) {
            .layout = pass_att->initial_layout,
            .stencil_layout = pass_att->initial_stencil_layout,
         };
      }

      if (a < pRenderPassBeginInfo->clearValueCount)
         att_state->clear_value = pRenderPassBeginInfo->pClearValues[a];
   }

   const VkRenderPassSampleLocationsBeginInfoEXT *rp_sl_info =
      vk_find_struct_const(pRenderPassBeginInfo->pNext,
                           RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT);
   if (rp_sl_info) {
      cmd_buffer->pass_sample_locations = clone_rp_sample_locations(rp_sl_info);
      assert(cmd_buffer->pass_sample_locations);

      for (uint32_t i = 0; i < rp_sl_info->attachmentInitialSampleLocationsCount; i++) {
         const VkAttachmentSampleLocationsEXT *att_sl =
            &rp_sl_info->pAttachmentInitialSampleLocations[i];

         assert(att_sl->attachmentIndex < pass->attachment_count);
         struct vk_attachment_state *att_state =
            &cmd_buffer->attachments[att_sl->attachmentIndex];

         /* Sample locations only matter for depth/stencil images created with
          * VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT
          */
         if (vk_format_is_depth_or_stencil(att_state->image_view->format) &&
             (att_state->image_view->image->create_flags &
              VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT)) {
            for (uint32_t v = 0; v < MESA_VK_MAX_MULTIVIEW_VIEW_COUNT; v++)
               att_state->views[v].sample_locations = &att_sl->sampleLocationsInfo;
         }
      }
   }

   begin_subpass(cmd_buffer, pSubpassBeginInfo);
}

void
vk_command_buffer_reset_render_pass(struct vk_command_buffer *cmd_buffer)
{
   cmd_buffer->render_pass = NULL;
   cmd_buffer->subpass_idx = 0;
   cmd_buffer->framebuffer = NULL;
   if (cmd_buffer->attachments != cmd_buffer->_attachments)
      free(cmd_buffer->attachments);
   cmd_buffer->attachments = NULL;
   if (cmd_buffer->pass_sample_locations != NULL)
      vk_free(vk_default_allocator(), cmd_buffer->pass_sample_locations);
   cmd_buffer->pass_sample_locations = NULL;
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdNextSubpass2(VkCommandBuffer commandBuffer,
                          const VkSubpassBeginInfo *pSubpassBeginInfo,
                          const VkSubpassEndInfo *pSubpassEndInfo)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);

   end_subpass(cmd_buffer, pSubpassEndInfo);
   cmd_buffer->subpass_idx++;
   begin_subpass(cmd_buffer, pSubpassBeginInfo);
}

VKAPI_ATTR void VKAPI_CALL
vk_common_CmdEndRenderPass2(VkCommandBuffer commandBuffer,
                            const VkSubpassEndInfo *pSubpassEndInfo)
{
   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);
   const struct vk_render_pass *pass = cmd_buffer->render_pass;
   struct vk_device_dispatch_table *disp =
      &cmd_buffer->base.device->dispatch_table;

   end_subpass(cmd_buffer, pSubpassEndInfo);

   /* Make sure all our attachments end up in their finalLayout */

   uint32_t max_image_barrier_count = 0;
   for (uint32_t a = 0; a < pass->attachment_count; a++) {
      const struct vk_render_pass_attachment *rp_att = &pass->attachments[a];

      max_image_barrier_count += util_bitcount(pass->view_mask) *
                                 util_bitcount(rp_att->aspects);
   }
   STACK_ARRAY(VkImageMemoryBarrier2, image_barriers, max_image_barrier_count);
   uint32_t image_barrier_count = 0;

   for (uint32_t a = 0; a < pass->attachment_count; a++) {
      const struct vk_render_pass_attachment *rp_att = &pass->attachments[a];

      transition_attachment(cmd_buffer, a, pass->view_mask,
                            rp_att->final_layout,
                            rp_att->final_stencil_layout,
                            &image_barrier_count,
                            max_image_barrier_count,
                            image_barriers);
   }
   assert(image_barrier_count <= max_image_barrier_count);

   if (image_barrier_count > 0) {
      const VkDependencyInfo dependency_info = {
         .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
         .dependencyFlags = 0,
         .imageMemoryBarrierCount = image_barrier_count,
         .pImageMemoryBarriers = image_barriers,
      };
      cmd_buffer->runtime_rp_barrier = true;
      disp->CmdPipelineBarrier2(vk_command_buffer_to_handle(cmd_buffer),
                                &dependency_info);
      cmd_buffer->runtime_rp_barrier = false;
   }

   STACK_ARRAY_FINISH(image_barriers);

   vk_command_buffer_reset_render_pass(cmd_buffer);
}
