mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-19 07:08:05 +02:00
For compressed textures, the POT miptree starting size is calculated backwards (POT then minify instead of minify then POT). In addition, the existing POT miptree start level code does not work for compressed textures. Due to the extra block alignment requirement each step of the way, we can no longer get away with nice log-based O(1) math. Switch to a loop. This should be equivalent for uncompressed textures, but yields different results with compression (element size > 1x1). Signed-off-by: Asahi Lina <lina@asahilina.net> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26614>
287 lines
10 KiB
C
287 lines
10 KiB
C
/*
|
|
* Copyright 2022 Alyssa Rosenzweig
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "layout.h"
|
|
|
|
static void
|
|
ail_initialize_linear(struct ail_layout *layout)
|
|
{
|
|
/* Select the optimal stride if none is forced */
|
|
if (layout->linear_stride_B == 0) {
|
|
uint32_t minimum_stride_B =
|
|
util_format_get_stride(layout->format, layout->width_px);
|
|
|
|
layout->linear_stride_B = ALIGN_POT(minimum_stride_B, AIL_CACHELINE);
|
|
}
|
|
|
|
assert((layout->linear_stride_B % 16) == 0 && "Strides must be aligned");
|
|
|
|
/* Layer stride must be cache line aligned to pack linear 2D arrays */
|
|
layout->layer_stride_B = align64(
|
|
(uint64_t)layout->linear_stride_B * layout->height_px, AIL_CACHELINE);
|
|
|
|
layout->size_B = layout->layer_stride_B * layout->depth_px;
|
|
}
|
|
|
|
/*
|
|
* Get the maximum tile size possible for a given block size. This satisfy
|
|
* width * height * blocksize = 16384 = page size, so each tile is one page.
|
|
*/
|
|
static inline struct ail_tile
|
|
ail_get_max_tile_size(unsigned blocksize_B)
|
|
{
|
|
/* clang-format off */
|
|
switch (blocksize_B) {
|
|
case 1: return (struct ail_tile) { 128, 128 };
|
|
case 2: return (struct ail_tile) { 128, 64 };
|
|
case 4: return (struct ail_tile) { 64, 64 };
|
|
case 8: return (struct ail_tile) { 64, 32 };
|
|
case 16: return (struct ail_tile) { 32, 32 };
|
|
case 32: return (struct ail_tile) { 32, 16 };
|
|
case 64: return (struct ail_tile) { 16, 16 };
|
|
default: unreachable("Invalid blocksize");
|
|
}
|
|
/* clang-format on */
|
|
}
|
|
|
|
/*
|
|
* Calculate the number of bytes in a block. This must take both block
|
|
* dimensions and multisampling into account.
|
|
*/
|
|
static uint32_t
|
|
ail_get_block_size_B(struct ail_layout *layout)
|
|
{
|
|
ASSERTED const struct util_format_description *desc =
|
|
util_format_description(layout->format);
|
|
|
|
assert(((layout->sample_count_sa == 1) ||
|
|
(desc->block.width == 1 && desc->block.height == 1)) &&
|
|
"multisampling and block-compression are mutually-exclusive");
|
|
|
|
return util_format_get_blocksize(layout->format) * layout->sample_count_sa;
|
|
}
|
|
|
|
static void
|
|
ail_initialize_twiddled(struct ail_layout *layout)
|
|
{
|
|
unsigned offset_B = 0;
|
|
unsigned blocksize_B = ail_get_block_size_B(layout);
|
|
unsigned w_el = util_format_get_nblocksx(layout->format, layout->width_px);
|
|
unsigned h_el = util_format_get_nblocksy(layout->format, layout->height_px);
|
|
unsigned bw_px = util_format_get_blockwidth(layout->format);
|
|
unsigned bh_px = util_format_get_blockheight(layout->format);
|
|
bool compressed = util_format_is_compressed(layout->format);
|
|
|
|
/* Calculate the tile size used for the large miptree, and the dimensions of
|
|
* level 0 given that tile size.
|
|
*/
|
|
struct ail_tile tilesize_el = ail_get_max_tile_size(blocksize_B);
|
|
unsigned stx_tiles = DIV_ROUND_UP(w_el, tilesize_el.width_el);
|
|
unsigned sty_tiles = DIV_ROUND_UP(h_el, tilesize_el.height_el);
|
|
unsigned sarea_tiles = stx_tiles * sty_tiles;
|
|
|
|
/* Calculate which level the small power-of-two miptree begins at. The
|
|
* power-of-two miptree is used when either the width or the height is
|
|
* smaller than a single large tile.
|
|
*/
|
|
unsigned pot_level = 0;
|
|
unsigned pot_w_px = bw_px * w_el;
|
|
unsigned pot_h_px = bh_px * h_el;
|
|
do {
|
|
unsigned pot_w_el = util_format_get_nblocksx(layout->format, pot_w_px);
|
|
unsigned pot_h_el = util_format_get_nblocksy(layout->format, pot_h_px);
|
|
if (pot_w_el < tilesize_el.width_el || pot_h_el < tilesize_el.height_el)
|
|
break;
|
|
pot_w_px = u_minify(pot_w_px, 1);
|
|
pot_h_px = u_minify(pot_h_px, 1);
|
|
pot_level++;
|
|
} while (1);
|
|
|
|
/* First allocate the large miptree. All tiles in the large miptree are of
|
|
* size tilesize_el and have their dimensions given by stx/sty/sarea.
|
|
*/
|
|
for (unsigned l = 0; l < MIN2(pot_level, layout->levels); ++l) {
|
|
unsigned tiles = (sarea_tiles >> (2 * l));
|
|
|
|
bool pad_left = (stx_tiles & BITFIELD_MASK(l));
|
|
bool pad_bottom = (sty_tiles & BITFIELD_MASK(l));
|
|
bool pad_corner = pad_left && pad_bottom;
|
|
|
|
if (pad_left)
|
|
tiles += (sty_tiles >> l);
|
|
|
|
if (pad_bottom)
|
|
tiles += (stx_tiles >> l);
|
|
|
|
if (pad_corner)
|
|
tiles += 1;
|
|
|
|
unsigned size_el = tiles * tilesize_el.width_el * tilesize_el.height_el;
|
|
layout->level_offsets_B[l] = offset_B;
|
|
offset_B = ALIGN_POT(offset_B + (blocksize_B * size_el), AIL_CACHELINE);
|
|
|
|
layout->tilesize_el[l] = tilesize_el;
|
|
}
|
|
|
|
/* Then begin the POT miptree. Note that we round up to a power-of-two
|
|
* outside the loop. That ensures correct handling of cases like 33x33
|
|
* images, where the round-down error of right-shifting could cause incorrect
|
|
* tile size calculations.
|
|
*/
|
|
unsigned potw_el, poth_el;
|
|
if (compressed) {
|
|
/* Compressed formats round then minify instead of minifying then rounding
|
|
*/
|
|
potw_el = u_minify(util_next_power_of_two(w_el), pot_level);
|
|
poth_el = u_minify(util_next_power_of_two(h_el), pot_level);
|
|
} else {
|
|
potw_el = util_next_power_of_two(u_minify(w_el, pot_level));
|
|
poth_el = util_next_power_of_two(u_minify(h_el, pot_level));
|
|
}
|
|
|
|
/* Finally we allocate the POT miptree, starting at level pot_level. Each
|
|
* level uses the largest power-of-two tile that fits the level.
|
|
*/
|
|
for (unsigned l = pot_level; l < layout->levels; ++l) {
|
|
unsigned size_el = potw_el * poth_el;
|
|
layout->level_offsets_B[l] = offset_B;
|
|
offset_B = ALIGN_POT(offset_B + (blocksize_B * size_el), AIL_CACHELINE);
|
|
|
|
/* The tilesize is based on the true mipmap level size, not the POT
|
|
* rounded size */
|
|
unsigned tilesize_el =
|
|
util_next_power_of_two(u_minify(MIN2(w_el, h_el), l));
|
|
layout->tilesize_el[l] = (struct ail_tile){tilesize_el, tilesize_el};
|
|
|
|
potw_el = u_minify(potw_el, 1);
|
|
poth_el = u_minify(poth_el, 1);
|
|
}
|
|
|
|
/* Align layer size if we have mipmaps and one miptree is larger than one
|
|
* page */
|
|
layout->page_aligned_layers = layout->levels != 1 && offset_B > AIL_PAGESIZE;
|
|
|
|
/* Single-layer images are not padded unless they are Z/S */
|
|
bool zs = util_format_is_depth_or_stencil(layout->format);
|
|
if (layout->depth_px == 1 && !zs)
|
|
layout->page_aligned_layers = false;
|
|
|
|
/* For writable images, we require page-aligned layers. This appears to be
|
|
* required for PBE stores, including block stores for colour rendering.
|
|
* Likewise, we specify the ZLS layer stride in pages, so we need
|
|
* page-aligned layers for renderable depth/stencil targets.
|
|
*/
|
|
layout->page_aligned_layers |= layout->writeable_image;
|
|
layout->page_aligned_layers |= layout->renderable && layout->depth_px > 1;
|
|
|
|
if (layout->page_aligned_layers)
|
|
layout->layer_stride_B = ALIGN_POT(offset_B, AIL_PAGESIZE);
|
|
else
|
|
layout->layer_stride_B = offset_B;
|
|
|
|
layout->size_B = (uint64_t)layout->layer_stride_B * layout->depth_px;
|
|
}
|
|
|
|
static void
|
|
ail_initialize_compression(struct ail_layout *layout)
|
|
{
|
|
assert(!util_format_is_compressed(layout->format) &&
|
|
"Compressed pixel formats not supported");
|
|
assert(util_format_get_blockwidth(layout->format) == 1);
|
|
assert(util_format_get_blockheight(layout->format) == 1);
|
|
|
|
unsigned width_sa =
|
|
ail_effective_width_sa(layout->width_px, layout->sample_count_sa);
|
|
unsigned height_sa =
|
|
ail_effective_height_sa(layout->height_px, layout->sample_count_sa);
|
|
|
|
assert(width_sa >= 16 && "Small textures are never compressed");
|
|
assert(height_sa >= 16 && "Small textures are never compressed");
|
|
|
|
layout->metadata_offset_B = layout->size_B;
|
|
|
|
width_sa = ALIGN_POT(width_sa, 16);
|
|
height_sa = ALIGN_POT(height_sa, 16);
|
|
|
|
unsigned compbuf_B = 0;
|
|
|
|
for (unsigned l = 0; l < layout->levels; ++l) {
|
|
if (width_sa < 16 && height_sa < 16)
|
|
break;
|
|
|
|
layout->level_offsets_compressed_B[l] = compbuf_B;
|
|
|
|
/* The compression buffer seems to have 8 bytes per 16 x 16 sample block. */
|
|
unsigned cmpw_el = DIV_ROUND_UP(util_next_power_of_two(width_sa), 16);
|
|
unsigned cmph_el = DIV_ROUND_UP(util_next_power_of_two(height_sa), 16);
|
|
compbuf_B += ALIGN_POT(cmpw_el * cmph_el * 8, AIL_CACHELINE);
|
|
|
|
width_sa = DIV_ROUND_UP(width_sa, 2);
|
|
height_sa = DIV_ROUND_UP(height_sa, 2);
|
|
}
|
|
|
|
layout->compression_layer_stride_B = compbuf_B;
|
|
layout->size_B +=
|
|
(uint64_t)(layout->compression_layer_stride_B * layout->depth_px);
|
|
}
|
|
|
|
void
|
|
ail_make_miptree(struct ail_layout *layout)
|
|
{
|
|
assert(layout->width_px >= 1 && "Invalid dimensions");
|
|
assert(layout->height_px >= 1 && "Invalid dimensions");
|
|
assert(layout->depth_px >= 1 && "Invalid dimensions");
|
|
|
|
if (layout->tiling == AIL_TILING_LINEAR) {
|
|
assert(layout->levels == 1 && "Invalid linear layout");
|
|
assert(layout->sample_count_sa == 1 &&
|
|
"Multisampled linear layouts not supported");
|
|
assert(util_format_get_blockwidth(layout->format) == 1 &&
|
|
"Strided linear block formats unsupported");
|
|
assert(util_format_get_blockheight(layout->format) == 1 &&
|
|
"Strided linear block formats unsupported");
|
|
} else {
|
|
assert(layout->linear_stride_B == 0 && "Invalid nonlinear layout");
|
|
assert(layout->levels >= 1 && "Invalid dimensions");
|
|
assert(layout->sample_count_sa >= 1 && "Invalid sample count");
|
|
}
|
|
|
|
assert(!(layout->writeable_image &&
|
|
layout->tiling == AIL_TILING_TWIDDLED_COMPRESSED) &&
|
|
"Writeable images must not be compressed");
|
|
|
|
/* Hardware strides are based on the maximum number of levels, so always
|
|
* allocate them all.
|
|
*/
|
|
if (layout->levels > 1) {
|
|
unsigned major_axis_px = MAX2(layout->width_px, layout->height_px);
|
|
|
|
if (layout->mipmapped_z)
|
|
major_axis_px = MAX2(major_axis_px, layout->depth_px);
|
|
|
|
layout->levels = util_logbase2(major_axis_px) + 1;
|
|
}
|
|
|
|
assert(util_format_get_blockdepth(layout->format) == 1 &&
|
|
"Deep formats unsupported");
|
|
|
|
switch (layout->tiling) {
|
|
case AIL_TILING_LINEAR:
|
|
ail_initialize_linear(layout);
|
|
break;
|
|
case AIL_TILING_TWIDDLED:
|
|
ail_initialize_twiddled(layout);
|
|
break;
|
|
case AIL_TILING_TWIDDLED_COMPRESSED:
|
|
ail_initialize_twiddled(layout);
|
|
ail_initialize_compression(layout);
|
|
break;
|
|
default:
|
|
unreachable("Unsupported tiling");
|
|
}
|
|
|
|
layout->size_B = ALIGN_POT(layout->size_B, AIL_CACHELINE);
|
|
assert(layout->size_B > 0 && "Invalid dimensions");
|
|
}
|