v3dv: allow creating uncompressed views from compressed images and vice versa
authorIago Toral Quiroga <itoral@igalia.com>
Mon, 24 May 2021 09:24:02 +0000 (11:24 +0200)
committerMarge Bot <eric+marge@anholt.net>
Tue, 25 May 2021 09:12:35 +0000 (09:12 +0000)
Relevant CTS tests (requires VK_KHR_maintenance2):
dEQP-VK.image.texel_view_compatible.*

Reviewed-by: Alejandro PiƱeiro <apinheiro@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10951>

src/broadcom/vulkan/v3dv_image.c

index 1a97769..f1b0658 100644 (file)
@@ -508,8 +508,52 @@ pack_texture_shader_state_helper(struct v3dv_device *device,
          tex.image_depth /= 6;
       }
 
-      tex.image_height = image->extent.height * msaa_scale;
-      tex.image_width = image->extent.width * msaa_scale;
+      uint32_t image_block_w = vk_format_get_blockwidth(image->vk_format);
+      uint32_t image_block_h = vk_format_get_blockheight(image->vk_format);
+      uint32_t view_block_w = vk_format_get_blockwidth(image_view->vk_format);
+      uint32_t view_block_h = vk_format_get_blockheight(image_view->vk_format);
+      if (image_block_w != view_block_w || image_block_h != view_block_h) {
+         /* If we are creating an uncompressed view from a compressed format or
+          * viceversa, we need to be careful when programming the image size
+          * so that the miplevel dimensions computed by the hardware match the
+          * miplevel dimensions of the underlying image.
+          *
+          * For example, if we have an etc2_rgb8 image with size 22x22, then its
+          * mip level dimensions are 22x22, 11x11 and 5x5. If we now interpret
+          * these in units of an uncompressed rgba16 format, we get 6x6, 3x3 and
+          * 2x2.
+          *
+          * However, here we are only programming the size of the level 0 and
+          * the hardware will automatically compute mip level dimensions when
+          * translating texture coordinates for the mip level. This is a problem
+          * because if we program here the size of mip 0 in the uncompressed
+          * rgba16 format, we would set 6x6 (22x22 divided by 4 and rounded up).
+          * And because we set an uncompressed view format, the hardware will
+          * compute mip level sizes 6x6, 3x3 and 1x1, so miplevel 2 won't match
+          * the size of miplevel 2 in the original compressed format and the
+          * texture sampling for that miplevel will differ. To fix this, we
+          * need to compute a level 0 size that makes the hardware compute the
+          * appropriate mip level dimensions. We do this by computing the
+          * mip level dimensions in blocks and then computing level 0
+          * dimensions from that.
+          */
+         uint32_t level = image_view->base_level;
+         uint32_t mip_w_in_blocks =
+            DIV_ROUND_UP(u_minify(image->extent.width, level) * view_block_w,
+                         image_block_w);
+         uint32_t mip_h_in_blocks =
+            DIV_ROUND_UP(u_minify(image->extent.height, level) * view_block_h,
+                         image_block_h);
+
+         tex.image_width = mip_w_in_blocks << level;
+         tex.image_height = mip_h_in_blocks << level;
+      } else {
+         tex.image_width = image->extent.width;
+         tex.image_height = image->extent.height;
+      }
+
+      tex.image_height *= msaa_scale;
+      tex.image_width *= msaa_scale;
 
       /* On 4.x, the height of a 1D texture is redefined to be the
        * upper 14 bits of the width (which is only usable with txf).
@@ -619,11 +663,6 @@ v3dv_CreateImageView(VkDevice _device,
 
    iview->base_level = range->baseMipLevel;
    iview->max_level = iview->base_level + v3dv_level_count(image, range) - 1;
-   iview->extent = (VkExtent3D) {
-      .width  = u_minify(image->extent.width , iview->base_level),
-      .height = u_minify(image->extent.height, iview->base_level),
-      .depth  = u_minify(image->extent.depth , iview->base_level),
-   };
 
    iview->first_layer = range->baseArrayLayer;
    iview->last_layer = range->baseArrayLayer +
@@ -670,6 +709,20 @@ v3dv_CreateImageView(VkDevice _device,
    iview->format = v3dv_get_format(format);
    assert(iview->format && iview->format->supported);
 
+   uint32_t level = iview->base_level;
+   uint32_t width = image->extent.width;
+   uint32_t height = image->extent.height;
+   uint32_t depth = image->extent.depth;
+   uint32_t image_block_w = vk_format_get_blockwidth(image->vk_format);
+   uint32_t image_block_h = vk_format_get_blockheight(image->vk_format);
+   uint32_t view_block_w = vk_format_get_blockwidth(iview->vk_format);
+   uint32_t view_block_h = vk_format_get_blockheight(iview->vk_format);
+   iview->extent.width =
+      DIV_ROUND_UP(u_minify(width, level) * view_block_w, image_block_w);
+   iview->extent.height =
+      DIV_ROUND_UP(u_minify(height, level) * view_block_h, image_block_h);
+   iview->extent.depth = u_minify(depth, level);
+
    if (vk_format_is_depth_or_stencil(iview->vk_format)) {
       iview->internal_type = v3dv_get_internal_depth_type(iview->vk_format);
    } else {