v3dv: implement vkCreateImageView

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
diff --git a/src/broadcom/vulkan/v3dv_formats.c b/src/broadcom/vulkan/v3dv_formats.c
index 5f6a20a..f78b1d2 100644
--- a/src/broadcom/vulkan/v3dv_formats.c
+++ b/src/broadcom/vulkan/v3dv_formats.c
@@ -71,6 +71,136 @@
       return NULL;
 }
 
+void
+v3dv_get_internal_type_bpp_for_output_format(uint32_t format,
+                                             uint32_t *type,
+                                             uint32_t *bpp)
+{
+   switch (format) {
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA8:
+   case V3D_OUTPUT_IMAGE_FORMAT_RGB8:
+   case V3D_OUTPUT_IMAGE_FORMAT_RG8:
+   case V3D_OUTPUT_IMAGE_FORMAT_R8:
+   case V3D_OUTPUT_IMAGE_FORMAT_ABGR4444:
+   case V3D_OUTPUT_IMAGE_FORMAT_BGR565:
+   case V3D_OUTPUT_IMAGE_FORMAT_ABGR1555:
+      *type = V3D_INTERNAL_TYPE_8;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA8I:
+   case V3D_OUTPUT_IMAGE_FORMAT_RG8I:
+   case V3D_OUTPUT_IMAGE_FORMAT_R8I:
+      *type = V3D_INTERNAL_TYPE_8I;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA8UI:
+   case V3D_OUTPUT_IMAGE_FORMAT_RG8UI:
+   case V3D_OUTPUT_IMAGE_FORMAT_R8UI:
+      *type = V3D_INTERNAL_TYPE_8UI;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_SRGB8_ALPHA8:
+   case V3D_OUTPUT_IMAGE_FORMAT_SRGB:
+   case V3D_OUTPUT_IMAGE_FORMAT_RGB10_A2:
+   case V3D_OUTPUT_IMAGE_FORMAT_R11F_G11F_B10F:
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA16F:
+      /* Note that sRGB RTs are stored in the tile buffer at 16F,
+       * and the conversion to sRGB happens at tilebuffer load/store.
+       */
+      *type = V3D_INTERNAL_TYPE_16F;
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RG16F:
+   case V3D_OUTPUT_IMAGE_FORMAT_R16F:
+      *type = V3D_INTERNAL_TYPE_16F;
+      /* Use 64bpp to make sure the TLB doesn't throw away the alpha
+       * channel before alpha test happens.
+       */
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA16I:
+      *type = V3D_INTERNAL_TYPE_16I;
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RG16I:
+   case V3D_OUTPUT_IMAGE_FORMAT_R16I:
+      *type = V3D_INTERNAL_TYPE_16I;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGB10_A2UI:
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA16UI:
+      *type = V3D_INTERNAL_TYPE_16UI;
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RG16UI:
+   case V3D_OUTPUT_IMAGE_FORMAT_R16UI:
+      *type = V3D_INTERNAL_TYPE_16UI;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA32I:
+      *type = V3D_INTERNAL_TYPE_32I;
+      *bpp = V3D_INTERNAL_BPP_128;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RG32I:
+      *type = V3D_INTERNAL_TYPE_32I;
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_R32I:
+      *type = V3D_INTERNAL_TYPE_32I;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA32UI:
+      *type = V3D_INTERNAL_TYPE_32UI;
+      *bpp = V3D_INTERNAL_BPP_128;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RG32UI:
+      *type = V3D_INTERNAL_TYPE_32UI;
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_R32UI:
+      *type = V3D_INTERNAL_TYPE_32UI;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RGBA32F:
+      *type = V3D_INTERNAL_TYPE_32F;
+      *bpp = V3D_INTERNAL_BPP_128;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_RG32F:
+      *type = V3D_INTERNAL_TYPE_32F;
+      *bpp = V3D_INTERNAL_BPP_64;
+      break;
+
+   case V3D_OUTPUT_IMAGE_FORMAT_R32F:
+      *type = V3D_INTERNAL_TYPE_32F;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+
+   default:
+      /* Provide some default values, as we'll be called at RB
+       * creation time, even if an RB with this format isn't supported.
+       */
+      *type = V3D_INTERNAL_TYPE_8;
+      *bpp = V3D_INTERNAL_BPP_32;
+      break;
+   }
+}
+
 static VkFormatFeatureFlags
 image_format_features(VkFormat vk_format,
                       const struct v3dv_format *v3dv_format,
diff --git a/src/broadcom/vulkan/v3dv_image.c b/src/broadcom/vulkan/v3dv_image.c
index 14b8b12..a4a2fb8 100644
--- a/src/broadcom/vulkan/v3dv_image.c
+++ b/src/broadcom/vulkan/v3dv_image.c
@@ -244,6 +244,17 @@
    }
 }
 
+static uint32_t
+layer_offset(struct v3dv_image *image, uint32_t level, uint32_t layer)
+{
+   struct v3d_resource_slice *slice = &image->slices[level];
+
+   if (image->type == VK_IMAGE_TYPE_3D)
+      return slice->offset + layer * slice->size;
+   else
+      return slice->offset + layer * image->cube_map_stride;
+}
+
 VkResult
 v3dv_CreateImage(VkDevice _device,
                  const VkImageCreateInfo *pCreateInfo,
@@ -297,3 +308,76 @@
 
    return VK_SUCCESS;
 }
