1 /*
2 * Copyright (C) 2008 VMware, Inc.
3 * Copyright (C) 2014 Broadcom
4 * Copyright (C) 2018-2019 Alyssa Rosenzweig
5 * Copyright (C) 2019-2020 Collabora, Ltd.
6 * Copyright (C) 2024 Arm Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 *
27 */
28
29 #include "pan_texture.h"
30 #include "util/macros.h"
31 #include "util/u_math.h"
32
33 #if PAN_ARCH >= 5
34 /*
35 * Arm Scalable Texture Compression (ASTC) corresponds to just a few formats.
36 * The block dimension is not part of the format. Instead, it is encoded as a
37 * 6-bit tag on the payload pointer. Map the block size for a single dimension.
38 */
39 static inline enum mali_astc_2d_dimension
panfrost_astc_dim_2d(unsigned dim)40 panfrost_astc_dim_2d(unsigned dim)
41 {
42 switch (dim) {
43 case 4:
44 return MALI_ASTC_2D_DIMENSION_4;
45 case 5:
46 return MALI_ASTC_2D_DIMENSION_5;
47 case 6:
48 return MALI_ASTC_2D_DIMENSION_6;
49 case 8:
50 return MALI_ASTC_2D_DIMENSION_8;
51 case 10:
52 return MALI_ASTC_2D_DIMENSION_10;
53 case 12:
54 return MALI_ASTC_2D_DIMENSION_12;
55 default:
56 unreachable("Invalid ASTC dimension");
57 }
58 }
59
60 static inline enum mali_astc_3d_dimension
panfrost_astc_dim_3d(unsigned dim)61 panfrost_astc_dim_3d(unsigned dim)
62 {
63 switch (dim) {
64 case 3:
65 return MALI_ASTC_3D_DIMENSION_3;
66 case 4:
67 return MALI_ASTC_3D_DIMENSION_4;
68 case 5:
69 return MALI_ASTC_3D_DIMENSION_5;
70 case 6:
71 return MALI_ASTC_3D_DIMENSION_6;
72 default:
73 unreachable("Invalid ASTC dimension");
74 }
75 }
76 #endif
77
78 /* Texture addresses are tagged with information about compressed formats.
79 * AFBC uses a bit for whether the colorspace transform is enabled (RGB and
80 * RGBA only).
81 * For ASTC, this is a "stretch factor" encoding the block size. */
82
83 static unsigned
panfrost_compression_tag(const struct util_format_description * desc,enum mali_texture_dimension dim,uint64_t modifier)84 panfrost_compression_tag(const struct util_format_description *desc,
85 enum mali_texture_dimension dim, uint64_t modifier)
86 {
87 #if PAN_ARCH >= 5 && PAN_ARCH <= 8
88 if (drm_is_afbc(modifier)) {
89 unsigned flags =
90 (modifier & AFBC_FORMAT_MOD_YTR) ? MALI_AFBC_SURFACE_FLAG_YTR : 0;
91
92 #if PAN_ARCH >= 6
93 /* Prefetch enable */
94 flags |= MALI_AFBC_SURFACE_FLAG_PREFETCH;
95
96 if (panfrost_afbc_is_wide(modifier))
97 flags |= MALI_AFBC_SURFACE_FLAG_WIDE_BLOCK;
98
99 if (modifier & AFBC_FORMAT_MOD_SPLIT)
100 flags |= MALI_AFBC_SURFACE_FLAG_SPLIT_BLOCK;
101 #endif
102
103 #if PAN_ARCH >= 7
104 /* Tiled headers */
105 if (modifier & AFBC_FORMAT_MOD_TILED)
106 flags |= MALI_AFBC_SURFACE_FLAG_TILED_HEADER;
107
108 /* Used to make sure AFBC headers don't point outside the AFBC
109 * body. HW is using the AFBC surface stride to do this check,
110 * which doesn't work for 3D textures because the surface
111 * stride does not cover the body. Only supported on v7+.
112 */
113 if (dim != MALI_TEXTURE_DIMENSION_3D)
114 flags |= MALI_AFBC_SURFACE_FLAG_CHECK_PAYLOAD_RANGE;
115 #endif
116
117 return flags;
118 } else if (desc->layout == UTIL_FORMAT_LAYOUT_ASTC) {
119 if (desc->block.depth > 1) {
120 return (panfrost_astc_dim_3d(desc->block.depth) << 4) |
121 (panfrost_astc_dim_3d(desc->block.height) << 2) |
122 panfrost_astc_dim_3d(desc->block.width);
123 } else {
124 return (panfrost_astc_dim_2d(desc->block.height) << 3) |
125 panfrost_astc_dim_2d(desc->block.width);
126 }
127 }
128 #endif
129
130 /* Tags are not otherwise used */
131 return 0;
132 }
133
134 /* Following the texture descriptor is a number of descriptors. How many? */
135
136 static unsigned
panfrost_texture_num_elements(const struct pan_image_view * iview)137 panfrost_texture_num_elements(const struct pan_image_view *iview)
138 {
139 unsigned levels = 1 + iview->last_level - iview->first_level;
140 unsigned layers = 1 + iview->last_layer - iview->first_layer;
141 unsigned nr_samples = pan_image_view_get_nr_samples(iview);
142
143 return levels * layers * MAX2(nr_samples, 1);
144 }
145
146 /* Conservative estimate of the size of the texture payload a priori.
147 * Average case, size equal to the actual size. Worst case, off by 2x (if
148 * a manual stride is not needed on a linear texture). Returned value
149 * must be greater than or equal to the actual size, so it's safe to use
150 * as an allocation amount */
151
152 unsigned
GENX(panfrost_estimate_texture_payload_size)153 GENX(panfrost_estimate_texture_payload_size)(const struct pan_image_view *iview)
154 {
155 size_t element_size;
156
157 #if PAN_ARCH >= 9
158 element_size = pan_size(PLANE);
159
160 /* 2-plane and 3-plane YUV use two plane descriptors. */
161 if (panfrost_format_is_yuv(iview->format) && iview->planes[1] != NULL)
162 element_size *= 2;
163 #elif PAN_ARCH == 7
164 if (panfrost_format_is_yuv(iview->format))
165 element_size = pan_size(MULTIPLANAR_SURFACE);
166 else
167 element_size = pan_size(SURFACE_WITH_STRIDE);
168 #else
169 /* Assume worst case. Overestimates on Midgard, but that's ok. */
170 element_size = pan_size(SURFACE_WITH_STRIDE);
171 #endif
172
173 unsigned elements = panfrost_texture_num_elements(iview);
174
175 return element_size * elements;
176 }
177
178 static void
panfrost_get_surface_strides(const struct pan_image_layout * layout,unsigned l,int32_t * row_stride,int32_t * surf_stride)179 panfrost_get_surface_strides(const struct pan_image_layout *layout, unsigned l,
180 int32_t *row_stride, int32_t *surf_stride)
181 {
182 const struct pan_image_slice_layout *slice = &layout->slices[l];
183
184 if (drm_is_afbc(layout->modifier)) {
185 /* Pre v7 don't have a row stride field. This field is
186 * repurposed as a Y offset which we don't use */
187 *row_stride = PAN_ARCH < 7 ? 0 : slice->row_stride;
188 *surf_stride = slice->afbc.surface_stride;
189 } else {
190 *row_stride = slice->row_stride;
191 *surf_stride = slice->surface_stride;
192 }
193 }
194
195 static uint64_t
panfrost_get_surface_pointer(const struct pan_image_layout * layout,enum mali_texture_dimension dim,uint64_t base,unsigned l,unsigned i,unsigned s)196 panfrost_get_surface_pointer(const struct pan_image_layout *layout,
197 enum mali_texture_dimension dim, uint64_t base,
198 unsigned l, unsigned i, unsigned s)
199 {
200 unsigned offset;
201
202 if (layout->dim == MALI_TEXTURE_DIMENSION_3D) {
203 assert(!s);
204 offset =
205 layout->slices[l].offset + i * panfrost_get_layer_stride(layout, l);
206 } else {
207 offset = panfrost_texture_offset(layout, l, i, s);
208 }
209
210 return base + offset;
211 }
212
213 struct pan_image_section_info {
214 uint64_t pointer;
215 int32_t row_stride;
216 int32_t surface_stride;
217 };
218
219 static struct pan_image_section_info
get_image_section_info(const struct pan_image_view * iview,const struct pan_image * plane,unsigned level,unsigned index,unsigned sample)220 get_image_section_info(const struct pan_image_view *iview,
221 const struct pan_image *plane, unsigned level,
222 unsigned index, unsigned sample)
223 {
224 const struct util_format_description *desc =
225 util_format_description(iview->format);
226 uint64_t base = plane->data.base + plane->data.offset;
227 struct pan_image_section_info info = {0};
228
229 if (iview->buf.size) {
230 assert(iview->dim == MALI_TEXTURE_DIMENSION_1D);
231 base += iview->buf.offset;
232 }
233
234 /* v4 does not support compression */
235 assert(PAN_ARCH >= 5 || !drm_is_afbc(plane->layout.modifier));
236 assert(PAN_ARCH >= 5 || desc->layout != UTIL_FORMAT_LAYOUT_ASTC);
237
238 /* panfrost_compression_tag() wants the dimension of the resource, not the
239 * one of the image view (those might differ).
240 */
241 unsigned tag = panfrost_compression_tag(desc, plane->layout.dim,
242 plane->layout.modifier);
243
244 info.pointer = panfrost_get_surface_pointer(
245 &plane->layout, iview->dim, base | tag, level, index, sample);
246 panfrost_get_surface_strides(&plane->layout, level, &info.row_stride,
247 &info.surface_stride);
248
249 return info;
250 }
251
252 #if PAN_ARCH <= 7
253 static void
panfrost_emit_surface_with_stride(const struct pan_image_section_info * section,void ** payload)254 panfrost_emit_surface_with_stride(const struct pan_image_section_info *section,
255 void **payload)
256 {
257 pan_cast_and_pack(*payload, SURFACE_WITH_STRIDE, cfg) {
258 cfg.pointer = section->pointer;
259 cfg.row_stride = section->row_stride;
260 cfg.surface_stride = section->surface_stride;
261 }
262 *payload += pan_size(SURFACE_WITH_STRIDE);
263 }
264 #endif
265
266 #if PAN_ARCH == 7
267 static void
panfrost_emit_multiplanar_surface(const struct pan_image_section_info * sections,void ** payload)268 panfrost_emit_multiplanar_surface(const struct pan_image_section_info *sections,
269 void **payload)
270 {
271 assert(sections[2].row_stride == 0 ||
272 sections[1].row_stride == sections[2].row_stride);
273
274 pan_cast_and_pack(*payload, MULTIPLANAR_SURFACE, cfg) {
275 cfg.plane_0_pointer = sections[0].pointer;
276 cfg.plane_0_row_stride = sections[0].row_stride;
277 cfg.plane_1_2_row_stride = sections[1].row_stride;
278 cfg.plane_1_pointer = sections[1].pointer;
279 cfg.plane_2_pointer = sections[2].pointer;
280 }
281 *payload += pan_size(MULTIPLANAR_SURFACE);
282 }
283 #endif
284
285 #if PAN_ARCH >= 9
286
287 /* clang-format off */
288 #define CLUMP_FMT(pipe, mali) [PIPE_FORMAT_ ## pipe] = MALI_CLUMP_FORMAT_ ## mali
289 static enum mali_clump_format special_clump_formats[PIPE_FORMAT_COUNT] = {
290 CLUMP_FMT(X32_S8X24_UINT, X32S8X24),
291 CLUMP_FMT(X24S8_UINT, X24S8),
292 CLUMP_FMT(S8X24_UINT, S8X24),
293 CLUMP_FMT(S8_UINT, S8),
294 CLUMP_FMT(L4A4_UNORM, L4A4),
295 CLUMP_FMT(L8A8_UNORM, L8A8),
296 CLUMP_FMT(L8A8_UINT, L8A8),
297 CLUMP_FMT(L8A8_SINT, L8A8),
298 CLUMP_FMT(A8_UNORM, A8),
299 CLUMP_FMT(A8_UINT, A8),
300 CLUMP_FMT(A8_SINT, A8),
301 CLUMP_FMT(ETC1_RGB8, ETC2_RGB8),
302 CLUMP_FMT(ETC2_RGB8, ETC2_RGB8),
303 CLUMP_FMT(ETC2_SRGB8, ETC2_RGB8),
304 CLUMP_FMT(ETC2_RGB8A1, ETC2_RGB8A1),
305 CLUMP_FMT(ETC2_SRGB8A1, ETC2_RGB8A1),
306 CLUMP_FMT(ETC2_RGBA8, ETC2_RGBA8),
307 CLUMP_FMT(ETC2_SRGBA8, ETC2_RGBA8),
308 CLUMP_FMT(ETC2_R11_UNORM, ETC2_R11_UNORM),
309 CLUMP_FMT(ETC2_R11_SNORM, ETC2_R11_SNORM),
310 CLUMP_FMT(ETC2_RG11_UNORM, ETC2_RG11_UNORM),
311 CLUMP_FMT(ETC2_RG11_SNORM, ETC2_RG11_SNORM),
312 CLUMP_FMT(DXT1_RGB, BC1_UNORM),
313 CLUMP_FMT(DXT1_RGBA, BC1_UNORM),
314 CLUMP_FMT(DXT1_SRGB, BC1_UNORM),
315 CLUMP_FMT(DXT1_SRGBA, BC1_UNORM),
316 CLUMP_FMT(DXT3_RGBA, BC2_UNORM),
317 CLUMP_FMT(DXT3_SRGBA, BC2_UNORM),
318 CLUMP_FMT(DXT5_RGBA, BC3_UNORM),
319 CLUMP_FMT(DXT5_SRGBA, BC3_UNORM),
320 CLUMP_FMT(RGTC1_UNORM, BC4_UNORM),
321 CLUMP_FMT(RGTC1_SNORM, BC4_SNORM),
322 CLUMP_FMT(RGTC2_UNORM, BC5_UNORM),
323 CLUMP_FMT(RGTC2_SNORM, BC5_SNORM),
324 CLUMP_FMT(BPTC_RGB_FLOAT, BC6H_SF16),
325 CLUMP_FMT(BPTC_RGB_UFLOAT, BC6H_UF16),
326 CLUMP_FMT(BPTC_RGBA_UNORM, BC7_UNORM),
327 CLUMP_FMT(BPTC_SRGBA, BC7_UNORM),
328 };
329 #undef CLUMP_FMT
330 /* clang-format on */
331
332 static enum mali_clump_format
panfrost_clump_format(enum pipe_format format)333 panfrost_clump_format(enum pipe_format format)
334 {
335 /* First, try a special clump format. Note that the 0 encoding is for a
336 * raw clump format, which will never be in the special table.
337 */
338 if (special_clump_formats[format])
339 return special_clump_formats[format];
340
341 /* Else, it's a raw format. Raw formats must not be compressed. */
342 assert(!util_format_is_compressed(format));
343
344 /* YUV-sampling has special cases */
345 if (panfrost_format_is_yuv(format)) {
346 switch (format) {
347 case PIPE_FORMAT_R8G8_R8B8_UNORM:
348 case PIPE_FORMAT_G8R8_B8R8_UNORM:
349 case PIPE_FORMAT_R8B8_R8G8_UNORM:
350 case PIPE_FORMAT_B8R8_G8R8_UNORM:
351 return MALI_CLUMP_FORMAT_Y8_UV8_422;
352 case PIPE_FORMAT_R8_G8B8_420_UNORM:
353 case PIPE_FORMAT_R8_B8G8_420_UNORM:
354 case PIPE_FORMAT_R8_G8_B8_420_UNORM:
355 case PIPE_FORMAT_R8_B8_G8_420_UNORM:
356 return MALI_CLUMP_FORMAT_Y8_UV8_420;
357 case PIPE_FORMAT_R10_G10B10_420_UNORM:
358 return MALI_CLUMP_FORMAT_Y10_UV10_420;
359 case PIPE_FORMAT_R10_G10B10_422_UNORM:
360 return MALI_CLUMP_FORMAT_Y10_UV10_422;
361 default:
362 unreachable("unhandled clump format");
363 }
364 }
365
366 /* Select the appropriate raw format. */
367 switch (util_format_get_blocksize(format)) {
368 case 1:
369 return MALI_CLUMP_FORMAT_RAW8;
370 case 2:
371 return MALI_CLUMP_FORMAT_RAW16;
372 case 3:
373 return MALI_CLUMP_FORMAT_RAW24;
374 case 4:
375 return MALI_CLUMP_FORMAT_RAW32;
376 case 6:
377 return MALI_CLUMP_FORMAT_RAW48;
378 case 8:
379 return MALI_CLUMP_FORMAT_RAW64;
380 case 12:
381 return MALI_CLUMP_FORMAT_RAW96;
382 case 16:
383 return MALI_CLUMP_FORMAT_RAW128;
384 default:
385 unreachable("Invalid bpp");
386 }
387 }
388
389 static enum mali_afbc_superblock_size
translate_superblock_size(uint64_t modifier)390 translate_superblock_size(uint64_t modifier)
391 {
392 assert(drm_is_afbc(modifier));
393
394 switch (modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
395 case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
396 return MALI_AFBC_SUPERBLOCK_SIZE_16X16;
397 case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
398 return MALI_AFBC_SUPERBLOCK_SIZE_32X8;
399 case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4:
400 return MALI_AFBC_SUPERBLOCK_SIZE_64X4;
401 default:
402 unreachable("Invalid superblock size");
403 }
404 }
405
406 static void
panfrost_emit_plane(const struct pan_image_view * iview,const struct pan_image_section_info * sections,int plane_index,unsigned level,void ** payload)407 panfrost_emit_plane(const struct pan_image_view *iview,
408 const struct pan_image_section_info *sections,
409 int plane_index, unsigned level, void **payload)
410 {
411 const struct util_format_description *desc =
412 util_format_description(iview->format);
413 const struct pan_image *plane = util_format_has_stencil(desc)
414 ? pan_image_view_get_s_plane(iview)
415 : pan_image_view_get_plane(iview, plane_index);
416 const struct pan_image_layout *layout = &plane->layout;
417 int32_t row_stride = sections[plane_index].row_stride;
418 int32_t surface_stride = sections[plane_index].surface_stride;
419 uint64_t pointer = sections[plane_index].pointer;
420
421 assert(row_stride >= 0 && surface_stride >= 0 && "negative stride");
422
423 bool afbc = drm_is_afbc(layout->modifier);
424 bool afrc = drm_is_afrc(layout->modifier);
425 // TODO: this isn't technically guaranteed to be YUV, but it is in practice.
426 bool is_chroma_2p =
427 desc->layout == UTIL_FORMAT_LAYOUT_PLANAR3 && plane_index > 0;
428
429 pan_cast_and_pack(*payload, PLANE, cfg) {
430 cfg.pointer = pointer;
431 cfg.row_stride = row_stride;
432 cfg.size = layout->data_size - layout->slices[level].offset;
433
434 if (is_chroma_2p) {
435 cfg.two_plane_yuv_chroma.secondary_pointer =
436 sections[plane_index + 1].pointer;
437 } else if (!panfrost_format_is_yuv(layout->format)) {
438 cfg.slice_stride = layout->nr_samples
439 ? surface_stride
440 : panfrost_get_layer_stride(layout, level);
441 }
442
443 if (desc->layout == UTIL_FORMAT_LAYOUT_ASTC) {
444 assert(!afbc);
445 assert(!afrc);
446
447 if (desc->block.depth > 1) {
448 cfg.plane_type = MALI_PLANE_TYPE_ASTC_3D;
449 cfg.astc._3d.block_width = panfrost_astc_dim_3d(desc->block.width);
450 cfg.astc._3d.block_height =
451 panfrost_astc_dim_3d(desc->block.height);
452 cfg.astc._3d.block_depth = panfrost_astc_dim_3d(desc->block.depth);
453 } else {
454 cfg.plane_type = MALI_PLANE_TYPE_ASTC_2D;
455 cfg.astc._2d.block_width = panfrost_astc_dim_2d(desc->block.width);
456 cfg.astc._2d.block_height =
457 panfrost_astc_dim_2d(desc->block.height);
458 }
459
460 bool srgb = (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB);
461
462 /* Mesa does not advertise _HDR formats yet */
463 cfg.astc.decode_hdr = false;
464
465 /* sRGB formats decode to RGBA8 sRGB, which is narrow.
466 *
467 * Non-sRGB formats decode to RGBA16F which is wide except if decode
468 * precision is set to GL_RGBA8 for that texture.
469 */
470 cfg.astc.decode_wide = !srgb && !iview->astc.narrow;
471 } else if (afbc) {
472 cfg.plane_type = MALI_PLANE_TYPE_AFBC;
473 cfg.afbc.superblock_size = translate_superblock_size(layout->modifier);
474 cfg.afbc.ytr = (layout->modifier & AFBC_FORMAT_MOD_YTR);
475 cfg.afbc.split_block = (layout->modifier & AFBC_FORMAT_MOD_SPLIT);
476 cfg.afbc.tiled_header = (layout->modifier & AFBC_FORMAT_MOD_TILED);
477 cfg.afbc.prefetch = true;
478 cfg.afbc.compression_mode =
479 GENX(pan_afbc_compression_mode)(iview->format);
480 cfg.afbc.header_stride = layout->slices[level].afbc.header_size;
481 } else if (afrc) {
482 #if PAN_ARCH >= 10
483 struct pan_afrc_format_info finfo =
484 panfrost_afrc_get_format_info(iview->format);
485
486 cfg.plane_type = MALI_PLANE_TYPE_AFRC;
487 cfg.afrc.block_size =
488 GENX(pan_afrc_block_size)(layout->modifier, plane_index);
489 cfg.afrc.format =
490 GENX(pan_afrc_format)(finfo, layout->modifier, plane_index);
491 #endif
492 } else {
493 cfg.plane_type =
494 is_chroma_2p ? MALI_PLANE_TYPE_CHROMA_2P : MALI_PLANE_TYPE_GENERIC;
495 cfg.clump_format = panfrost_clump_format(iview->format);
496 }
497
498 if (!afbc && !afrc) {
499 if (layout->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED)
500 cfg.clump_ordering = MALI_CLUMP_ORDERING_TILED_U_INTERLEAVED;
501 else
502 cfg.clump_ordering = MALI_CLUMP_ORDERING_LINEAR;
503 }
504 }
505 *payload += pan_size(PLANE);
506 }
507 #endif
508
509 static void
panfrost_emit_surface(const struct pan_image_view * iview,unsigned level,unsigned index,unsigned sample,void ** payload)510 panfrost_emit_surface(const struct pan_image_view *iview, unsigned level,
511 unsigned index, unsigned sample, void **payload)
512 {
513 #if PAN_ARCH == 7 || PAN_ARCH >= 9
514 if (panfrost_format_is_yuv(iview->format)) {
515 struct pan_image_section_info sections[MAX_IMAGE_PLANES] = {0};
516 unsigned plane_count = 0;
517
518 for (int i = 0; i < MAX_IMAGE_PLANES; i++) {
519 const struct pan_image *plane = pan_image_view_get_plane(iview, i);
520
521 if (!plane)
522 break;
523
524 sections[i] =
525 get_image_section_info(iview, plane, level, index, sample);
526 plane_count++;
527 }
528
529 #if PAN_ARCH >= 9
530 /* 3-plane YUV is submitted using two PLANE descriptors, where the
531 * second one is of type CHROMA_2P */
532 panfrost_emit_plane(iview, sections, 0, level, payload);
533
534 if (plane_count > 1) {
535 /* 3-plane YUV requires equal stride for both chroma planes */
536 assert(plane_count == 2 ||
537 sections[1].row_stride == sections[2].row_stride);
538 panfrost_emit_plane(iview, sections, 1, level, payload);
539 }
540 #else
541 if (plane_count > 1)
542 panfrost_emit_multiplanar_surface(sections, payload);
543 else
544 panfrost_emit_surface_with_stride(sections, payload);
545 #endif
546 return;
547 }
548 #endif
549
550 const struct util_format_description *fdesc =
551 util_format_description(iview->format);
552
553 /* In case of multiplanar depth/stencil, the stencil is always on
554 * plane 1. Combined depth/stencil only has one plane, so depth
555 * will be on plane 0 in either case.
556 */
557 const struct pan_image *plane = util_format_has_stencil(fdesc)
558 ? pan_image_view_get_s_plane(iview)
559 : pan_image_view_get_plane(iview, 0);
560 assert(plane != NULL);
561
562 struct pan_image_section_info section =
563 get_image_section_info(iview, plane, level, index, sample);
564
565 #if PAN_ARCH >= 9
566 panfrost_emit_plane(iview, §ion, 0, level, payload);
567 #else
568 panfrost_emit_surface_with_stride(§ion, payload);
569 #endif
570 }
571
572 static void
panfrost_emit_texture_payload(const struct pan_image_view * iview,void * payload)573 panfrost_emit_texture_payload(const struct pan_image_view *iview, void *payload)
574 {
575 unsigned nr_samples =
576 PAN_ARCH <= 7 ? pan_image_view_get_nr_samples(iview) : 1;
577
578 /* Inject the addresses in, interleaving array indices, mip levels,
579 * cube faces, and strides in that order. On Bifrost and older, each
580 * sample had its own surface descriptor; on Valhall, they are fused
581 * into a single plane descriptor.
582 */
583
584 #if PAN_ARCH >= 7
585 /* V7 and later treats faces as extra layers */
586 for (int layer = iview->first_layer; layer <= iview->last_layer; ++layer) {
587 for (int sample = 0; sample < nr_samples; ++sample) {
588 for (int level = iview->first_level; level <= iview->last_level; ++level) {
589 panfrost_emit_surface(iview, level, layer, sample, &payload);
590 }
591 }
592 }
593 #else
594 unsigned first_layer = iview->first_layer, last_layer = iview->last_layer;
595 unsigned face_count = 1;
596
597 if (iview->dim == MALI_TEXTURE_DIMENSION_CUBE) {
598 first_layer /= 6;
599 last_layer /= 6;
600 face_count = 6;
601 }
602
603 /* V6 and earlier has a different memory-layout */
604 for (int layer = first_layer; layer <= last_layer; ++layer) {
605 for (int level = iview->first_level; level <= iview->last_level; ++level) {
606 /* order of face and sample doesn't matter; we can only have multiple
607 * of one or the other (no support for multisampled cubemaps)
608 */
609 for (int face = 0; face < face_count; ++face) {
610 for (int sample = 0; sample < nr_samples; ++sample) {
611 panfrost_emit_surface(iview, level, (face_count * layer) + face,
612 sample, &payload);
613 }
614 }
615 }
616 }
617 #endif
618 }
619
620 #if PAN_ARCH <= 7
621 /* Map modifiers to mali_texture_layout for packing in a texture descriptor */
622
623 static enum mali_texture_layout
panfrost_modifier_to_layout(uint64_t modifier)624 panfrost_modifier_to_layout(uint64_t modifier)
625 {
626 if (drm_is_afbc(modifier))
627 return MALI_TEXTURE_LAYOUT_AFBC;
628 else if (modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED)
629 return MALI_TEXTURE_LAYOUT_TILED;
630 else if (modifier == DRM_FORMAT_MOD_LINEAR)
631 return MALI_TEXTURE_LAYOUT_LINEAR;
632 else
633 unreachable("Invalid modifer");
634 }
635 #endif
636
637 #if PAN_ARCH >= 7
638 void
GENX(panfrost_texture_swizzle_replicate_x)639 GENX(panfrost_texture_swizzle_replicate_x)(struct pan_image_view *iview)
640 {
641 /* v7+ doesn't have an _RRRR component order, combine the
642 * user swizzle with a .XXXX swizzle to emulate that. */
643 assert(util_format_is_depth_or_stencil(iview->format));
644
645 static const unsigned char replicate_x[4] = {
646 PIPE_SWIZZLE_X,
647 PIPE_SWIZZLE_X,
648 PIPE_SWIZZLE_X,
649 PIPE_SWIZZLE_X,
650 };
651
652 util_format_compose_swizzles(replicate_x, iview->swizzle, iview->swizzle);
653 }
654 #endif
655
656 #if PAN_ARCH == 7
657 void
GENX(panfrost_texture_afbc_reswizzle)658 GENX(panfrost_texture_afbc_reswizzle)(struct pan_image_view *iview)
659 {
660 /* v7 (only) restricts component orders when AFBC is in use.
661 * Rather than restrict AFBC for all non-canonical component orders, we use
662 * an allowed component order with an invertible swizzle composed.
663 * This allows us to support AFBC(BGR) as well as AFBC(RGB).
664 */
665 assert(!util_format_is_depth_or_stencil(iview->format));
666 assert(!panfrost_format_is_yuv(iview->format));
667 assert(panfrost_format_supports_afbc(PAN_ARCH, iview->format));
668
669 uint32_t mali_format =
670 GENX(panfrost_format_from_pipe_format)(iview->format)->hw;
671
672 enum mali_rgb_component_order orig = mali_format & BITFIELD_MASK(12);
673 struct pan_decomposed_swizzle decomposed = GENX(pan_decompose_swizzle)(orig);
674
675 /* Apply the new component order */
676 if (orig != decomposed.pre)
677 iview->format = util_format_rgb_to_bgr(iview->format);
678 /* Only RGB<->BGR should be allowed for AFBC */
679 assert(iview->format != PIPE_FORMAT_NONE);
680 assert(decomposed.pre ==
681 (GENX(panfrost_format_from_pipe_format)(iview->format)->hw &
682 BITFIELD_MASK(12)));
683
684 /* Compose the new swizzle */
685 util_format_compose_swizzles(decomposed.post, iview->swizzle,
686 iview->swizzle);
687 }
688 #endif
689
690 /*
691 * Generates a texture descriptor. Ideally, descriptors are immutable after the
692 * texture is created, so we can keep these hanging around in GPU memory in a
693 * dedicated BO and not have to worry. In practice there are some minor gotchas
694 * with this (the driver sometimes will change the format of a texture on the
695 * fly for compression) but it's fast enough to just regenerate the descriptor
696 * in those cases, rather than monkeypatching at drawtime. A texture descriptor
697 * consists of a 32-byte header followed by pointers.
698 */
699 void
GENX(panfrost_new_texture)700 GENX(panfrost_new_texture)(const struct pan_image_view *iview,
701 struct mali_texture_packed *out,
702 const struct panfrost_ptr *payload)
703 {
704 const struct util_format_description *desc =
705 util_format_description(iview->format);
706 const struct pan_image *first_plane = pan_image_view_get_first_plane(iview);
707 const struct pan_image_layout *layout = &first_plane->layout;
708 uint32_t mali_format =
709 GENX(panfrost_format_from_pipe_format)(iview->format)->hw;
710
711 if (desc->layout == UTIL_FORMAT_LAYOUT_ASTC && iview->astc.narrow &&
712 desc->colorspace != UTIL_FORMAT_COLORSPACE_SRGB) {
713 mali_format = MALI_PACK_FMT(RGBA8_UNORM, RGBA, L);
714 }
715
716 panfrost_emit_texture_payload(iview, payload->cpu);
717
718 unsigned array_size = iview->last_layer - iview->first_layer + 1;
719
720 /* If this is a cubemap, we expect the number of layers to be a multiple
721 * of 6.
722 */
723 if (iview->dim == MALI_TEXTURE_DIMENSION_CUBE) {
724 assert(array_size % 6 == 0);
725 array_size /= 6;
726 }
727
728 /* Multiplanar YUV textures require 2 surface descriptors. */
729 if (panfrost_format_is_yuv(iview->format) && PAN_ARCH >= 9 &&
730 pan_image_view_get_plane(iview, 1) != NULL)
731 array_size *= 2;
732
733 unsigned width, height, depth;
734
735 if (iview->buf.size) {
736 assert(iview->dim == MALI_TEXTURE_DIMENSION_1D);
737 assert(!iview->first_level && !iview->last_level);
738 assert(!iview->first_layer && !iview->last_layer);
739 assert(layout->nr_samples == 1);
740 assert(layout->height == 1 && layout->depth == 1);
741 assert(iview->buf.offset + iview->buf.size <= layout->width);
742 width = iview->buf.size;
743 height = 1;
744 depth = 1;
745 } else {
746 width = u_minify(layout->width, iview->first_level);
747 height = u_minify(layout->height, iview->first_level);
748 depth = u_minify(layout->depth, iview->first_level);
749 if (util_format_is_compressed(layout->format) &&
750 !util_format_is_compressed(iview->format)) {
751 width =
752 DIV_ROUND_UP(width, util_format_get_blockwidth(layout->format));
753 height =
754 DIV_ROUND_UP(height, util_format_get_blockheight(layout->format));
755 depth =
756 DIV_ROUND_UP(depth, util_format_get_blockdepth(layout->format));
757 assert(util_format_get_blockwidth(iview->format) == 1);
758 assert(util_format_get_blockheight(iview->format) == 1);
759 assert(util_format_get_blockheight(iview->format) == 1);
760 assert(iview->last_level == iview->first_level);
761 }
762 }
763
764 pan_pack(out, TEXTURE, cfg) {
765 cfg.dimension = iview->dim;
766 cfg.format = mali_format;
767 cfg.width = width;
768 cfg.height = height;
769 if (iview->dim == MALI_TEXTURE_DIMENSION_3D)
770 cfg.depth = depth;
771 else
772 cfg.sample_count = layout->nr_samples;
773 cfg.swizzle = panfrost_translate_swizzle_4(iview->swizzle);
774 #if PAN_ARCH >= 9
775 cfg.texel_interleave = (layout->modifier != DRM_FORMAT_MOD_LINEAR) ||
776 util_format_is_compressed(iview->format);
777 #else
778 cfg.texel_ordering = panfrost_modifier_to_layout(layout->modifier);
779 #endif
780 cfg.levels = iview->last_level - iview->first_level + 1;
781 cfg.array_size = array_size;
782
783 #if PAN_ARCH >= 6
784 cfg.surfaces = payload->gpu;
785
786 /* We specify API-level LOD clamps in the sampler descriptor
787 * and use these clamps simply for bounds checking.
788 */
789 cfg.minimum_lod = 0;
790 cfg.maximum_lod = cfg.levels - 1;
791 #endif
792 }
793 }
794
795 #if PAN_ARCH >= 9
796 enum mali_afbc_compression_mode
GENX(pan_afbc_compression_mode)797 GENX(pan_afbc_compression_mode)(enum pipe_format format)
798 {
799 /* There's a special case for texturing the stencil part from a combined
800 * depth/stencil texture, handle it separately.
801 */
802 if (format == PIPE_FORMAT_X24S8_UINT)
803 return MALI_AFBC_COMPRESSION_MODE_X24S8;
804
805 /* Otherwise, map canonical formats to the hardware enum. This only
806 * needs to handle the subset of formats returned by
807 * panfrost_afbc_format.
808 */
809 /* clang-format off */
810 switch (panfrost_afbc_format(PAN_ARCH, format)) {
811 case PAN_AFBC_MODE_R8: return MALI_AFBC_COMPRESSION_MODE_R8;
812 case PAN_AFBC_MODE_R8G8: return MALI_AFBC_COMPRESSION_MODE_R8G8;
813 case PAN_AFBC_MODE_R5G6B5: return MALI_AFBC_COMPRESSION_MODE_R5G6B5;
814 case PAN_AFBC_MODE_R4G4B4A4: return MALI_AFBC_COMPRESSION_MODE_R4G4B4A4;
815 case PAN_AFBC_MODE_R5G5B5A1: return MALI_AFBC_COMPRESSION_MODE_R5G5B5A1;
816 case PAN_AFBC_MODE_R8G8B8: return MALI_AFBC_COMPRESSION_MODE_R8G8B8;
817 case PAN_AFBC_MODE_R8G8B8A8: return MALI_AFBC_COMPRESSION_MODE_R8G8B8A8;
818 case PAN_AFBC_MODE_R10G10B10A2: return MALI_AFBC_COMPRESSION_MODE_R10G10B10A2;
819 case PAN_AFBC_MODE_R11G11B10: return MALI_AFBC_COMPRESSION_MODE_R11G11B10;
820 case PAN_AFBC_MODE_S8: return MALI_AFBC_COMPRESSION_MODE_S8;
821 case PAN_AFBC_MODE_INVALID: unreachable("Invalid AFBC format");
822 }
823 /* clang-format on */
824
825 unreachable("all AFBC formats handled");
826 }
827 #endif
828
829 #if PAN_ARCH >= 10
830 enum mali_afrc_format
GENX(pan_afrc_format)831 GENX(pan_afrc_format)(struct pan_afrc_format_info info, uint64_t modifier,
832 unsigned plane)
833 {
834 bool scan = panfrost_afrc_is_scan(modifier);
835
836 assert(info.bpc == 8 || info.bpc == 10);
837 assert(info.num_comps > 0 && info.num_comps <= 4);
838
839 switch (info.ichange_fmt) {
840 case PAN_AFRC_ICHANGE_FORMAT_RAW:
841 assert(plane == 0);
842
843 if (info.bpc == 8)
844 return (scan ? MALI_AFRC_FORMAT_R8_SCAN : MALI_AFRC_FORMAT_R8_ROT) +
845 (info.num_comps - 1);
846
847 assert(info.num_comps == 4);
848 return (scan ? MALI_AFRC_FORMAT_R10G10B10A10_SCAN
849 : MALI_AFRC_FORMAT_R10G10B10A10_ROT);
850
851 case PAN_AFRC_ICHANGE_FORMAT_YUV444:
852 if (info.bpc == 8) {
853 if (plane == 0 || info.num_planes == 3)
854 return (scan ? MALI_AFRC_FORMAT_R8_444_SCAN
855 : MALI_AFRC_FORMAT_R8_444_ROT);
856
857 return (scan ? MALI_AFRC_FORMAT_R8G8_444_SCAN
858 : MALI_AFRC_FORMAT_R8G8_444_ROT);
859 }
860
861 assert(info.num_planes == 3);
862 return (scan ? MALI_AFRC_FORMAT_R10_444_SCAN
863 : MALI_AFRC_FORMAT_R10_444_ROT);
864
865 case PAN_AFRC_ICHANGE_FORMAT_YUV422:
866 if (info.bpc == 8) {
867 if (plane == 0 || info.num_planes == 3)
868 return (scan ? MALI_AFRC_FORMAT_R8_422_SCAN
869 : MALI_AFRC_FORMAT_R8_422_ROT);
870
871 return (scan ? MALI_AFRC_FORMAT_R8G8_422_SCAN
872 : MALI_AFRC_FORMAT_R8G8_422_ROT);
873 }
874
875 if (plane == 0 || info.num_planes == 3)
876 return (scan ? MALI_AFRC_FORMAT_R10_422_SCAN
877 : MALI_AFRC_FORMAT_R10_422_ROT);
878
879 return (scan ? MALI_AFRC_FORMAT_R10G10_422_SCAN
880 : MALI_AFRC_FORMAT_R10G10_422_ROT);
881
882 case PAN_AFRC_ICHANGE_FORMAT_YUV420:
883 if (info.bpc == 8) {
884 if (plane == 0 || info.num_planes == 3)
885 return (scan ? MALI_AFRC_FORMAT_R8_420_SCAN
886 : MALI_AFRC_FORMAT_R8_420_ROT);
887
888 return (scan ? MALI_AFRC_FORMAT_R8G8_420_SCAN
889 : MALI_AFRC_FORMAT_R8G8_420_ROT);
890 }
891
892 if (plane == 0 || info.num_planes == 3)
893 return (scan ? MALI_AFRC_FORMAT_R10_420_SCAN
894 : MALI_AFRC_FORMAT_R10_420_ROT);
895
896 return (scan ? MALI_AFRC_FORMAT_R10G10_420_SCAN
897 : MALI_AFRC_FORMAT_R10G10_420_ROT);
898
899 default:
900 return MALI_AFRC_FORMAT_INVALID;
901 }
902 }
903
904 enum mali_afrc_block_size
GENX(pan_afrc_block_size)905 GENX(pan_afrc_block_size)(uint64_t modifier, unsigned index)
906 {
907 /* Clump size flag for planes 1 and 2 is shifted by 4 bits */
908 unsigned shift = index == 0 ? 0 : 4;
909 uint64_t flag = (modifier >> shift) & AFRC_FORMAT_MOD_CU_SIZE_MASK;
910
911 /* clang-format off */
912 switch (flag) {
913 case AFRC_FORMAT_MOD_CU_SIZE_16: return MALI_AFRC_BLOCK_SIZE_16;
914 case AFRC_FORMAT_MOD_CU_SIZE_24: return MALI_AFRC_BLOCK_SIZE_24;
915 case AFRC_FORMAT_MOD_CU_SIZE_32: return MALI_AFRC_BLOCK_SIZE_32;
916 default: unreachable("invalid code unit size");
917 }
918 /* clang-format on */
919 }
920 #endif
921