• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Alyssa Rosenzweig
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "layout.h"
7 
8 static void
ail_initialize_linear(struct ail_layout * layout)9 ail_initialize_linear(struct ail_layout *layout)
10 {
11    /* Select the optimal stride if none is forced */
12    if (layout->linear_stride_B == 0) {
13       uint32_t minimum_stride_B =
14          util_format_get_stride(layout->format, layout->width_px);
15 
16       layout->linear_stride_B = ALIGN_POT(minimum_stride_B, AIL_CACHELINE);
17    }
18 
19    assert((layout->linear_stride_B % 16) == 0 && "Strides must be aligned");
20 
21    /* Layer stride must be cache line aligned to pack linear 2D arrays */
22    layout->layer_stride_B = align64(
23       (uint64_t)layout->linear_stride_B * layout->height_px, AIL_CACHELINE);
24 
25    layout->size_B = layout->layer_stride_B * layout->depth_px;
26 }
27 
28 /*
29  * Get the maximum tile size possible for a given block size. This satisfy
30  * width * height * blocksize = 16384 = page size, so each tile is one page.
31  */
32 static inline struct ail_tile
ail_get_max_tile_size(unsigned blocksize_B)33 ail_get_max_tile_size(unsigned blocksize_B)
34 {
35    /* clang-format off */
36    switch (blocksize_B) {
37    case  1: return (struct ail_tile) { 128, 128 };
38    case  2: return (struct ail_tile) { 128,  64 };
39    case  4: return (struct ail_tile) {  64,  64 };
40    case  8: return (struct ail_tile) {  64,  32 };
41    case 16: return (struct ail_tile) {  32,  32 };
42    case 32: return (struct ail_tile) {  32,  16 };
43    case 64: return (struct ail_tile) {  16,  16 };
44    default: unreachable("Invalid blocksize");
45    }
46    /* clang-format on */
47 }
48 
49 /*
50  * Calculate the number of bytes in a block. This must take both block
51  * dimensions and multisampling into account.
52  */
53 static uint32_t
ail_get_block_size_B(struct ail_layout * layout)54 ail_get_block_size_B(struct ail_layout *layout)
55 {
56    ASSERTED const struct util_format_description *desc =
57       util_format_description(layout->format);
58 
59    assert(((layout->sample_count_sa == 1) ||
60            (desc->block.width == 1 && desc->block.height == 1)) &&
61           "multisampling and block-compression are mutually-exclusive");
62 
63    return util_format_get_blocksize(layout->format) * layout->sample_count_sa;
64 }
65 
66 static void
ail_initialize_twiddled(struct ail_layout * layout)67 ail_initialize_twiddled(struct ail_layout *layout)
68 {
69    unsigned offset_B = 0;
70    unsigned blocksize_B = ail_get_block_size_B(layout);
71    unsigned w_el = util_format_get_nblocksx(layout->format, layout->width_px);
72    unsigned h_el = util_format_get_nblocksy(layout->format, layout->height_px);
73    unsigned bw_px = util_format_get_blockwidth(layout->format);
74    unsigned bh_px = util_format_get_blockheight(layout->format);
75    bool compressed = util_format_is_compressed(layout->format);
76 
77    /* Calculate the tile size used for the large miptree, and the dimensions of
78     * level 0 given that tile size.
79     */
80    struct ail_tile tilesize_el = ail_get_max_tile_size(blocksize_B);
81    unsigned stx_tiles = DIV_ROUND_UP(w_el, tilesize_el.width_el);
82    unsigned sty_tiles = DIV_ROUND_UP(h_el, tilesize_el.height_el);
83    unsigned sarea_tiles = stx_tiles * sty_tiles;
84 
85    /* Calculate which level the small power-of-two miptree begins at. The
86     * power-of-two miptree is used when either the width or the height is
87     * smaller than a single large tile.
88     */
89    unsigned pot_level = 0;
90    unsigned pot_w_px = bw_px * w_el;
91    unsigned pot_h_px = bh_px * h_el;
92    do {
93       unsigned pot_w_el = util_format_get_nblocksx(layout->format, pot_w_px);
94       unsigned pot_h_el = util_format_get_nblocksy(layout->format, pot_h_px);
95       if (pot_w_el < tilesize_el.width_el || pot_h_el < tilesize_el.height_el)
96          break;
97       pot_w_px = u_minify(pot_w_px, 1);
98       pot_h_px = u_minify(pot_h_px, 1);
99       pot_level++;
100    } while (1);
101 
102    /* First allocate the large miptree. All tiles in the large miptree are of
103     * size tilesize_el and have their dimensions given by stx/sty/sarea.
104     */
105    for (unsigned l = 0; l < MIN2(pot_level, layout->levels); ++l) {
106       unsigned tiles = (sarea_tiles >> (2 * l));
107 
108       bool pad_left = (stx_tiles & BITFIELD_MASK(l));
109       bool pad_bottom = (sty_tiles & BITFIELD_MASK(l));
110       bool pad_corner = pad_left && pad_bottom;
111 
112       if (pad_left)
113          tiles += (sty_tiles >> l);
114 
115       if (pad_bottom)
116          tiles += (stx_tiles >> l);
117 
118       if (pad_corner)
119          tiles += 1;
120 
121       unsigned size_el = tiles * tilesize_el.width_el * tilesize_el.height_el;
122       layout->level_offsets_B[l] = offset_B;
123       offset_B = ALIGN_POT(offset_B + (blocksize_B * size_el), AIL_CACHELINE);
124 
125       layout->stride_el[l] = util_format_get_nblocksx(
126          layout->format, u_minify(layout->width_px, l));
127 
128       /* Compressed textures pad the stride in this case */
129       if (compressed && pad_left)
130          layout->stride_el[l]++;
131 
132       layout->tilesize_el[l] = tilesize_el;
133    }
134 
135    /* Then begin the POT miptree. Note that we round up to a power-of-two
136     * outside the loop. That ensures correct handling of cases like 33x33
137     * images, where the round-down error of right-shifting could cause incorrect
138     * tile size calculations.
139     */
140    unsigned potw_el, poth_el;
141    if (compressed) {
142       /* Compressed formats round then minify instead of minifying then rounding
143        */
144       potw_el = u_minify(util_next_power_of_two(w_el), pot_level);
145       poth_el = u_minify(util_next_power_of_two(h_el), pot_level);
146    } else {
147       potw_el = util_next_power_of_two(u_minify(w_el, pot_level));
148       poth_el = util_next_power_of_two(u_minify(h_el, pot_level));
149    }
150 
151    /* Finally we allocate the POT miptree, starting at level pot_level. Each
152     * level uses the largest power-of-two tile that fits the level.
153     */
154    for (unsigned l = pot_level; l < layout->levels; ++l) {
155       unsigned size_el = potw_el * poth_el;
156       layout->level_offsets_B[l] = offset_B;
157       offset_B = ALIGN_POT(offset_B + (blocksize_B * size_el), AIL_CACHELINE);
158 
159       /* The tilesize is based on the true mipmap level size, not the POT
160        * rounded size, except for compressed textures */
161       unsigned tilesize_el;
162       if (compressed)
163          tilesize_el = util_next_power_of_two(MIN2(potw_el, poth_el));
164       else
165          tilesize_el = util_next_power_of_two(u_minify(MIN2(w_el, h_el), l));
166       layout->tilesize_el[l] = (struct ail_tile){tilesize_el, tilesize_el};
167       layout->stride_el[l] = util_format_get_nblocksx(
168          layout->format, u_minify(layout->width_px, l));
169 
170       potw_el = u_minify(potw_el, 1);
171       poth_el = u_minify(poth_el, 1);
172    }
173 
174    /* Align layer size if we have mipmaps and one miptree is larger than one
175     * page */
176    layout->page_aligned_layers = layout->levels != 1 && offset_B > AIL_PAGESIZE;
177 
178    /* Single-layer images are not padded unless they are Z/S */
179    bool zs = util_format_is_depth_or_stencil(layout->format);
180    if (layout->depth_px == 1 && !zs)
181       layout->page_aligned_layers = false;
182 
183    /* For writable images, we require page-aligned layers. This appears to be
184     * required for PBE stores, including block stores for colour rendering.
185     * Likewise, we specify the ZLS layer stride in pages, so we need
186     * page-aligned layers for renderable depth/stencil targets.
187     */
188    layout->page_aligned_layers |= layout->writeable_image;
189    layout->page_aligned_layers |= layout->renderable && layout->depth_px > 1;
190 
191    if (layout->page_aligned_layers)
192       layout->layer_stride_B = ALIGN_POT(offset_B, AIL_PAGESIZE);
193    else
194       layout->layer_stride_B = offset_B;
195 
196    layout->size_B = (uint64_t)layout->layer_stride_B * layout->depth_px;
197 }
198 
199 static void
ail_initialize_compression(struct ail_layout * layout)200 ail_initialize_compression(struct ail_layout *layout)
201 {
202    assert(!util_format_is_compressed(layout->format) &&
203           "Compressed pixel formats not supported");
204    assert(util_format_get_blockwidth(layout->format) == 1);
205    assert(util_format_get_blockheight(layout->format) == 1);
206 
207    unsigned width_sa =
208       ail_effective_width_sa(layout->width_px, layout->sample_count_sa);
209    unsigned height_sa =
210       ail_effective_height_sa(layout->height_px, layout->sample_count_sa);
211 
212    assert(width_sa >= 16 && "Small textures are never compressed");
213    assert(height_sa >= 16 && "Small textures are never compressed");
214 
215    layout->metadata_offset_B = layout->size_B;
216 
217    width_sa = ALIGN_POT(width_sa, 16);
218    height_sa = ALIGN_POT(height_sa, 16);
219 
220    unsigned compbuf_B = 0;
221 
222    for (unsigned l = 0; l < layout->levels; ++l) {
223       if (!ail_is_level_compressed(layout, l))
224          break;
225 
226       layout->level_offsets_compressed_B[l] = compbuf_B;
227 
228       /* The compression buffer seems to have 8 bytes per 16 x 16 sample block. */
229       unsigned cmpw_el = DIV_ROUND_UP(util_next_power_of_two(width_sa), 16);
230       unsigned cmph_el = DIV_ROUND_UP(util_next_power_of_two(height_sa), 16);
231       compbuf_B += ALIGN_POT(cmpw_el * cmph_el * 8, AIL_CACHELINE);
232 
233       width_sa = DIV_ROUND_UP(width_sa, 2);
234       height_sa = DIV_ROUND_UP(height_sa, 2);
235    }
236 
237    layout->compression_layer_stride_B = compbuf_B;
238    layout->size_B +=
239       (uint64_t)(layout->compression_layer_stride_B * layout->depth_px);
240 }
241 
242 void
ail_make_miptree(struct ail_layout * layout)243 ail_make_miptree(struct ail_layout *layout)
244 {
245    assert(layout->width_px >= 1 && "Invalid dimensions");
246    assert(layout->height_px >= 1 && "Invalid dimensions");
247    assert(layout->depth_px >= 1 && "Invalid dimensions");
248 
249    if (layout->tiling == AIL_TILING_LINEAR) {
250       assert(layout->levels == 1 && "Invalid linear layout");
251       assert(layout->sample_count_sa == 1 &&
252              "Multisampled linear layouts not supported");
253       assert(util_format_get_blockwidth(layout->format) == 1 &&
254              "Strided linear block formats unsupported");
255       assert(util_format_get_blockheight(layout->format) == 1 &&
256              "Strided linear block formats unsupported");
257    } else {
258       assert(layout->linear_stride_B == 0 && "Invalid nonlinear layout");
259       assert(layout->levels >= 1 && "Invalid dimensions");
260       assert(layout->sample_count_sa >= 1 && "Invalid sample count");
261    }
262 
263    assert(!(layout->writeable_image &&
264             layout->tiling == AIL_TILING_TWIDDLED_COMPRESSED) &&
265           "Writeable images must not be compressed");
266 
267    /* Hardware strides are based on the maximum number of levels, so always
268     * allocate them all.
269     */
270    if (layout->levels > 1) {
271       unsigned major_axis_px = MAX2(layout->width_px, layout->height_px);
272 
273       if (layout->mipmapped_z)
274          major_axis_px = MAX2(major_axis_px, layout->depth_px);
275 
276       layout->levels = util_logbase2(major_axis_px) + 1;
277    }
278 
279    assert(util_format_get_blockdepth(layout->format) == 1 &&
280           "Deep formats unsupported");
281 
282    switch (layout->tiling) {
283    case AIL_TILING_LINEAR:
284       ail_initialize_linear(layout);
285       break;
286    case AIL_TILING_TWIDDLED:
287       ail_initialize_twiddled(layout);
288       break;
289    case AIL_TILING_TWIDDLED_COMPRESSED:
290       ail_initialize_twiddled(layout);
291       ail_initialize_compression(layout);
292       break;
293    default:
294       unreachable("Unsupported tiling");
295    }
296 
297    layout->size_B = ALIGN_POT(layout->size_B, AIL_CACHELINE);
298    assert(layout->size_B > 0 && "Invalid dimensions");
299 }
300