[Mesa-dev] [PATCH 04/14] isl: Rework the way we define tile sizes.

Chad Versace chad.versace at intel.com
Tue Jul 12 19:31:33 UTC 2016


On Sat 09 Jul 2016, Jason Ekstrand wrote:
> This is based on a very long set of discussions between Chad and myself
> about how we should properly represent HiZ and CCS buffers.  The end result
> of that discussion was that a tiling actually has two different sizes, a
> logical size in elements, and a physical size in bytes and rows.  This
> commit reworks ISL's pitch and size calculations to work in terms of these
> two sizes.
> ---
>  src/intel/isl/isl.c | 181 ++++++++++++++++++++++++++++++----------------------
>  src/intel/isl/isl.h |  32 +++++++++-
>  2 files changed, 133 insertions(+), 80 deletions(-)
> 
> diff --git a/src/intel/isl/isl.c b/src/intel/isl/isl.c
> index 6f57ac2..633bfdf 100644
> --- a/src/intel/isl/isl.c
> +++ b/src/intel/isl/isl.c
> @@ -111,30 +111,32 @@ isl_tiling_get_info(const struct isl_device *dev,
>                      struct isl_tile_info *tile_info)
>  {


>     case ISL_TILING_W:
>        /* XXX: Should W tile be same as Y? */
> -      width = 1 << 6;
> -      height = 1 << 6;
> +      assert(bs == 1);
> +      logical_el = isl_extent2d(64, 64);
> +      phys_B = isl_extent2d(64, 64);
>        break;

I see you clean up W-tiling patch 5. So this looks ok.




> @@ -1094,10 +1083,6 @@ isl_surf_init_s(const struct isl_device *dev,
>     assert(phys_slice0_sa.w % fmtl->bw == 0);
>     assert(phys_slice0_sa.h % fmtl->bh == 0);
>  
> -   const uint32_t row_pitch = isl_calc_row_pitch(dev, info, &tile_info,
> -                                                 &image_align_sa,
> -                                                 &phys_slice0_sa);
> -
>     const uint32_t array_pitch_el_rows =
>        isl_calc_array_pitch_el_rows(dev, info, &tile_info, dim_layout,
>                                     array_pitch_span, &image_align_sa,
> @@ -1108,16 +1093,50 @@ isl_surf_init_s(const struct isl_device *dev,
>     uint32_t pad_bytes;
>     isl_apply_surface_padding(dev, info, &tile_info, &total_h_el, &pad_bytes);
>  
> -   /* Be sloppy. Align any leftover padding to a row boundary. */
> -   total_h_el += isl_align_div_npot(pad_bytes, row_pitch);
> +   uint32_t row_pitch, size, base_alignment;
> +   if (tiling == ISL_TILING_LINEAR) {
> +      row_pitch = isl_calc_linear_row_pitch(dev, info, &phys_slice0_sa);
> +      size = row_pitch * total_h_el + pad_bytes;
>  
> -   const uint32_t size =
> -      row_pitch * isl_align(total_h_el, tile_info.height);
> +      /* From the Broadwell PRM Vol 2d, RENDER_SURFACE_STATE::SurfaceBaseAddress:
> +       *
> +       *    "The Base Address for linear render target surfaces and surfaces
> +       *    accessed with the typed surface read/write data port messages must
> +       *    be element-size aligned, for non-YUV surface formats, or a
> +       *    multiple of 2 element-sizes for YUV surface formats. Other linear
> +       *    surfaces have no alignment requirements (byte alignment is
> +       *    sufficient.)"
> +       */
> +      base_alignment = MAX(1, info->min_alignment);
> +      if (info->usage & ISL_SURF_USAGE_RENDER_TARGET_BIT) {
> +         if (isl_format_is_yuv(info->format)) {
> +            base_alignment = MAX(base_alignment, 2 * fmtl->bs);
> +         } else {
> +            base_alignment = MAX(base_alignment, fmtl->bs);
> +         }
> +      }
> +   } else {
> +      assert(phys_slice0_sa.w % fmtl->bw == 0);
> +      const uint32_t total_w_el = phys_slice0_sa.width / fmtl->bw;
> +      const uint32_t total_w_tl =
> +         isl_align_div(total_w_el, tile_info.logical_extent_el.width);
> +
> +      row_pitch = total_w_tl * tile_info.phys_extent_B.width;
> +      if (row_pitch < info->min_pitch) {
> +         row_pitch = isl_align(info->min_pitch, tile_info.phys_extent_B.width);
> +      }
>  
> -   /* Alignment of surface base address, in bytes */
> -   uint32_t base_alignment = MAX(1, info->min_alignment);
> -   assert(isl_is_pow2(base_alignment) && isl_is_pow2(tile_info.size));
> -   base_alignment = MAX(base_alignment, tile_info.size);
> +      total_h_el += isl_align_div_npot(pad_bytes, row_pitch);
> +      const uint32_t total_h_tl =
> +         isl_align_div(total_h_el, tile_info.logical_extent_el.height);
> +
> +      size = total_h_tl * tile_info.phys_extent_B.height * row_pitch;
> +
> +      const uint32_t tile_size = tile_info.phys_extent_B.width *
> +                                 tile_info.phys_extent_B.height;
> +      assert(isl_is_pow2(info->min_alignment) && isl_is_pow2(tile_size));
> +      base_alignment = MAX(info->min_alignment, tile_size);
> +   }

The calculations look correct to me. The unit suffixes really help the
verification.

> @@ -1420,9 +1439,6 @@ isl_tiling_get_intratile_offset_el(const struct isl_device *dev,
>                                     uint32_t *x_offset_el,
>                                     uint32_t *y_offset_el)
>  {
> -   struct isl_tile_info tile_info;
> -   isl_tiling_get_info(dev, tiling, bs, &tile_info);
> -
>     /* This function only really works for power-of-two surfaces.  In
>      * theory, we could make it work for non-power-of-two surfaces by going
>      * to the left until we find a block that is bs-aligned.  The Vulkan
> @@ -1431,18 +1447,29 @@ isl_tiling_get_intratile_offset_el(const struct isl_device *dev,
>      */
>     assert(tiling == ISL_TILING_LINEAR || isl_is_pow2(bs));
>  
> -   uint32_t small_y_offset_el = total_y_offset_el % tile_info.height;
> -   uint32_t big_y_offset_el = total_y_offset_el - small_y_offset_el;
> -   uint32_t big_y_offset_B = big_y_offset_el * row_pitch;
> +   if (tiling == ISL_TILING_LINEAR) {
> +      *base_address_offset = total_y_offset_el * row_pitch +
> +                             total_x_offset_el * bs;
> +      *x_offset_el = 0;
> +      *y_offset_el = 0;
> +      return;
> +   }
> +
> +   struct isl_tile_info tile_info;
> +   isl_tiling_get_info(dev, tiling, bs, &tile_info);
> +
> +   /* Compute the offset into the tile */
> +   *x_offset_el = total_x_offset_el % tile_info.logical_extent_el.w;
> +   *y_offset_el = total_y_offset_el % tile_info.logical_extent_el.h;
>  
> -   uint32_t total_x_offset_B = total_x_offset_el * bs;
> -   uint32_t small_x_offset_B = total_x_offset_B % tile_info.width;
> -   uint32_t small_x_offset_el = small_x_offset_B / bs;
> -   uint32_t big_x_offset_B = (total_x_offset_B / tile_info.width) * tile_info.size;
> +   /* Compute the offset of the tile in units of whole tiles */
> +   uint32_t x_offset_tl = total_x_offset_el / tile_info.logical_extent_el.w;
> +   uint32_t y_offset_tl = total_y_offset_el / tile_info.logical_extent_el.h;
>  
> -   *base_address_offset = big_y_offset_B + big_x_offset_B;
> -   *x_offset_el = small_x_offset_el;
> -   *y_offset_el = small_y_offset_el;
> +   assert(row_pitch % tile_info.phys_extent_B.width == 0);
> +   *base_address_offset =
> +      y_offset_tl * tile_info.phys_extent_B.h * row_pitch +
> +      x_offset_tl * tile_info.phys_extent_B.h * tile_info.phys_extent_B.w;
>  }

I was initially afraid that the new isl_tile_info concepts would lead to
confusing code. To the contrary, this function proves the new concept
leads to very readable code that clearly expresses the calculations.

>  uint32_t
> diff --git a/src/intel/isl/isl.h b/src/intel/isl/isl.h
> index 985b605..6ec956b 100644
> --- a/src/intel/isl/isl.h
> +++ b/src/intel/isl/isl.h
> @@ -660,9 +660,28 @@ struct isl_format_layout {
>  
>  struct isl_tile_info {
>     enum isl_tiling tiling;
> -   uint32_t width; /**< in bytes */
> -   uint32_t height; /**< in rows of memory */
> -   uint32_t size; /**< in bytes */
> +
> +   /** The logical size of the tile in units of surface elements
> +    *
> +    * This field determines how a given surface is cut up into tiles.  It is
> +    * used to compute the size of a surface in tiles and can be used to
> +    * determine the location of the tile containing any given surface element.
> +    * The exact value of this field depends heavily on the bits-per-block of
> +    * the format being used.
> +    */
> +   struct isl_extent2d logical_extent_el;
> +
> +   /** The physical size of the tile in bytes and rows of bytes
> +    *
> +    * This field determines how the tiles of a surface are physically layed
> +    * out in memory.  The logical and physical tile extent are frequently the
> +    * same but this is not always the case.  For instance, a W-tile (which is
> +    * always used with ISL_FORMAT_R8) has a logical size of 64el x 64el but
> +    * its physical size is 128B x 32rows, the same as a Y-tile.
> +    *
> +    * @see isl_surf::row_pitch
> +    */
> +   struct isl_extent2d phys_extent_B;

These comments look good.

>  };
>  
>  /**
> @@ -744,6 +763,13 @@ struct isl_surf {
>  
>     /**
>      * Pitch between vertically adjacent surface elements, in bytes.
> +    *
> +    * For tiled surfaces, the interpretation of this value depends on the
> +    * value of isl_tile_info::physical_extent_B.  In particular, the distance
> +    * in bytes between vertically adjacent tiles in the image is given by
> +    * row_pitch * isl_tile_info::physical_extent_B.height.
> +    *
> +    * @see isl_tile_info::phys_extent_B;
>      */
>     uint32_t row_pitch;

Everything in the patch looked good, until I reached this final hunk.
The comment confuses me; it seems to contradict itself. The comment
first says the unit of row_pitch is B / el_rows; later it says the unit
is B / physical_B_rows. (I'm inventing units here). My review here is
not merely pedantic; I honestly no longer know what isl_surf::row_pitch
is anymore.

I searched for a clue by checking out your branch and grepping for
row_pitch, but that led to more confusion. I found this line, which
makes sense to me:
    row_pitch = total_w_tl * tile_info.phy_extent_B.width
In that line, the row_pitch unit is (loosely) B / physical_b_rows.
But I also found this line:
	size = row_pitch * total_h_el + pad_bytes
In that line, the row_pitchunit is B / el_rows

HELP!?!?!?

Could you clarify the comment and perhaps provide an example?


More information about the mesa-dev mailing list