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 /* Add the end offset so we can easily recover the size of a level */
175 assert(layout->levels < ARRAY_SIZE(layout->level_offsets_B));
176 layout->level_offsets_B[layout->levels] = offset_B;
177
178 /* Align layer size if we have mipmaps and one miptree is larger than one
179 * page */
180 layout->page_aligned_layers = layout->levels != 1 && offset_B > AIL_PAGESIZE;
181
182 /* Single-layer images are not padded unless they are Z/S */
183 bool zs = util_format_is_depth_or_stencil(layout->format);
184 if (layout->depth_px == 1 && !zs)
185 layout->page_aligned_layers = false;
186
187 /* For writable images, we require page-aligned layers. This appears to be
188 * required for PBE stores, including block stores for colour rendering.
189 * Likewise, we specify the ZLS layer stride in pages, so we need
190 * page-aligned layers for renderable depth/stencil targets.
191 */
192 layout->page_aligned_layers |= layout->writeable_image;
193 layout->page_aligned_layers |= layout->renderable && layout->depth_px > 1;
194
195 if (layout->page_aligned_layers)
196 layout->layer_stride_B = ALIGN_POT(offset_B, AIL_PAGESIZE);
197 else
198 layout->layer_stride_B = offset_B;
199
200 layout->size_B = (uint64_t)layout->layer_stride_B * layout->depth_px;
201 }
202
203 static void
ail_initialize_compression(struct ail_layout * layout)204 ail_initialize_compression(struct ail_layout *layout)
205 {
206 assert(!util_format_is_compressed(layout->format) &&
207 "Compressed pixel formats not supported");
208 assert(util_format_get_blockwidth(layout->format) == 1);
209 assert(util_format_get_blockheight(layout->format) == 1);
210
211 unsigned width_sa =
212 ail_effective_width_sa(layout->width_px, layout->sample_count_sa);
213 unsigned height_sa =
214 ail_effective_height_sa(layout->height_px, layout->sample_count_sa);
215
216 assert(width_sa >= 16 && "Small textures are never compressed");
217 assert(height_sa >= 16 && "Small textures are never compressed");
218
219 layout->metadata_offset_B = layout->size_B;
220
221 width_sa = ALIGN_POT(width_sa, 16);
222 height_sa = ALIGN_POT(height_sa, 16);
223
224 unsigned compbuf_B = 0;
225
226 for (unsigned l = 0; l < layout->levels; ++l) {
227 if (!ail_is_level_compressed(layout, l))
228 break;
229
230 layout->level_offsets_compressed_B[l] = compbuf_B;
231
232 /* The metadata buffer contains 8 bytes per 16x16 compression tile.
233 * Addressing is fully twiddled, so both width and height are padded to
234 * powers-of-two.
235 */
236 unsigned w_tl = DIV_ROUND_UP(util_next_power_of_two(width_sa), 16);
237 unsigned h_tl = DIV_ROUND_UP(util_next_power_of_two(height_sa), 16);
238 unsigned B_per_tl_2 = 8;
239
240 compbuf_B += ALIGN_POT(w_tl * h_tl * B_per_tl_2, AIL_CACHELINE);
241
242 width_sa = DIV_ROUND_UP(width_sa, 2);
243 height_sa = DIV_ROUND_UP(height_sa, 2);
244 }
245
246 layout->compression_layer_stride_B = compbuf_B;
247 layout->size_B +=
248 (uint64_t)(layout->compression_layer_stride_B * layout->depth_px);
249 }
250
251 void
ail_make_miptree(struct ail_layout * layout)252 ail_make_miptree(struct ail_layout *layout)
253 {
254 assert(layout->width_px >= 1 && "Invalid dimensions");
255 assert(layout->height_px >= 1 && "Invalid dimensions");
256 assert(layout->depth_px >= 1 && "Invalid dimensions");
257
258 if (layout->tiling == AIL_TILING_LINEAR) {
259 assert(layout->levels == 1 && "Invalid linear layout");
260 assert(layout->sample_count_sa == 1 &&
261 "Multisampled linear layouts not supported");
262 assert(util_format_get_blockwidth(layout->format) == 1 &&
263 "Strided linear block formats unsupported");
264 assert(util_format_get_blockheight(layout->format) == 1 &&
265 "Strided linear block formats unsupported");
266 } else {
267 assert(layout->linear_stride_B == 0 && "Invalid nonlinear layout");
268 assert(layout->levels >= 1 && "Invalid dimensions");
269 assert(layout->sample_count_sa >= 1 && "Invalid sample count");
270 }
271
272 assert(!(layout->writeable_image &&
273 layout->tiling == AIL_TILING_TWIDDLED_COMPRESSED) &&
274 "Writeable images must not be compressed");
275
276 /* Hardware strides are based on the maximum number of levels, so always
277 * allocate them all.
278 */
279 if (layout->levels > 1) {
280 unsigned major_axis_px = MAX2(layout->width_px, layout->height_px);
281
282 if (layout->mipmapped_z)
283 major_axis_px = MAX2(major_axis_px, layout->depth_px);
284
285 layout->levels = util_logbase2(major_axis_px) + 1;
286 }
287
288 assert(util_format_get_blockdepth(layout->format) == 1 &&
289 "Deep formats unsupported");
290
291 switch (layout->tiling) {
292 case AIL_TILING_LINEAR:
293 ail_initialize_linear(layout);
294 break;
295 case AIL_TILING_TWIDDLED:
296 ail_initialize_twiddled(layout);
297 break;
298 case AIL_TILING_TWIDDLED_COMPRESSED:
299 ail_initialize_twiddled(layout);
300 ail_initialize_compression(layout);
301 break;
302 default:
303 unreachable("Unsupported tiling");
304 }
305
306 layout->size_B = ALIGN_POT(layout->size_B, AIL_CACHELINE);
307 assert(layout->size_B > 0 && "Invalid dimensions");
308 }
309