/*
 * Copyright © 2016 Red Hat.
 * Copyright © 2016 Bas Nieuwenhuizen
 *
 * based in part on anv driver which is:
 * Copyright © 2015 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 */

#include "vk_log.h"

#include "radv_buffer.h"
#include "radv_buffer_view.h"
#include "radv_debug.h"
#include "radv_entrypoints.h"
#include "radv_formats.h"
#include "radv_image.h"
#include "radv_image_view.h"

#include "ac_descriptors.h"
#include "ac_formats.h"
#include "gfx10_format_table.h"

static unsigned
radv_tex_dim(VkImageType image_type, VkImageViewType view_type, unsigned nr_layers, unsigned nr_samples,
             bool is_storage_image, bool gfx9)
{
   if (view_type == VK_IMAGE_VIEW_TYPE_CUBE || view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
      return is_storage_image ? V_008F1C_SQ_RSRC_IMG_2D_ARRAY : V_008F1C_SQ_RSRC_IMG_CUBE;

   /* GFX9 allocates 1D textures as 2D. */
   if (gfx9 && image_type == VK_IMAGE_TYPE_1D)
      image_type = VK_IMAGE_TYPE_2D;
   switch (image_type) {
   case VK_IMAGE_TYPE_1D:
      return nr_layers > 1 ? V_008F1C_SQ_RSRC_IMG_1D_ARRAY : V_008F1C_SQ_RSRC_IMG_1D;
   case VK_IMAGE_TYPE_2D:
      if (nr_samples > 1)
         return nr_layers > 1 ? V_008F1C_SQ_RSRC_IMG_2D_MSAA_ARRAY : V_008F1C_SQ_RSRC_IMG_2D_MSAA;
      else
         return nr_layers > 1 ? V_008F1C_SQ_RSRC_IMG_2D_ARRAY : V_008F1C_SQ_RSRC_IMG_2D;
   case VK_IMAGE_TYPE_3D:
      if (view_type == VK_IMAGE_VIEW_TYPE_3D)
         return V_008F1C_SQ_RSRC_IMG_3D;
      else
         return V_008F1C_SQ_RSRC_IMG_2D_ARRAY;
   default:
      UNREACHABLE("illegal image type");
   }
}

void
radv_set_mutable_tex_desc_fields(struct radv_device *device, struct radv_image *image,
                                 const struct legacy_surf_level *base_level_info, unsigned plane_id,
                                 unsigned base_level, unsigned first_level, unsigned block_width, bool is_stencil,
                                 bool is_storage_image, bool disable_compression, bool enable_write_compression,
                                 uint32_t *state, const struct ac_surf_nbc_view *nbc_view, uint64_t offset)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   struct radv_image_plane *plane = &image->planes[plane_id];
   const uint32_t bind_idx = image->disjoint ? plane_id : 0;
   struct radv_image_binding *binding = &image->bindings[bind_idx];
   uint64_t gpu_address = binding->bo ? image->bindings[bind_idx].addr + offset : 0;
   const bool dcc_enabled = pdev->info.gfx_level >= GFX12 || radv_dcc_enabled(image, first_level);

   const struct ac_mutable_tex_state ac_state = {
      .surf = &plane->surface,
      .va = gpu_address,
      .gfx10 =
         {
            .nbc_view = nbc_view,
            .write_compress_enable = dcc_enabled && is_storage_image && enable_write_compression,
            .iterate_256 = radv_image_get_iterate256(device, image),
         },
      .gfx6 =
         {
            .base_level_info = base_level_info,
            .base_level = base_level,
            .block_width = block_width,
         },
      .is_stencil = is_stencil,
      .dcc_enabled = !disable_compression && dcc_enabled,
      .tc_compat_htile_enabled = !disable_compression && radv_tc_compat_htile_enabled(image, first_level),
   };

   ac_set_mutable_tex_desc_fields(&pdev->info, &ac_state, state);
}

/**
 * Build the sampler view descriptor for a texture (GFX10).
 */
