[Mesa-dev] [PATCH 08/12] anv/image: Support creating uncompressed views of compressed images

Jason Ekstrand jason at jlekstrand.net
Fri Sep 15 16:01:47 UTC 2017


In order to get support everywhere, this gets a bit complicated.  On Sky
Lake and later, everything is fine because HALIGN/VALIGN are specified
in surface elements and are required to be at least 4 so any offsetting
we may need to do falls neatly within the heavy restrictions placed on
the X/Y Offset parameter of RENDER_SURFACE_STATE.  On Broadwell and
earlier, HALIGN/VALIGN are specified in pixels are are hard-coded to
align to exactly the block size of the compressed texture.  This meas
that, when reinterpreted as a non-compressed texture, the tile offsets
may be anything and we can't rely on X/Y Offset.

In order to work around this issue, we fall back to linear where we can
trivially offset to whatever element we so choose.  However, since
linear texturing performance is terrible, we create a tiled shadow copy
of the image to use for texturing.  Whenever the user does a layout
transition from anything to SHADER_READ_ONLY_OPTIMAL, we use blorp to
copy the contents of the texture from the linear copy to the tiled
shadow copy.  This assumes that the client will use the image far more
for texturing than as a storage image or render target.

Even though we don't need the shadow copy on Sky Lake, we implement it
this way first to make testing easier.  Due to the hardware restriction
that ASTC must not be linear, ASTC does not work yet.
---
 src/intel/vulkan/anv_blorp.c       |  46 +++++++++++++++
 src/intel/vulkan/anv_image.c       | 111 ++++++++++++++++++++++++++++++++++++-
 src/intel/vulkan/anv_private.h     |  14 +++++
 src/intel/vulkan/genX_cmd_buffer.c |  21 ++++++-
 4 files changed, 187 insertions(+), 5 deletions(-)

diff --git a/src/intel/vulkan/anv_blorp.c b/src/intel/vulkan/anv_blorp.c
index 7f51bed..95f8696 100644
--- a/src/intel/vulkan/anv_blorp.c
+++ b/src/intel/vulkan/anv_blorp.c
@@ -1489,6 +1489,52 @@ anv_cmd_buffer_resolve_subpass(struct anv_cmd_buffer *cmd_buffer)
 }
 
 void
+anv_image_copy_to_shadow(struct anv_cmd_buffer *cmd_buffer,
+                         const struct anv_image *image,
+                         VkImageAspectFlagBits aspect,
+                         uint32_t base_level, uint32_t level_count,
+                         uint32_t base_layer, uint32_t layer_count)
+{
+   struct blorp_batch batch;
+   blorp_batch_init(&cmd_buffer->device->blorp, &batch, cmd_buffer, 0);
+
+   struct blorp_surf surf;
+   get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_DEPTH_BIT,
+                                ISL_AUX_USAGE_NONE, &surf);
+
+   struct blorp_surf shadow_surf = {
+      .surf = &image->shadow_surface.isl,
+      .addr = {
+         .buffer = image->bo,
+         .offset = image->offset + image->shadow_surface.offset,
+      },
+   };
+
+   for (uint32_t l = 0; l < level_count; l++) {
+      const uint32_t level = base_level + l;
+
+      const VkExtent3D extent = {
+         .width = anv_minify(image->extent.width, level),
+         .height = anv_minify(image->extent.height, level),
+         .depth = anv_minify(image->extent.depth, level),
+      };
+
+      if (image->type == VK_IMAGE_TYPE_3D)
+         layer_count = extent.depth;
+
+      for (uint32_t a = 0; a < layer_count; a++) {
+         const uint32_t layer = base_layer + a;
+
+         blorp_copy(&batch, &surf, level, layer,
+                    &shadow_surf, level, layer,
+                    0, 0, 0, 0, extent.width, extent.height);
+      }
+   }
+
+   blorp_batch_finish(&batch);
+}
+
+void
 anv_gen8_hiz_op_resolve(struct anv_cmd_buffer *cmd_buffer,
                         const struct anv_image *image,
                         enum blorp_hiz_op op)
diff --git a/src/intel/vulkan/anv_image.c b/src/intel/vulkan/anv_image.c
index 77ffa7d..e6e3250 100644
--- a/src/intel/vulkan/anv_image.c
+++ b/src/intel/vulkan/anv_image.c
@@ -235,6 +235,18 @@ make_surface(const struct anv_device *dev,
                                                aspect, vk_info->tiling);
    assert(format != ISL_FORMAT_UNSUPPORTED);
 