+
+VkResult
+v3dv_CreateImageView(VkDevice _device,
+                     const VkImageViewCreateInfo *pCreateInfo,
+                     const VkAllocationCallbacks *pAllocator,
+                     VkImageView *pView)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_image, image, pCreateInfo->image);
+   struct v3dv_image_view *iview;
+
+   iview = vk_zalloc2(&device->alloc, pAllocator, sizeof(*iview), 8,
+                      VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (iview == NULL)
+      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   const VkImageSubresourceRange *range = &pCreateInfo->subresourceRange;
+
+   assert(range->layerCount > 0);
+   assert(range->baseMipLevel < image->levels);
+
+   /* FIXME: we don't handle depth/stencil yet */
+   assert((range->aspectMask &
+           (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) == 0);
+
+#ifdef DEBUG
+   switch (image->type) {
+   case VK_IMAGE_TYPE_1D:
+   case VK_IMAGE_TYPE_2D:
+      assert(range->baseArrayLayer + v3dv_layer_count(image, range) - 1 <=
+             image->array_size);
+      break;
+   case VK_IMAGE_TYPE_3D:
+      assert(range->baseArrayLayer + v3dv_layer_count(image, range) - 1
+             <= u_minify(image->extent.depth, range->baseMipLevel));
+      break;
+   default:
+      unreachable("bad VkImageType");
+   }
+#endif
+
+   iview->image = image;
+   iview->aspects = range->aspectMask;
+   iview->extent = (VkExtent3D) {
+      .width  = u_minify(image->extent.width , range->baseMipLevel),
+      .height = u_minify(image->extent.height, range->baseMipLevel),
+      .depth  = u_minify(image->extent.depth , range->baseMipLevel),
+   };
+
+   iview->first_layer = range->baseArrayLayer;
+   iview->last_layer = range->baseArrayLayer +
+                       v3dv_layer_count(image, range) - 1;
+   iview->offset = layer_offset(image, range->baseMipLevel, iview->first_layer);
+
+   iview->tiling = image->slices[0].tiling;
+
+   iview->vk_format = pCreateInfo->format;
+   iview->format = v3dv_get_format(pCreateInfo->format);
+   assert(iview->format && iview->format->supported);
+
+   const struct util_format_description *desc =
+      vk_format_description(iview->vk_format);
+   iview->swap_rb = desc->swizzle[0] == PIPE_SWIZZLE_Z &&
+                    iview->vk_format != VK_FORMAT_B5G6R5_UNORM_PACK16;
+
+   v3dv_get_internal_type_bpp_for_output_format(iview->format->rt_type,
+                                               &iview->internal_type,
+                                               &iview->internal_bpp);
+
+   *pView = v3dv_image_view_to_handle(iview);
+
+   return VK_SUCCESS;
+}
diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h
index a37d0fc..b4a4494 100644
--- a/src/broadcom/vulkan/v3dv_private.h
+++ b/src/broadcom/vulkan/v3dv_private.h
@@ -278,6 +278,23 @@
    VkDeviceSize mem_offset;
 };
 
+struct v3dv_image_view {
+   const struct v3dv_image *image;
+   VkImageAspectFlags aspects;
+   VkExtent3D extent;
+
+   VkFormat vk_format;
+   const struct v3dv_format *format;
+   bool swap_rb;
+   enum v3d_tiling_mode tiling;
+   uint32_t internal_bpp;
+   uint32_t internal_type;
+
+   uint32_t first_layer;
+   uint32_t last_layer;
+   uint32_t offset;
+};
+
 struct v3dv_shader_module {
    unsigned char sha1[20];
    uint32_t size;
@@ -321,6 +338,7 @@
 void v3dv_loge_v(const char *format, va_list va);
 
 const struct v3dv_format *v3dv_get_format(VkFormat);
+void v3dv_get_internal_type_bpp_for_output_format(uint32_t format, uint32_t *type, uint32_t *bpp);
 
 uint32_t v3d_utile_width(int cpp);
 uint32_t v3d_utile_height(int cpp);
@@ -376,8 +394,16 @@
 
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_device_memory, VkDeviceMemory)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_image, VkImage)
+V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_image_view, VkImageView)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_shader_module, VkShaderModule)
 
+/* This is defined as a macro so that it works for both
+ * VkImageSubresourceRange and VkImageSubresourceLayers
+ */
+#define v3dv_layer_count(_image, _range) \
+   ((_range)->layerCount == VK_REMAINING_ARRAY_LAYERS ? \
+    (_image)->array_size - (_range)->baseArrayLayer : (_range)->layerCount)
+
 static inline int
 v3dv_ioctl(int fd, unsigned long request, void *arg)
 {
diff --git a/src/broadcom/vulkan/vk_format_info.h b/src/broadcom/vulkan/vk_format_info.h
index ac4e174..0969c02 100644
--- a/src/broadcom/vulkan/vk_format_info.h
+++ b/src/broadcom/vulkan/vk_format_info.h
@@ -120,4 +120,10 @@
    return util_format_get_blockheight(vk_format_to_pipe_format(format));
 }
 
+static inline const struct util_format_description *
+vk_format_description(VkFormat format)
+{
+   return util_format_description(vk_format_to_pipe_format(format));
+}
+
 #endif /* VK_FORMAT_INFO_H */