isl: Rework the way we define tile sizes.
authorJason Ekstrand <jason.ekstrand@intel.com>
Sat, 9 Jul 2016 00:10:59 +0000 (17:10 -0700)
committerJason Ekstrand <jason.ekstrand@intel.com>
Wed, 13 Jul 2016 18:47:37 +0000 (11:47 -0700)
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.

Reviewed-by: Chad Versace <chad.versace@intel.com>
src/intel/isl/isl.c
src/intel/isl/isl.h

index 022b06c1a62b1cc57323ea8948b640b85624c0e3..c710bca6d2985e1c04d5c938eb403ae656472fc2 100644 (file)
@@ -111,30 +111,32 @@ isl_tiling_get_info(const struct isl_device *dev,
                     struct isl_tile_info *tile_info)
 {
    const uint32_t bs = format_block_size;
-   uint32_t width, height;
+   struct isl_extent2d logical_el, phys_B;
 
    assert(bs > 0);
+   assert(tiling == ISL_TILING_LINEAR || isl_is_pow2(bs));
 
    switch (tiling) {
    case ISL_TILING_LINEAR:
-      width = 1;
-      height = 1;
+      logical_el = isl_extent2d(1, 1);
+      phys_B = isl_extent2d(bs, 1);
       break;
 
    case ISL_TILING_X:
-      width = 1 << 9;
-      height = 1 << 3;
+      logical_el = isl_extent2d(512 / bs, 8);
+      phys_B = isl_extent2d(512, 8);
       break;
 
    case ISL_TILING_Y0:
-      width = 1 << 7;
-      height = 1 << 5;
+      logical_el = isl_extent2d(128 / bs, 32);
+      phys_B = isl_extent2d(128, 32);
       break;
 
    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;
 
    case ISL_TILING_Yf:
@@ -147,8 +149,11 @@ isl_tiling_get_info(const struct isl_device *dev,
 
       bool is_Ys = tiling == ISL_TILING_Ys;
 
-      width = 1 << (6 + (ffs(bs) / 2) + (2 * is_Ys));
-      height = 1 << (6 - (ffs(bs) / 2) + (2 * is_Ys));
+      unsigned width = 1 << (6 + (ffs(bs) / 2) + (2 * is_Ys));
+      unsigned height = 1 << (6 - (ffs(bs) / 2) + (2 * is_Ys));
+
+      logical_el = isl_extent2d(width / bs, height);
+      phys_B = isl_extent2d(width, height);
       break;
    }
 
@@ -158,9 +163,8 @@ isl_tiling_get_info(const struct isl_device *dev,
 
    *tile_info = (struct isl_tile_info) {
       .tiling = tiling,
-      .width = width,
-      .height = height,
-      .size = width * height,
+      .logical_extent_el = logical_el,
+      .phys_extent_B = phys_B,
    };
 
    return true;
@@ -827,7 +831,7 @@ isl_calc_array_pitch_el_rows(const struct isl_device *dev,
        *    Tile Mode != Linear: This field must be set to an integer multiple
        *    of the tile height
        */
-      pitch_el_rows = isl_align(pitch_el_rows, tile_info->height);
+      pitch_el_rows = isl_align(pitch_el_rows, tile_info->logical_extent_el.height);
    }
 
    return pitch_el_rows;
@@ -837,11 +841,9 @@ isl_calc_array_pitch_el_rows(const struct isl_device *dev,
  * Calculate the pitch of each surface row, in bytes.
  */
 static uint32_t
-isl_calc_row_pitch(const struct isl_device *dev,
-                   const struct isl_surf_init_info *restrict info,
-                   const struct isl_tile_info *tile_info,
-                   const struct isl_extent3d *image_align_sa,
-                   const struct isl_extent2d *phys_slice0_sa)
+isl_calc_linear_row_pitch(const struct isl_device *dev,
+                          const struct isl_surf_init_info *restrict info,
+                          const struct isl_extent2d *phys_slice0_sa)
 {
    const struct isl_format_layout *fmtl = isl_format_get_layout(info->format);
 
@@ -894,39 +896,26 @@ isl_calc_row_pitch(const struct isl_device *dev,
    assert(phys_slice0_sa->w % fmtl->bw == 0);
    row_pitch = MAX(row_pitch, fmtl->bs * (phys_slice0_sa->w / fmtl->bw));
 
-   switch (tile_info->tiling) {
-   case ISL_TILING_LINEAR:
-      /* From the Broadwel PRM >> Volume 2d: Command Reference: Structures >>
-       * RENDER_SURFACE_STATE Surface Pitch (p349):
-       *
-       *    - For linear render target surfaces and surfaces accessed with the
-       *      typed data port messages, the pitch must be a multiple of the
-       *      element size for non-YUV surface formats.  Pitch must be
-       *      a multiple of 2 * element size for YUV surface formats.
-       *
-       *    - [Requirements for SURFTYPE_BUFFER and SURFTYPE_STRBUF, which we
-       *      ignore because isl doesn't do buffers.]
-       *
-       *    - For other linear surfaces, the pitch can be any multiple of
-       *      bytes.
-       */
-      if (info->usage & ISL_SURF_USAGE_RENDER_TARGET_BIT) {
-         if (isl_format_is_yuv(info->format)) {
-            row_pitch = isl_align_npot(row_pitch, 2 * fmtl->bs);
-         } else  {
-            row_pitch = isl_align_npot(row_pitch, fmtl->bs);
-         }
+   /* From the Broadwel PRM >> Volume 2d: Command Reference: Structures >>
+    * RENDER_SURFACE_STATE Surface Pitch (p349):
+    *
+    *    - For linear render target surfaces and surfaces accessed with the
+    *      typed data port messages, the pitch must be a multiple of the
+    *      element size for non-YUV surface formats.  Pitch must be
+    *      a multiple of 2 * element size for YUV surface formats.
+    *
+    *    - [Requirements for SURFTYPE_BUFFER and SURFTYPE_STRBUF, which we
+    *      ignore because isl doesn't do buffers.]
+    *
+    *    - For other linear surfaces, the pitch can be any multiple of
+    *      bytes.
+    */
+   if (info->usage & ISL_SURF_USAGE_RENDER_TARGET_BIT) {
+      if (isl_format_is_yuv(info->format)) {
+         row_pitch = isl_align_npot(row_pitch, 2 * fmtl->bs);
+      } else  {
+         row_pitch = isl_align_npot(row_pitch, fmtl->bs);
       }
-      break;
-   default:
-      /* From the Broadwel PRM >> Volume 2d: Command Reference: Structures >>
-       * RENDER_SURFACE_STATE Surface Pitch (p349):
-       *
-       *    - For tiled surfaces, the pitch must be a multiple of the tile
-       *      width.
-       */
-      row_pitch = isl_align(row_pitch, tile_info->width);
-      break;
    }
 
    return row_pitch;
@@ -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);
+   }
 
    *surf = (struct isl_surf) {
       .dim = info->dim,
@@ -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;
 }
 
 uint32_t
index 985b605c18164b538c3d422c64ddfda716958882..f169fefd16e16ffc89a3f0b22cb8e84731aa5fd6 100644 (file)
@@ -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;
 };
 
 /**
@@ -743,7 +762,17 @@ struct isl_surf {
    uint32_t alignment;
 
    /**
-    * Pitch between vertically adjacent surface elements, in bytes.
+    * The interpretation of this field depends on the value of
+    * isl_tile_info::physical_extent_B.  In particular, the width of the
+    * surface in tiles is row_pitch / isl_tile_info::physical_extent_B.width
+    * and the distance in bytes between vertically adjacent tiles in the image
+    * is given by row_pitch * isl_tile_info::physical_extent_B.height.
+    *
+    * For linear images where isl_tile_info::physical_extent_B.height == 1,
+    * this cleanly reduces to being the distance, in bytes, between vertically
+    * adjacent surface elements.
+    *
+    * @see isl_tile_info::phys_extent_B;
     */
    uint32_t row_pitch;