static void
gfx10_make_texture_descriptor(struct radv_device *device, struct radv_image *image, bool is_storage_image,
                              VkImageViewType view_type, VkFormat vk_format, const VkComponentMapping *mapping,
                              unsigned first_level, unsigned last_level, unsigned first_layer, unsigned last_layer,
                              unsigned width, unsigned height, unsigned depth, float min_lod, uint32_t *state,
                              uint32_t *fmask_state, const struct ac_surf_nbc_view *nbc_view,
                              const VkImageViewSlicedCreateInfoEXT *sliced_3d)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   const bool create_2d_view_of_3d =
      (image->vk.create_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT) && view_type == VK_IMAGE_VIEW_TYPE_2D;
   enum pipe_format format = radv_format_to_pipe_format(vk_format);
   const struct util_format_description *desc;
   enum pipe_swizzle swizzle[4];
   unsigned array_pitch = 0;
   unsigned type;

   /* For emulated ETC2 without alpha we need to override the format to a 3-componenent format, so
    * that border colors work correctly (alpha forced to 1). Since Vulkan has no such format,
    * this uses the Gallium formats to set the description. */
   if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK && format == PIPE_FORMAT_R8G8B8A8_UNORM) {
      format = PIPE_FORMAT_R8G8B8X8_UNORM;
   } else if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK && format == PIPE_FORMAT_R8G8B8A8_SRGB) {
      format = PIPE_FORMAT_R8G8B8X8_SRGB;
   }

   desc = util_format_description(format);

   radv_compose_swizzle(desc, mapping, swizzle);

   if (create_2d_view_of_3d) {
      assert(image->vk.image_type == VK_IMAGE_TYPE_3D);
      type = V_008F1C_SQ_RSRC_IMG_3D;
   } else {
      type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, image->vk.samples, is_storage_image,
                          pdev->info.gfx_level == GFX9);
   }

   if (type == V_008F1C_SQ_RSRC_IMG_1D_ARRAY) {
      height = 1;
      depth = image->vk.array_layers;
   } else if (type == V_008F1C_SQ_RSRC_IMG_2D_ARRAY || type == V_008F1C_SQ_RSRC_IMG_2D_MSAA_ARRAY) {
      if (view_type != VK_IMAGE_VIEW_TYPE_3D)
         depth = image->vk.array_layers;
   } else if (type == V_008F1C_SQ_RSRC_IMG_CUBE)
      depth = image->vk.array_layers / 6;

   if (create_2d_view_of_3d) {
      assert(type == V_008F1C_SQ_RSRC_IMG_3D);

      depth = !is_storage_image ? depth : u_minify(depth, first_level);
      array_pitch = is_storage_image;
   } else if (sliced_3d) {
      assert(type == V_008F1C_SQ_RSRC_IMG_3D && is_storage_image);

      const unsigned total = u_minify(depth, first_level);
      const unsigned slice_count = sliced_3d->sliceCount == VK_REMAINING_3D_SLICES_EXT
                                      ? MAX2(1, total - sliced_3d->sliceOffset)
                                      : sliced_3d->sliceCount;

      first_layer = sliced_3d->sliceOffset;
      depth = sliced_3d->sliceOffset + slice_count;
      array_pitch = 1;
   }

   const struct ac_texture_state tex_state = {
      .surf = &image->planes[0].surface,
      .format = format,
      .img_format = radv_format_to_pipe_format(image->vk.format),
      .width = width,
      .height = height,
      .depth = type == V_008F1C_SQ_RSRC_IMG_3D ? depth - 1 : last_layer,
      .type = type,
      .swizzle =
         {
            swizzle[0],
            swizzle[1],
            swizzle[2],
            swizzle[3],
         },
      .num_samples = image->vk.samples,
      .num_storage_samples = image->vk.samples,
      .first_level = first_level,
      .last_level = last_level,
      .num_levels = image->vk.mip_levels,
      .first_layer = first_layer,
      .last_layer = last_layer,
      .min_lod = min_lod,
      .gfx10 =
         {
            .nbc_view = nbc_view,
            .uav3d = array_pitch,
         },
      .dcc_enabled = radv_dcc_enabled(image, first_level),
      .tc_compat_htile_enabled = radv_tc_compat_htile_enabled(image, first_level),
   };

   ac_build_texture_descriptor(&pdev->info, &tex_state, &state[0]);

   /* Initialize the sampler view for FMASK. */
   if (fmask_state) {
      if (radv_image_has_fmask(image)) {
         assert(image->plane_count == 1);

         const struct ac_fmask_state ac_state = {
            .surf = &image->planes[0].surface,
            .va = image->bindings[0].addr,
            .width = width,
            .height = height,
            .depth = depth,
            .type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, 0, false, false),
            .first_layer = first_layer,
            .last_layer = last_layer,
            .num_samples = image->vk.samples,
            .num_storage_samples = image->vk.samples,
            .tc_compat_cmask = radv_image_is_tc_compat_cmask(image),
         };

         ac_build_fmask_descriptor(pdev->info.gfx_level, &ac_state, &fmask_state[0]);
      } else
         memset(fmask_state, 0, 8 * 4);
   }
}

/**
 * Build the sampler view descriptor for a texture (SI-GFX9)
 */
