--- /dev/null
+#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;
+}
--- /dev/null
+#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 */