nil: Create images

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24326>
This commit is contained in:
Faith Ekstrand 2023-01-30 20:11:49 -06:00 committed by Marge Bot
parent 13abd1dbf2
commit 69967e3f0d
5 changed files with 313 additions and 14 deletions

View file

@ -19,8 +19,8 @@
# SOFTWARE.
libnil_files = files(
'nil.c',
'nil.h',
'nil_image.c',
'nil_image.h',
)
_libnil = static_library(

View file

@ -1 +0,0 @@
#include "nil.h"

View file

@ -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 */

199
src/nouveau/nil/nil_image.c Normal file
View file

@ -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;
}

112
src/nouveau/nil/nil_image.h Normal file
View file

@ -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 */