static void
gfx6_make_texture_descriptor(struct radv_device *device, struct radv_image *image, bool is_storage_image,
                             VkImageViewType view_type, VkFormat vk_format, const VkComponentMapping *mapping,
                             unsigned first_level, unsigned last_level, unsigned first_layer, unsigned last_layer,
                             unsigned width, unsigned height, unsigned depth, float min_lod, uint32_t *state,
                             uint32_t *fmask_state)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   const struct radv_instance *instance = radv_physical_device_instance(pdev);
   enum pipe_format format = radv_format_to_pipe_format(vk_format);
   const bool create_2d_view_of_3d =
      (image->vk.create_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT) && view_type == VK_IMAGE_VIEW_TYPE_2D;
   const struct util_format_description *desc;
   enum pipe_swizzle swizzle[4];
   unsigned type;

   /* For emulated ETC2 without alpha we need to override the format to a 3-componenent format, so
    * that border colors work correctly (alpha forced to 1). Since Vulkan has no such format,
    * this uses the Gallium formats to set the description. */
   if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK && format == PIPE_FORMAT_R8G8B8A8_UNORM) {
      format = PIPE_FORMAT_R8G8B8X8_UNORM;
   } else if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK && format == PIPE_FORMAT_R8G8B8A8_SRGB) {
      format = PIPE_FORMAT_R8G8B8X8_SRGB;
   }

   desc = util_format_description(format);

   radv_compose_swizzle(desc, mapping, swizzle);

   if (pdev->info.gfx_level == GFX9 && create_2d_view_of_3d) {
      assert(image->vk.image_type == VK_IMAGE_TYPE_3D);
      type = V_008F1C_SQ_RSRC_IMG_3D;
   } else {
      type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, image->vk.samples, is_storage_image,
                          pdev->info.gfx_level == GFX9);
   }

   if (type == V_008F1C_SQ_RSRC_IMG_1D_ARRAY) {
      height = 1;
      depth = image->vk.array_layers;
   } else if (type == V_008F1C_SQ_RSRC_IMG_2D_ARRAY || type == V_008F1C_SQ_RSRC_IMG_2D_MSAA_ARRAY) {
      if (view_type != VK_IMAGE_VIEW_TYPE_3D)
         depth = image->vk.array_layers;
   } else if (type == V_008F1C_SQ_RSRC_IMG_CUBE)
      depth = image->vk.array_layers / 6;

   const struct ac_texture_state tex_state = {
      .surf = &image->planes[0].surface,
      .format = format,
      .img_format = radv_format_to_pipe_format(image->vk.format),
      .width = width,
      .height = height,
      .depth = depth,
      .type = type,
      .swizzle =
         {
            swizzle[0],
            swizzle[1],
            swizzle[2],
            swizzle[3],
         },
      .num_samples = image->vk.samples,
      .num_storage_samples = image->vk.samples,
      .first_level = first_level,
      .last_level = last_level,
      .num_levels = image->vk.mip_levels,
      .first_layer = first_layer,
      .last_layer = last_layer,
      .min_lod = min_lod,
      .dcc_enabled = radv_dcc_enabled(image, first_level),
      .tc_compat_htile_enabled = radv_tc_compat_htile_enabled(image, first_level),
      .aniso_single_level = !instance->drirc.debug.disable_aniso_single_level,
   };

   ac_build_texture_descriptor(&pdev->info, &tex_state, &state[0]);

   /* Initialize the sampler view for FMASK. */
   if (fmask_state) {
      if (radv_image_has_fmask(image)) {
         assert(image->plane_count == 1);

         const struct ac_fmask_state ac_fmask_state = {
            .surf = &image->planes[0].surface,
            .va = image->bindings[0].addr,
            .width = width,
            .height = height,
            .depth = depth,
            .type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, 0, false, false),
            .first_layer = first_layer,
            .last_layer = last_layer,
            .num_samples = image->vk.samples,
            .num_storage_samples = image->vk.samples,
            .tc_compat_cmask = radv_image_is_tc_compat_cmask(image),
         };

         ac_build_fmask_descriptor(pdev->info.gfx_level, &ac_fmask_state, &fmask_state[0]);
      } else
         memset(fmask_state, 0, 8 * 4);
   }
}

void
radv_make_texture_descriptor(struct radv_device *device, struct radv_image *image, bool is_storage_image,
                             VkImageViewType view_type, VkFormat vk_format, const VkComponentMapping *mapping,
                             unsigned first_level, unsigned last_level, unsigned first_layer, unsigned last_layer,
                             unsigned width, unsigned height, unsigned depth, float min_lod, uint32_t *state,
                             uint32_t *fmask_state, const struct ac_surf_nbc_view *nbc_view,
                             const VkImageViewSlicedCreateInfoEXT *sliced_3d)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);

   if (pdev->info.gfx_level >= GFX10) {
      gfx10_make_texture_descriptor(device, image, is_storage_image, view_type, vk_format, mapping, first_level,
                                    last_level, first_layer, last_layer, width, height, depth, min_lod, state,
                                    fmask_state, nbc_view, sliced_3d);
   } else {
      gfx6_make_texture_descriptor(device, image, is_storage_image, view_type, vk_format, mapping, first_level,
                                   last_level, first_layer, last_layer, width, height, depth, min_lod, state,
                                   fmask_state);
   }
}

static inline void
compute_non_block_compressed_view(struct radv_device *device, const struct radv_image_view *iview,
                                  struct ac_surf_nbc_view *nbc_view)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   const struct radv_image *image = iview->image;
   const struct radeon_surf *surf = &image->planes[0].surface;
   struct ac_surf_info surf_info = radv_get_ac_surf_info(device, image);

   ac_surface_compute_nbc_view(pdev->addrlib, &pdev->info, surf, &surf_info, iview->vk.base_mip_level,
                               iview->vk.base_array_layer, nbc_view);
}