+   /* If an image is created as BLOCK_TEXEL_VIEW_COMPATIBLE, then we need to
+    * fall back to linear because we aren't guaranteed that we can handle
+    * offsets correctly.
+    */
+   bool needs_shadow = false;
+   if ((vk_info->flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR) &&
+       vk_info->tiling == VK_IMAGE_TILING_OPTIMAL) {
+      assert(isl_format_is_compressed(format));
+      tiling_flags = ISL_TILING_LINEAR_BIT;
+      needs_shadow = true;
+   }
+
    ok = isl_surf_init(&dev->isl_dev, &anv_surf->isl,
       .dim = vk_to_isl_surf_dim[vk_info->imageType],
       .format = format,
@@ -256,6 +268,36 @@ make_surface(const struct anv_device *dev,
 
    add_surface(image, anv_surf);
 
+   /* If an image is created as BLOCK_TEXEL_VIEW_COMPATIBLE, then we need to
+    * create an identical tiled shadow surface for use while texturing so we
+    * don't get garbage performance.
+    */
+   if (needs_shadow) {
+      assert(aspect == VK_IMAGE_ASPECT_COLOR_BIT);
+      assert(tiling_flags == ISL_TILING_LINEAR_BIT);
+
+      ok = isl_surf_init(&dev->isl_dev, &image->shadow_surface.isl,
+         .dim = vk_to_isl_surf_dim[vk_info->imageType],
+         .format = format,
+         .width = image->extent.width,
+         .height = image->extent.height,
+         .depth = image->extent.depth,
+         .levels = vk_info->mipLevels,
+         .array_len = vk_info->arrayLayers,
+         .samples = vk_info->samples,
+         .min_alignment = 0,
+         .row_pitch = anv_info->stride,
+         .usage = choose_isl_surf_usage(image->usage, image->usage, aspect),
+         .tiling_flags = ISL_TILING_ANY_MASK);
+
+      /* isl_surf_init() will fail only if provided invalid input. Invalid input
+       * is illegal in Vulkan.
+       */
+      assert(ok);
+
+      add_surface(image, &image->shadow_surface);
+   }
+
    /* Add a HiZ surface to a depth buffer that will be used for rendering.
     */
    if (aspect == VK_IMAGE_ASPECT_DEPTH_BIT) {
@@ -673,6 +715,20 @@ anv_image_fill_surface_state(struct anv_device *device,
    struct isl_view view = *view_in;
    view.usage |= view_usage;
 
+   /* For texturing with VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL from a
+    * compressed surface with a shadow surface, we use the shadow instead of
+    * the primary surface.  The shadow surface will be tiled, unlike the main
+    * surface, so it should get significantly better performance.
+    */
+   if (image->shadow_surface.isl.size > 0 &&
+       isl_format_is_compressed(view.format) &&
+       (flags & ANV_IMAGE_VIEW_STATE_TEXTURE_OPTIMAL)) {
+      assert(isl_format_is_compressed(surface->isl.format));
+      assert(surface->isl.tiling == ISL_TILING_LINEAR);
+      assert(image->shadow_surface.isl.tiling != ISL_TILING_LINEAR);
+      surface = &image->shadow_surface;
+   }
+
    if (view_usage == ISL_SURF_USAGE_RENDER_TARGET_BIT)
       view.swizzle = anv_swizzle_for_render(view.swizzle);
 
@@ -718,16 +774,65 @@ anv_image_fill_surface_state(struct anv_device *device,
                                                       view.format);
       }
 
+      const struct isl_surf *isl_surf = &surface->isl;
+
+      struct isl_surf tmp_surf;
+      uint32_t offset_B = 0, tile_x_sa = 0, tile_y_sa = 0;
+      if (isl_format_is_compressed(surface->isl.format) &&
+          !isl_format_is_compressed(view.format)) {
+         /* We're creating an uncompressed view of a compressed surface.  This
+          * is allowed but only for a single level/layer.
+          */
+         assert(surface->isl.samples == 1);
+         assert(view.levels == 1);
+         assert(view.array_len == 1);
+
+         isl_surf_get_image_surf(&device->isl_dev, isl_surf,
+                                 view.base_level,
+                                 surface->isl.dim == ISL_SURF_DIM_3D ?
+                                    0 : view.base_array_layer,
+                                 surface->isl.dim == ISL_SURF_DIM_3D ?
+                                    view.base_array_layer : 0,
+                                 &tmp_surf,
+                                 &offset_B, &tile_x_sa, &tile_y_sa);
+
+         /* The newly created image represents the one subimage we're
+          * referencing with this view so it only has one array slice and
+          * miplevel.
+          */
+         view.base_array_layer = 0;
+         view.base_level = 0;
+
+         /* We're making an uncompressed view here.  The image dimensions need
+          * to be scaled down by the block size.
+          */
+         const struct isl_format_layout *fmtl =
+            isl_format_get_layout(surface->isl.format);
+         tmp_surf.format = view.format;
+         tmp_surf.logical_level0_px.width =
+            DIV_ROUND_UP(tmp_surf.logical_level0_px.width, fmtl->bw);
+         tmp_surf.logical_level0_px.height =
+            DIV_ROUND_UP(tmp_surf.logical_level0_px.height, fmtl->bh);
+         tmp_surf.phys_level0_sa.width /= fmtl->bw;
+         tmp_surf.phys_level0_sa.height /= fmtl->bh;
+
+         isl_surf = &tmp_surf;
+
+         assert(surface->isl.tiling == ISL_TILING_LINEAR);
+         assert(tile_x_sa == 0);
+         assert(tile_y_sa == 0);
+      }
+
       isl_surf_fill_state(&device->isl_dev, state_inout->state.map,
-                          .surf = &surface->isl,
+                          .surf = isl_surf,
                           .view = &view,
-                          .address = address,
+                          .address = address + offset_B,
                           .clear_color = *clear_color,
                           .aux_surf = &image->aux_surface.isl,
                           .aux_usage = aux_usage,
                           .aux_address = aux_address,
                           .mocs = device->default_mocs);
-      state_inout->address = address;
+      state_inout->address = address + offset_B;
       if (device->info.gen >= 8) {
          state_inout->aux_address = aux_address;
       } else {
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index 85843b2..355adba 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -2266,6 +2266,13 @@ struct anv_image {
    };
 
    /**
+    * A surface which shadows the main surface and may have different tiling.
+    * This is used for sampling using a tiling that isn't supported for other
+    * operations.
+    */
+   struct anv_surface shadow_surface;
+
+   /**
     * For color images, this is the aux usage for this image when not used as a
     * color attachment.
     *
@@ -2352,6 +2359,13 @@ anv_image_fast_clear(struct anv_cmd_buffer *cmd_buffer,
                      const uint32_t base_level, const uint32_t level_count,
                      const uint32_t base_layer, uint32_t layer_count);
 
+void
+anv_image_copy_to_shadow(struct anv_cmd_buffer *cmd_buffer,
+                         const struct anv_image *image,
+                         VkImageAspectFlagBits aspect,
+                         uint32_t base_level, uint32_t level_count,
+                         uint32_t base_layer, uint32_t layer_count);
+
 enum isl_aux_usage
 anv_layout_to_aux_usage(const struct gen_device_info * const devinfo,
                         const struct anv_image *image,
diff --git a/src/intel/vulkan/genX_cmd_buffer.c b/src/intel/vulkan/genX_cmd_buffer.c
index 7fb607f..fbc1995 100644
--- a/src/intel/vulkan/genX_cmd_buffer.c
+++ b/src/intel/vulkan/genX_cmd_buffer.c
@@ -627,8 +627,25 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
    /* No work is necessary if the layout stays the same or if this subresource
     * range lacks auxiliary data.
     */
-   if (initial_layout == final_layout ||
-       base_layer >= anv_image_aux_layers(image, base_level))
+   if (initial_layout == final_layout)
+      return;
+
+   if (image->shadow_surface.isl.size > 0 &&
+       final_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+      /* This surface is a linear compressed image with a tiled shadow surface
+       * for texturing.  The client is about to use it in READ_ONLY_OPTIMAL so
+       * we need to ensure the shadow copy is up-to-date.
+       */
+      assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
+      assert(image->color_surface.isl.tiling == ISL_TILING_LINEAR);
+      assert(image->shadow_surface.isl.tiling != ISL_TILING_LINEAR);
+      assert(isl_format_is_compressed(image->color_surface.isl.format));
+      anv_image_copy_to_shadow(cmd_buffer, image, VK_IMAGE_ASPECT_COLOR_BIT,
+                               base_level, level_count,
+                               base_layer, layer_count);
+   }
+
+   if (base_layer >= anv_image_aux_layers(image, base_level))
       return;
 
    /* A transition of a 3D subresource works on all slices at a time. */
-- 
2.5.0.400.gff86faf



More information about the mesa-dev mailing list