nil: Create images
authorFaith Ekstrand <faith.ekstrand@collabora.com>
Tue, 31 Jan 2023 02:11:49 +0000 (20:11 -0600)
committerMarge Bot <emma+marge@anholt.net>
Fri, 4 Aug 2023 21:31:54 +0000 (21:31 +0000)
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24326>

src/nouveau/nil/meson.build
src/nouveau/nil/nil.c [deleted file]
src/nouveau/nil/nil.h [deleted file]
src/nouveau/nil/nil_image.c [new file with mode: 0644]
src/nouveau/nil/nil_image.h [new file with mode: 0644]

index d2231a0..585d2fe 100644 (file)
@@ -19,8 +19,8 @@
 # SOFTWARE.
 
 libnil_files = files(
-  'nil.c',
-  'nil.h',
+  'nil_image.c',
+  'nil_image.h',
 )
 
 _libnil = static_library(
diff --git a/src/nouveau/nil/nil.c b/src/nouveau/nil/nil.c
deleted file mode 100644 (file)
index 8cf6c8d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "nil.h"
diff --git a/src/nouveau/nil/nil.h b/src/nouveau/nil/nil.h
deleted file mode 100644 (file)
index 5f577be..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef NIL_H
-#define NIL_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "util/macros.h"
-#include "util/format/u_format.h"
-
-#endif /* NIL_H */
diff --git a/src/nouveau/nil/nil_image.c b/src/nouveau/nil/nil_image.c
new file mode 100644 (file)
index 0000000..6956fa3
--- /dev/null
@@ -0,0 +1,199 @@
+#include "nil_image.h"
+
+#include "util/u_math.h"
+
+static struct nil_extent4d
+nil_minify_extent4d(struct nil_extent4d extent, uint32_t level)
+{
+   return (struct nil_extent4d) {
+      .w = u_minify(extent.w, level),
+      .h = u_minify(extent.h, level),
+      .d = u_minify(extent.d, level),
+      .a = extent.a,
+   };
+}
+
+static struct nil_extent4d
+nil_extent4d_div_round_up(struct nil_extent4d num, struct nil_extent4d denom)
+{
+   return (struct nil_extent4d) {
+      .w = DIV_ROUND_UP(num.w, denom.w),
+      .h = DIV_ROUND_UP(num.h, denom.h),
+      .d = DIV_ROUND_UP(num.d, denom.d),
+      .a = DIV_ROUND_UP(num.a, denom.a),
+   };
+}
+
+static struct nil_extent4d
+nil_extent4d_align(struct nil_extent4d ext, struct nil_extent4d align)
+{
+   return (struct nil_extent4d) {
+      .w = ALIGN_POT(ext.w, align.w),
+      .h = ALIGN_POT(ext.h, align.h),
+      .d = ALIGN_POT(ext.d, align.d),
+      .a = ALIGN_POT(ext.a, align.a),
+   };
+}
+
+static struct nil_extent4d
+nil_extent4d_px_to_el(struct nil_extent4d extent_px,
+                      enum pipe_format format)
+{
+   const struct util_format_description *fmt =
+      util_format_description(format);
+
+   const struct nil_extent4d block_extent_px = {
+      .w = fmt->block.width,
+      .h = fmt->block.height,
+      .d = fmt->block.depth,
+      .a = 1,
+   };
+
+   return nil_extent4d_div_round_up(extent_px, block_extent_px);
+}
+
+static struct nil_extent4d
+nil_extent4d_el_to_B(struct nil_extent4d extent_el,
+                     uint32_t B_per_el)
+{
+   struct nil_extent4d extent_B = extent_el;
+   extent_B.w *= B_per_el;
+   return extent_B;
+}
+
+static struct nil_extent4d
+nil_extent4d_B_to_GOB(struct nil_extent4d extent_B,
+                      bool gob_height_8)
+{
+   const struct nil_extent4d gob_extent_B = {
+      .w = NIL_GOB_WIDTH_B,
+      .h = NIL_GOB_HEIGHT(gob_height_8),
+      .d = NIL_GOB_DEPTH,
+      .a = 1,
+   };
+
+   return nil_extent4d_div_round_up(extent_B, gob_extent_B);
+}
+
+static struct nil_extent4d
+nil_tiling_extent_B(struct nil_tiling tiling)
+{
+   if (tiling.is_tiled) {
+      return (struct nil_extent4d) {
+         .w = NIL_GOB_WIDTH_B, /* Tiles are always 1 GOB wide */
+         .h = NIL_GOB_HEIGHT(tiling.gob_height_8) << tiling.y_log2,
+         .d = NIL_GOB_DEPTH << tiling.z_log2,
+         .a = 1,
+      };
+   } else {
+      return nil_extent4d(1, 1, 1, 1);
+   }
+}
+
+static struct nil_tiling
+choose_tiling(struct nil_extent4d extent_B,
+              enum nil_image_usage_flags usage)
+{
+   struct nil_tiling tiling = {
+      .is_tiled = true,
+      .gob_height_8 = true,
+   };
+
+   const struct nil_extent4d extent_GOB =
+      nil_extent4d_B_to_GOB(extent_B, tiling.gob_height_8);
+
+   const uint32_t height_log2 = util_logbase2_ceil(extent_GOB.height);
+   const uint32_t depth_log2 = util_logbase2_ceil(extent_GOB.depth);
+
+   tiling.y_log2 = MIN2(height_log2, 5);
+   tiling.z_log2 = MIN2(depth_log2, 5);
+
+   if (usage & NIL_IMAGE_USAGE_2D_VIEW_BIT)
+      tiling.z_log2 = 0;
+
+   return tiling;
+}
+
+static uint32_t
+nil_tiling_size_B(struct nil_tiling tiling)
+{
+   const struct nil_extent4d extent_B = nil_tiling_extent_B(tiling);
+   return extent_B.w * extent_B.h * extent_B.d * extent_B.a;
+}
+
+static struct nil_extent4d
+nil_extent4d_B_to_tl(struct nil_extent4d extent_B,
+                     struct nil_tiling tiling)
+{
+   return nil_extent4d_div_round_up(extent_B, nil_tiling_extent_B(tiling));
+}
+
+static struct nil_extent4d
+image_level_extent_B(const struct nil_image *image, uint32_t level)
+{
+   const struct nil_extent4d level_extent_px =
+      nil_minify_extent4d(image->extent_px, level);
+   const struct nil_extent4d level_extent_el =
+      nil_extent4d_px_to_el(level_extent_px, image->format);
+   const uint32_t B_per_el = util_format_get_blocksize(image->format);
+   return nil_extent4d_el_to_B(level_extent_el, B_per_el);
+}
+
+bool
+nil_image_init(struct nouveau_ws_device *dev,
+               struct nil_image *image,
+               const struct nil_image_init_info *restrict info)
+{
+   switch (info->dim) {
+   case NIL_IMAGE_DIM_1D:
+      assert(info->extent_px.h == 1);
+      assert(info->extent_px.d == 1);
+      assert(info->samples == 1);
+      break;
+   case NIL_IMAGE_DIM_2D:
+      assert(info->extent_px.d == 1);
+      break;
+   case NIL_IMAGE_DIM_3D:
+      assert(info->extent_px.a == 1);
+      assert(info->samples == 1);
+      break;
+   }
+
+   *image = (struct nil_image) {
+      .dim = info->dim,
+      .format = info->format,
+      .extent_px = info->extent_px,
+      .num_levels = info->levels,
+      /* TODO: Figure out miptails */
+      .mip_tail_start = info->levels,
+      .num_samples = info->samples,
+   };
+
+   uint64_t layer_size_B = 0;
+   for (uint32_t l = 0; l < info->levels; l++) {
+      struct nil_extent4d lvl_ext_B = image_level_extent_B(image, l);
+
+      /* Tiling is chosen per-level with LOD0 acting as a maximum */
+      struct nil_tiling lvl_tiling = choose_tiling(lvl_ext_B, info->usage);
+
+      /* Align the size to tiles */
+      struct nil_extent4d lvl_tiling_ext_B = nil_tiling_extent_B(lvl_tiling);
+      lvl_ext_B = nil_extent4d_align(lvl_ext_B, lvl_tiling_ext_B);
+
+      image->levels[l] = (struct nil_image_level) {
+         .offset_B = layer_size_B,
+         .tiling = lvl_tiling,
+         .row_stride_B = lvl_ext_B.width,
+      };
+      layer_size_B += (uint64_t)lvl_ext_B.w *
+                      (uint64_t)lvl_ext_B.h *
+                      (uint64_t)lvl_ext_B.d;
+   }
+
+   /* I have no idea why but hardware seems to align layer strides */
+   image->array_stride_B = ALIGN(layer_size_B, 0x800);
+
+   image->size_B = (uint64_t)image->array_stride_B * image->extent_px.a;
+
+   return true;
+}
diff --git a/src/nouveau/nil/nil_image.h b/src/nouveau/nil/nil_image.h
new file mode 100644 (file)
index 0000000..e1d79df
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef NIL_H
+#define NIL_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "util/macros.h"
+#include "util/format/u_format.h"
+
+struct nouveau_ws_device;
+
+enum PACKED nil_image_dim {
+   NIL_IMAGE_DIM_1D = 1,
+   NIL_IMAGE_DIM_2D = 2,
+   NIL_IMAGE_DIM_3D = 3,
+};
+
+enum nil_image_usage_flags {
+   NIL_IMAGE_USAGE_RENDER_TARGET_BIT   = BITFIELD_BIT(0),
+   NIL_IMAGE_USAGE_DEPTH_BIT           = BITFIELD_BIT(1),
+   NIL_IMAGE_USAGE_STENCIL_BIT         = BITFIELD_BIT(2),
+   NIL_IMAGE_USAGE_TEXTURE_BIT         = BITFIELD_BIT(3),
+   NIL_IMAGE_USAGE_STORAGE_BIT         = BITFIELD_BIT(4),
+   NIL_IMAGE_USAGE_CUBE_BIT            = BITFIELD_BIT(5),
+   NIL_IMAGE_USAGE_2D_VIEW_BIT         = BITFIELD_BIT(6),
+};
+
+struct nil_extent4d {
+   union { uint32_t w, width; };
+   union { uint32_t h, height; };
+   union { uint32_t d, depth; };
+   union { uint32_t a, array_len; };
+};
+
+static inline struct nil_extent4d
+nil_extent4d(uint32_t w, uint32_t h, uint32_t d, uint32_t a)
+{
+   struct nil_extent4d e;
+   e.w = w;
+   e.h = h;
+   e.d = d;
+   e.a = a;
+   return e;
+}
+
+#define NIL_GOB_WIDTH_B 64
+#define NIL_GOB_HEIGHT(gob_height_8) ((gob_height_8) ? 8 : 4)
+#define NIL_GOB_DEPTH 1
+#define NIL_MAX_LEVELS 16
+
+struct nil_tiling {
+   bool is_tiled:1;
+   bool gob_height_8:1; /**< GOB height is 4 or 8 */
+   uint8_t y_log2:3; /**< log2 of the Y tile dimension in GOBs */
+   uint8_t z_log2:3; /**< log2 of the Z tile dimension in GOBs */
+};
+
+struct nil_image_init_info {
+   enum nil_image_dim dim;
+   enum pipe_format format;
+
+   struct nil_extent4d extent_px;
+   uint32_t levels;
+   uint32_t samples;
+
+   enum nil_image_usage_flags usage;
+};
+
+/** Represents the data layout of a single slice (level+lod) of an image */
+struct nil_image_level {
+   /** Offset into the image of this level in bytes */
+   uint64_t offset_B;
+
+   /** Tiling for this level */
+   struct nil_tiling tiling;
+
+   /** Stride between rows in bytes */
+   uint32_t row_stride_B;
+};
+
+struct nil_image {
+   enum nil_image_dim dim;
+   enum pipe_format format;
+
+   struct nil_extent4d extent_px;
+   uint8_t num_levels;
+   uint8_t mip_tail_start;
+   uint8_t num_samples;
+
+   struct nil_image_level levels[NIL_MAX_LEVELS];
+
+   uint32_t array_stride_B;
+
+   uint64_t size_B;
+};
+
+bool nil_image_init(struct nouveau_ws_device *dev,
+                    struct nil_image *image,
+                    const struct nil_image_init_info *restrict info);
+
+static inline uint64_t
+nil_image_level_layer_offset_B(const struct nil_image *image,
+                               uint32_t level, uint32_t layer)
+{
+   assert(level < image->num_levels);
+   assert(layer < image->extent_px.array_len);
+   return image->levels[level].offset_B + (layer * image->array_stride_B);
+}
+
+
+#endif /* NIL_H */