static void
radv_image_view_make_descriptor(struct radv_image_view *iview, struct radv_device *device, VkFormat vk_format,
                                const VkComponentMapping *components, bool is_storage_image, bool disable_compression,
                                bool enable_compression, unsigned plane_id, unsigned descriptor_plane_id,
                                const VkImageViewSlicedCreateInfoEXT *sliced_3d)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   struct radv_image *image = iview->image;
   struct radv_image_plane *plane = &image->planes[plane_id];
   bool is_stencil = iview->vk.aspects == VK_IMAGE_ASPECT_STENCIL_BIT;
   unsigned first_layer = iview->vk.base_array_layer;
   uint32_t blk_w;
   union radv_descriptor *descriptor;
   uint32_t hw_level = iview->vk.base_mip_level;
   bool force_zero_base_mip = false;
   uint64_t offset = 0;

   if (is_storage_image) {
      descriptor = &iview->storage_descriptor;
   } else {
      descriptor = &iview->descriptor;
   }

   assert(vk_format_get_plane_count(vk_format) == 1);
   assert(plane->surface.blk_w % vk_format_get_blockwidth(plane->format) == 0);
   blk_w = plane->surface.blk_w / vk_format_get_blockwidth(plane->format) * vk_format_get_blockwidth(vk_format);

   VkExtent3D extent = {
      .width = iview->extent.width,
      .height = iview->extent.height,
      .depth = iview->extent.depth,
   };

   if (pdev->info.gfx_level >= GFX9) {
      if (iview->nbc_view.valid) {
         hw_level = iview->nbc_view.level;

         /* Clear the base array layer because addrlib adds it as part of the base addr offset. */
         first_layer = 0;
      } else {
         /* Video decode target uses custom height alignment. */
         if (image->vk.usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR &&
             image->planes[plane_id].surface.u.gfx9.swizzle_mode == 0) {
            offset += first_layer * image->planes[plane_id].surface.u.gfx9.surf_slice_size;
            first_layer = 0;
         }
      }
   } else {
      /* On GFX6-8, there are some cases where the view must use mip0 and minified image sizes:
       * - storage descriptors
       * - block compressed images
       * - depth view of a depth/stencil image (ie. depth/stencil pitch adjustments)
       * - 2d view of a 3d image
       */
      if (is_storage_image) {
         force_zero_base_mip = true;
      } else if (vk_format_is_block_compressed(image->planes[plane_id].format)) {
         force_zero_base_mip = true;
      } else if (iview->vk.aspects == VK_IMAGE_ASPECT_DEPTH_BIT &&
                 (iview->image->vk.aspects == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) {
         force_zero_base_mip = true;
      } else if (iview->image->vk.create_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT &&
                 iview->vk.view_type == VK_IMAGE_VIEW_TYPE_2D) {
         force_zero_base_mip = true;
      }

      if (force_zero_base_mip) {
         hw_level = 0;
      } else {
         extent.width = image->vk.extent.width;
         extent.height = image->vk.extent.height;
         extent.depth = image->vk.extent.depth;
      }

      /* Video decode target uses custom height alignment. */
      if (image->vk.usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) {
         offset += first_layer * image->planes[plane_id].surface.u.legacy.level[0].slice_size_dw * 4;
         first_layer = 0;
      }
   }

   /* Special case for 96-bit formats that are viewed as 32-bit formats for internal operations. */
   if (vk_format_is_96bit(plane->format) && !vk_format_is_96bit(vk_format)) {
      extent.width *= 3;
      blk_w *= 3;
   }

   radv_make_texture_descriptor(
      device, image, is_storage_image, iview->vk.view_type, vk_format, components, hw_level,
      hw_level + iview->vk.level_count - 1, first_layer, iview->vk.base_array_layer + iview->vk.layer_count - 1,
      vk_format_get_plane_width(image->vk.format, plane_id, extent.width),
      vk_format_get_plane_height(image->vk.format, plane_id, extent.height), extent.depth, iview->vk.min_lod,
      descriptor->plane_descriptors[descriptor_plane_id],
      descriptor_plane_id || is_storage_image ? NULL : descriptor->fmask_descriptor, &iview->nbc_view, sliced_3d);

   const struct legacy_surf_level *base_level_info = NULL;
   if (pdev->info.gfx_level <= GFX8) {
      if (is_stencil)
         base_level_info =
            &plane->surface.u.legacy.zs.stencil_level[force_zero_base_mip ? iview->vk.base_mip_level : 0];
      else
         base_level_info = &plane->surface.u.legacy.level[force_zero_base_mip ? iview->vk.base_mip_level : 0];
   }

   bool enable_write_compression = radv_image_compress_dcc_on_image_stores(device, image);
   bool decompress_htile_on_image_stores = radv_image_decompress_htile_on_image_stores(device, image);

   if (is_storage_image && !(enable_write_compression || enable_compression || decompress_htile_on_image_stores))
      disable_compression = true;

   radv_set_mutable_tex_desc_fields(device, image, base_level_info, plane_id,
                                    force_zero_base_mip ? iview->vk.base_mip_level : 0, iview->vk.base_mip_level, blk_w,
                                    is_stencil, is_storage_image, disable_compression, enable_write_compression,
                                    descriptor->plane_descriptors[descriptor_plane_id], &iview->nbc_view, offset);
}

