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

Lionel Landwerlin lionel.g.landwerlin at intel.com
Tue Sep 19 13:52:48 UTC 2017


On 15/09/17 17:01, Jason Ekstrand wrote:
> 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);

There is something odd here (I must misunderstand...).
In make_surface() you always make the shadow_surface with linear tiling, 
yet here you're expecting it *not* to be 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. */




More information about the mesa-dev mailing list