/**
 * Determine if the given image view can be fast cleared.
 */
static bool
radv_image_view_can_fast_clear(const struct radv_device *device, const struct radv_image_view *iview)
{
   struct radv_image *image;

   if (!iview)
      return false;
   image = iview->image;

   /* Only fast clear if the image itself can be fast cleared. */
   if (!radv_image_can_fast_clear(device, image))
      return false;

   /* Only fast clear if all layers are bound when comp-to-single isn't supported. */
   const bool all_slices = iview->vk.base_array_layer == 0 && iview->vk.layer_count == image->vk.array_layers;
   if (!all_slices && !image->support_comp_to_single)
      return false;

   /* Only fast clear if the view covers the whole image. */
   if (!radv_image_extent_compare(image, &iview->extent))
      return false;

   return true;
}

static uint32_t
radv_surface_max_layer_count(struct radv_image_view *iview)
{
   return iview->vk.view_type == VK_IMAGE_VIEW_TYPE_3D ? iview->extent.depth
                                                       : (iview->vk.base_array_layer + iview->vk.layer_count);
}

static void
radv_initialise_color_surface(struct radv_device *device, struct radv_color_buffer_info *cb,
                              struct radv_image_view *iview)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   const struct radv_instance *instance = radv_physical_device_instance(pdev);
   uint64_t va;
   const struct radv_image_plane *plane = &iview->image->planes[iview->plane_id];
   const struct radeon_surf *surf = &plane->surface;

   memset(cb, 0, sizeof(*cb));

   const unsigned num_layers =
      iview->image->vk.image_type == VK_IMAGE_TYPE_3D ? (iview->extent.depth - 1) : (iview->image->vk.array_layers - 1);

   const struct ac_cb_state cb_state = {
      .surf = surf,
      .format = radv_format_to_pipe_format(iview->vk.format),
      .width = vk_format_get_plane_width(iview->image->vk.format, iview->plane_id, iview->extent.width),
      .height = vk_format_get_plane_height(iview->image->vk.format, iview->plane_id, iview->extent.height),
      .first_layer = iview->vk.base_array_layer,
      .last_layer = radv_surface_max_layer_count(iview) - 1,
      .num_layers = num_layers,
      .num_samples = iview->image->vk.samples,
      .num_storage_samples = iview->image->vk.samples,
      .base_level = iview->vk.base_mip_level,
      .num_levels = iview->image->vk.mip_levels,
      .gfx10 =
         {
            .nbc_view = iview->nbc_view.valid ? &iview->nbc_view : NULL,
         },
   };

   ac_init_cb_surface(&pdev->info, &cb_state, &cb->ac);

   uint32_t plane_id = iview->image->disjoint ? iview->plane_id : 0;
   va = iview->image->bindings[plane_id].addr;

   const struct ac_mutable_cb_state mutable_cb_state = {
      .surf = surf,
      .cb = &cb->ac,
      .va = va,
      .base_level = iview->vk.base_mip_level,
      .num_samples = iview->image->vk.samples,
      .fmask_enabled = radv_image_has_fmask(iview->image),
      .cmask_enabled = radv_image_has_cmask(iview->image),
      .fast_clear_enabled = !(instance->debug_flags & RADV_DEBUG_NO_FAST_CLEARS),
      .tc_compat_cmask_enabled = radv_image_is_tc_compat_cmask(iview->image),
      .dcc_enabled = radv_dcc_enabled(iview->image, iview->vk.base_mip_level) &&
                     (pdev->info.gfx_level >= GFX11 || !iview->disable_dcc_mrt),
      .gfx10 =
         {
            .nbc_view = iview->nbc_view.valid ? &iview->nbc_view : NULL,
         },
   };

   ac_set_mutable_cb_surface_fields(&pdev->info, &mutable_cb_state, &cb->ac);
}

static void
radv_initialise_ds_surface(const struct radv_device *device, struct radv_ds_buffer_info *ds,
                           struct radv_image_view *iview, VkImageAspectFlags ds_aspects, bool depth_compress_disable,
                           bool stencil_compress_disable)
{
   const struct radv_physical_device *pdev = radv_device_physical(device);
   unsigned level = iview->vk.base_mip_level;
   bool stencil_only = iview->image->vk.format == VK_FORMAT_S8_UINT;

   assert(vk_format_get_plane_count(iview->image->vk.format) == 1);

   memset(ds, 0, sizeof(*ds));

   uint32_t max_slice = radv_surface_max_layer_count(iview) - 1;

   /* Recommended value for better performance with 4x and 8x. */
   ds->db_render_override2 = S_028010_DECOMPRESS_Z_ON_FLUSH(iview->image->vk.samples >= 4) |
                             S_028010_CENTROID_COMPUTATION_MODE(pdev->info.gfx_level >= GFX10_3);

   const struct ac_ds_state ds_state = {
      .surf = &iview->image->planes[0].surface,
      .va = iview->image->bindings[0].addr,
      .format = radv_format_to_pipe_format(iview->image->vk.format),
      .width = iview->image->vk.extent.width,
      .height = iview->image->vk.extent.height,
      .level = level,
      .num_levels = iview->image->vk.mip_levels,
      .num_samples = iview->image->vk.samples,
      .first_layer = iview->vk.base_array_layer,
      .last_layer = max_slice,
      .stencil_only = stencil_only,
      .z_read_only = !(ds_aspects & VK_IMAGE_ASPECT_DEPTH_BIT),
      .stencil_read_only = !(ds_aspects & VK_IMAGE_ASPECT_STENCIL_BIT),
      .htile_enabled = radv_htile_enabled(iview->image, level),
      .htile_stencil_disabled = radv_image_tile_stencil_disabled(device, iview->image),
      .vrs_enabled = radv_image_has_vrs_htile(device, iview->image),
   };

   ac_init_ds_surface(&pdev->info, &ds_state, &ds->ac);

   const struct ac_mutable_ds_state mutable_ds_state = {
      .ds = &ds->ac,
      .format = radv_format_to_pipe_format(iview->image->vk.format),
      .tc_compat_htile_enabled = radv_tc_compat_htile_enabled(iview->image, level),
      .zrange_precision = true,
      .no_d16_compression = true,
   };

   ac_set_mutable_ds_surface_fields(&pdev->info, &mutable_ds_state, &ds->ac);

   if (pdev->info.gfx_level >= GFX11) {
      radv_gfx11_set_db_render_control(device, iview->image->vk.samples, &ds->db_render_control);
   }

   /* For depth/stencil expand on graphics. */
   ds->db_render_control |= S_028000_DEPTH_COMPRESS_DISABLE(depth_compress_disable) |
                            S_028000_STENCIL_COMPRESS_DISABLE(stencil_compress_disable);
}

void
radv_initialise_vrs_surface(struct radv_image *image, struct radv_buffer *htile_buffer, struct radv_ds_buffer_info *ds)
{
   const struct radeon_surf *surf = &image->planes[0].surface;

   assert(image->vk.format == VK_FORMAT_D16_UNORM);
   memset(ds, 0, sizeof(*ds));

   ds->ac.db_z_info = S_028038_FORMAT(V_028040_Z_16) | S_028038_SW_MODE(surf->u.gfx9.swizzle_mode) |
                      S_028038_ZRANGE_PRECISION(1) | S_028038_TILE_SURFACE_ENABLE(1);
   ds->ac.db_stencil_info = S_02803C_FORMAT(V_028044_STENCIL_INVALID);

   ds->ac.db_depth_size = S_02801C_X_MAX(image->vk.extent.width - 1) | S_02801C_Y_MAX(image->vk.extent.height - 1);

   ds->ac.u.gfx6.db_htile_data_base = radv_buffer_get_va(htile_buffer->bo) >> 8;
   ds->ac.u.gfx6.db_htile_surface =
      S_028ABC_FULL_CACHE(1) | S_028ABC_PIPE_ALIGNED(1) | S_028ABC_VRS_HTILE_ENCODING(V_028ABC_VRS_HTILE_4BIT_ENCODING);
}

void
radv_image_view_init(struct radv_image_view *iview, struct radv_device *device,
                     const VkImageViewCreateInfo *pCreateInfo,
                     const struct radv_image_view_extra_create_info *extra_create_info)
{
   VK_FROM_HANDLE(radv_image, image, pCreateInfo->image);
   const struct radv_physical_device *pdev = radv_device_physical(device);
   uint32_t plane_count = 1;

   const struct VkImageViewSlicedCreateInfoEXT *sliced_3d =
      vk_find_struct_const(pCreateInfo->pNext, IMAGE_VIEW_SLICED_CREATE_INFO_EXT);

   if (!extra_create_info || !extra_create_info->from_client)
      assert(pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_DRIVER_INTERNAL_BIT_MESA);

   memset(iview, 0, sizeof(*iview));
   vk_image_view_init(&device->vk, &iview->vk, pCreateInfo);

   iview->image = image;
   iview->plane_id = radv_plane_from_aspect(pCreateInfo->subresourceRange.aspectMask);
   iview->nbc_view.valid = false;

   /* If the image has an Android external format, pCreateInfo->format will be
    * VK_FORMAT_UNDEFINED. */
   if (iview->vk.format == VK_FORMAT_UNDEFINED) {
      iview->vk.format = image->vk.format;
      iview->vk.view_format = image->vk.format;
   }

   /* Split out the right aspect. Note that for internal meta code we sometimes
    * use an equivalent color format for the aspect so we first have to check
    * if we actually got depth/stencil formats. */
   if (iview->vk.aspects == VK_IMAGE_ASPECT_STENCIL_BIT) {
      if (vk_format_has_stencil(iview->vk.view_format))
         iview->vk.view_format = vk_format_stencil_only(iview->vk.view_format);
   } else if (iview->vk.aspects == VK_IMAGE_ASPECT_DEPTH_BIT) {
      if (vk_format_has_depth(iview->vk.view_format))
         iview->vk.view_format = vk_format_depth_only(iview->vk.view_format);
   }

   if (vk_format_get_plane_count(image->vk.format) > 1 &&
       pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
      plane_count = vk_format_get_plane_count(iview->vk.format);
   }

   /* when the view format is emulated, redirect the view to the hidden plane 1 */
   if (radv_is_format_emulated(pdev, iview->vk.format)) {
      assert(radv_is_format_emulated(pdev, image->vk.format));
      iview->plane_id = 1;
      iview->vk.view_format = image->planes[iview->plane_id].format;
      iview->vk.format = image->planes[iview->plane_id].format;
      plane_count = 1;
   }

   if (pdev->info.gfx_level >= GFX9) {
      iview->extent = (VkExtent3D){
         .width = image->vk.extent.width,
         .height = image->vk.extent.height,
         .depth = image->vk.extent.depth,
      };
   } else {
      /* On GFX6-8, CB/DS surfaces use minified images sizes because the mip level can't be
       * specified in registers.
       */
      iview->extent = vk_image_mip_level_extent(&image->vk, iview->vk.base_mip_level);
   }

   if (iview->vk.format != image->planes[iview->plane_id].format) {
      const struct radv_image_plane *plane = &image->planes[iview->plane_id];
      unsigned view_bw = vk_format_get_blockwidth(iview->vk.format);
      unsigned view_bh = vk_format_get_blockheight(iview->vk.format);
      unsigned plane_bw = vk_format_get_blockwidth(plane->format);
      unsigned plane_bh = vk_format_get_blockheight(plane->format);

      iview->extent.width = DIV_ROUND_UP(iview->extent.width * view_bw, plane_bw);
      iview->extent.height = DIV_ROUND_UP(iview->extent.height * view_bh, plane_bh);

      /* Comment ported from amdvlk -
       * If we have the following image:
       *              Uncompressed pixels   Compressed block sizes (4x4)
       *      mip0:       22 x 22                   6 x 6
       *      mip1:       11 x 11                   3 x 3
       *      mip2:        5 x  5                   2 x 2
       *      mip3:        2 x  2                   1 x 1
       *      mip4:        1 x  1                   1 x 1
       *
       * On GFX9 the descriptor is always programmed with the WIDTH and HEIGHT of the base level and
       * the HW is calculating the degradation of the block sizes down the mip-chain as follows
       * (straight-up divide-by-two integer math): mip0:  6x6 mip1:  3x3 mip2:  1x1 mip3:  1x1
       *
       * This means that mip2 will be missing texels.
       *
       * Fix this by calculating the base mip's width and height, then convert
       * that, and round it back up to get the level 0 size. Clamp the
       * converted size between the original values, and the physical extent
       * of the base mipmap.
       *
       * On GFX10 we have to take care to not go over the physical extent
       * of the base mipmap as otherwise the GPU computes a different layout.
       * Note that the GPU does use the same base-mip dimensions for both a
       * block compatible format and the compressed format, so even if we take
       * the plain converted dimensions the physical layout is correct.
       */
      if (pdev->info.gfx_level >= GFX9 && vk_format_is_block_compressed(plane->format) &&
          !vk_format_is_block_compressed(iview->vk.format)) {
         /* If we have multiple levels in the view we should ideally take the last level,
          * but the mip calculation has a max(..., 1) so walking back to the base mip in an
          * useful way is hard. */
         if (iview->vk.level_count > 1) {
            iview->extent.width = plane->surface.u.gfx9.base_mip_width;
            iview->extent.height = plane->surface.u.gfx9.base_mip_height;
         } else {
            unsigned lvl_width = u_minify(image->vk.extent.width, iview->vk.base_mip_level);
            unsigned lvl_height = u_minify(image->vk.extent.height, iview->vk.base_mip_level);

            lvl_width = DIV_ROUND_UP(lvl_width * view_bw, plane_bw);
            lvl_height = DIV_ROUND_UP(lvl_height * view_bh, plane_bh);

            iview->extent.width =
               CLAMP(lvl_width << iview->vk.base_mip_level, iview->extent.width, plane->surface.u.gfx9.base_mip_width);
            iview->extent.height = CLAMP(lvl_height << iview->vk.base_mip_level, iview->extent.height,
                                         plane->surface.u.gfx9.base_mip_height);

            /* If the hardware-computed extent is still be too small, on GFX10
             * we can attempt another workaround provided by addrlib that
             * changes the descriptor's base level, and adjusts the address and
             * extents accordingly.
             */
            if (pdev->info.gfx_level >= GFX10 &&
                (u_minify(iview->extent.width, iview->vk.base_mip_level) < lvl_width ||
                 u_minify(iview->extent.height, iview->vk.base_mip_level) < lvl_height) &&
                iview->vk.layer_count == 1) {
               compute_non_block_compressed_view(device, iview, &iview->nbc_view);

               if (iview->nbc_view.valid) {
                  iview->extent.width = iview->nbc_view.width;
                  iview->extent.height = iview->nbc_view.height;
               }
            }
         }
      }
   }

   iview->support_fast_clear = radv_image_view_can_fast_clear(device, iview);
   iview->disable_dcc_mrt = extra_create_info ? extra_create_info->disable_dcc_mrt : false;
   iview->disable_tc_compat_cmask_mrt = extra_create_info ? extra_create_info->disable_tc_compat_cmask_mrt : false;

   const bool depth_compress_disable = extra_create_info ? extra_create_info->depth_compress_disable : false;
   const bool stencil_compress_disable = extra_create_info ? extra_create_info->stencil_compress_disable : false;
   bool disable_compression = extra_create_info ? extra_create_info->disable_compression : false;
   bool enable_compression = extra_create_info ? extra_create_info->enable_compression : false;
   for (unsigned i = 0; i < plane_count; ++i) {
      VkFormat format = vk_format_get_plane_format(iview->vk.view_format, i);
      radv_image_view_make_descriptor(iview, device, format, &pCreateInfo->components, false, disable_compression,
                                      enable_compression, iview->plane_id + i, i, NULL);
      radv_image_view_make_descriptor(iview, device, format, &pCreateInfo->components, true, disable_compression,
                                      enable_compression, iview->plane_id + i, i, sliced_3d);
   }

   if (iview->vk.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
      radv_initialise_color_surface(device, &iview->color_desc, iview);

   if (iview->vk.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
      if (vk_format_has_depth(image->vk.format) && vk_format_has_stencil(image->vk.format))
         radv_initialise_ds_surface(device, &iview->depth_stencil_desc, iview,
                                    VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, depth_compress_disable,
                                    stencil_compress_disable);
      if (vk_format_has_depth(image->vk.format))
         radv_initialise_ds_surface(device, &iview->depth_only_desc, iview, VK_IMAGE_ASPECT_DEPTH_BIT,
                                    depth_compress_disable, stencil_compress_disable);
      if (vk_format_has_stencil(image->vk.format))
         radv_initialise_ds_surface(device, &iview->stencil_only_desc, iview, VK_IMAGE_ASPECT_STENCIL_BIT,
                                    depth_compress_disable, stencil_compress_disable);
   }
}

void
radv_hiz_image_view_init(struct radv_image_view *iview, struct radv_device *device,
                         const VkImageViewCreateInfo *pCreateInfo)
{
   VK_FROM_HANDLE(radv_image, image, pCreateInfo->image);

   assert(pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_DRIVER_INTERNAL_BIT_MESA);

   memset(iview, 0, sizeof(*iview));
   vk_image_view_init(&device->vk, &iview->vk, pCreateInfo);

   assert(iview->vk.aspects == VK_IMAGE_ASPECT_DEPTH_BIT);

   iview->image = image;

   const uint32_t type =
      radv_tex_dim(image->vk.image_type, iview->vk.view_type, image->vk.array_layers, image->vk.samples, true, false);

   const struct ac_gfx12_hiz_state hiz_state = {
      .surf = &image->planes[0].surface,
      .va = image->bindings[0].addr,
      .type = type,
      .num_samples = image->vk.samples,
      .first_level = iview->vk.base_mip_level,
      .last_level = iview->vk.base_mip_level + iview->vk.level_count - 1,
      .num_levels = image->vk.mip_levels,
      .first_layer = iview->vk.base_array_layer,
      .last_layer = iview->vk.base_array_layer + iview->vk.layer_count - 1,
   };

   uint32_t *desc = iview->storage_descriptor.plane_descriptors[0];

   ac_build_gfx12_hiz_descriptor(&hiz_state, desc);
}

void
radv_image_view_finish(struct radv_image_view *iview)
{
   vk_image_view_finish(&iview->vk);
}

VKAPI_ATTR VkResult VKAPI_CALL
radv_CreateImageView(VkDevice _device, const VkImageViewCreateInfo *pCreateInfo,
                     const VkAllocationCallbacks *pAllocator, VkImageView *pView)
{
   VK_FROM_HANDLE(radv_device, device, _device);
   struct radv_image_view *view;

   view = vk_alloc2(&device->vk.alloc, pAllocator, sizeof(*view), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
   if (view == NULL)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   radv_image_view_init(view, device, pCreateInfo, &(struct radv_image_view_extra_create_info){.from_client = true});

   *pView = radv_image_view_to_handle(view);

   return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL
radv_DestroyImageView(VkDevice _device, VkImageView _iview, const VkAllocationCallbacks *pAllocator)
{
   VK_FROM_HANDLE(radv_device, device, _device);
   VK_FROM_HANDLE(radv_image_view, iview, _iview);

   if (!iview)
      return;

   radv_image_view_finish(iview);
   vk_free2(&device->vk.alloc, pAllocator, iview);
}
