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_3_planar_yuv = desc->layout == UTIL_FORMAT_LAYOUT_PLANAR3;
427
428 pan_cast_and_pack(*payload, PLANE, cfg) {
429 cfg.pointer = pointer;
430 cfg.row_stride = row_stride;
431 cfg.size = layout->data_size - layout->slices[level].offset;
432
433 if (is_3_planar_yuv) {
434 cfg.two_plane_yuv_chroma.secondary_pointer =
435 sections[plane_index + 1].pointer;
436 } else if (!panfrost_format_is_yuv(layout->format)) {
437 cfg.slice_stride = layout->nr_samples
438 ? surface_stride
439 : panfrost_get_layer_stride(layout, level);
440 }
441
442 if (desc->layout == UTIL_FORMAT_LAYOUT_ASTC) {
443 assert(!afbc);
444 assert(!afrc);
445
446 if (desc->block.depth > 1) {
447 cfg.plane_type = MALI_PLANE_TYPE_ASTC_3D;
448 cfg.astc._3d.block_width = panfrost_astc_dim_3d(desc->block.width);
449 cfg.astc._3d.block_height =
450 panfrost_astc_dim_3d(desc->block.height);
451 cfg.astc._3d.block_depth = panfrost_astc_dim_3d(desc->block.depth);
452 } else {
453 cfg.plane_type = MALI_PLANE_TYPE_ASTC_2D;
454 cfg.astc._2d.block_width = panfrost_astc_dim_2d(desc->block.width);
455 cfg.astc._2d.block_height =
456 panfrost_astc_dim_2d(desc->block.height);
457 }
458
459 bool srgb = (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB);
460
461 /* Mesa does not advertise _HDR formats yet */
462 cfg.astc.decode_hdr = false;
463
464 /* sRGB formats decode to RGBA8 sRGB, which is narrow.
465 *
466 * Non-sRGB formats decode to RGBA16F which is wide except if decode
467 * precision is set to GL_RGBA8 for that texture.
468 */
469 cfg.astc.decode_wide = !srgb && !iview->astc.narrow;
470 } else if (afbc) {
471 cfg.plane_type = MALI_PLANE_TYPE_AFBC;
472 cfg.afbc.superblock_size = translate_superblock_size(layout->modifier);
473 cfg.afbc.ytr = (layout->modifier & AFBC_FORMAT_MOD_YTR);
474 cfg.afbc.split_block = (layout->modifier & AFBC_FORMAT_MOD_SPLIT);
475 cfg.afbc.tiled_header = (layout->modifier & AFBC_FORMAT_MOD_TILED);
476 cfg.afbc.prefetch = true;
477 cfg.afbc.compression_mode =
478 GENX(pan_afbc_compression_mode)(iview->format);
479 cfg.afbc.header_stride = layout->slices[level].afbc.header_size;
480 } else if (afrc) {
481 #if PAN_ARCH >= 10
482 struct pan_afrc_format_info finfo =
483 panfrost_afrc_get_format_info(iview->format);
484
485 cfg.plane_type = MALI_PLANE_TYPE_AFRC;
486 cfg.afrc.block_size =
487 GENX(pan_afrc_block_size)(layout->modifier, plane_index);
488 cfg.afrc.format =
489 GENX(pan_afrc_format)(finfo, layout->modifier, plane_index);
490 #endif
491 } else {
492 cfg.plane_type = is_3_planar_yuv ? MALI_PLANE_TYPE_CHROMA_2P
493 : MALI_PLANE_TYPE_GENERIC;
494 cfg.clump_format = panfrost_clump_format(iview->format);
495 }
496
497 if (!afbc && !afrc) {
498 if (layout->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED)
499 cfg.clump_ordering = MALI_CLUMP_ORDERING_TILED_U_INTERLEAVED;
500 else
501 cfg.clump_ordering = MALI_CLUMP_ORDERING_LINEAR;
502 }
503 }
504 *payload += pan_size(PLANE);
505 }
506 #endif
507
508 static void
panfrost_emit_surface(const struct pan_image_view * iview,unsigned level,unsigned index,unsigned sample,void ** payload)509 panfrost_emit_surface(const struct pan_image_view *iview, unsigned level,
510 unsigned index, unsigned sample, void **payload)
511 {
512 #if PAN_ARCH == 7 || PAN_ARCH >= 9
513 if (panfrost_format_is_yuv(iview->format)) {
514 struct pan_image_section_info sections[MAX_IMAGE_PLANES] = {0};
515 unsigned plane_count = 0;
516
517 for (int i = 0; i < MAX_IMAGE_PLANES; i++) {
518 const struct pan_image *plane = pan_image_view_get_plane(iview, i);
519
520 if (!plane)
521 break;
522
523 sections[i] =
524 get_image_section_info(iview, plane, level, index, sample);
525 plane_count++;
526 }
527
528 #if PAN_ARCH >= 9
529 /* 3-plane YUV is submitted using two PLANE descriptors, where the
530 * second one is of type CHROMA_2P */
531 panfrost_emit_plane(iview, sections, 0, level, payload);
532
533 if (plane_count > 1) {
534 /* 3-plane YUV requires equal stride for both chroma planes */
535 assert(plane_count == 2 ||
536 sections[1].row_stride == sections[2].row_stride);
537 panfrost_emit_plane(iview, sections, 1, level, payload);
538 }
539 #else
540 if (plane_count > 1)
541 panfrost_emit_multiplanar_surface(sections, payload);
542 else
543 panfrost_emit_surface_with_stride(sections, payload);
544 #endif
545 return;
546 }
547 #endif
548
549 const struct util_format_description *fdesc =
550 util_format_description(iview->format);
551
552 /* In case of multiplanar depth/stencil, the stencil is always on
553 * plane 1. Combined depth/stencil only has one plane, so depth
554 * will be on plane 0 in either case.
555 */
556 const struct pan_image *plane = util_format_has_stencil(fdesc)
557 ? pan_image_view_get_s_plane(iview)
558 : pan_image_view_get_plane(iview, 0);
559 assert(plane != NULL);
560
561 struct pan_image_section_info section =
562 get_image_section_info(iview, plane, level, index, sample);
563
564 #if PAN_ARCH >= 9
565 panfrost_emit_plane(iview, §ion, 0, level, payload);
566 #else
567 panfrost_emit_surface_with_stride(§ion, payload);
568 #endif
569 }
570
571 static void
panfrost_emit_texture_payload(const struct pan_image_view * iview,void * payload)572 panfrost_emit_texture_payload(const struct pan_image_view *iview, void *payload)
573 {
574 unsigned nr_samples =
575 PAN_ARCH <= 7 ? pan_image_view_get_nr_samples(iview) : 1;
576
577 /* Inject the addresses in, interleaving array indices, mip levels,
578 * cube faces, and strides in that order. On Bifrost and older, each
579 * sample had its own surface descriptor; on Valhall, they are fused
580 * into a single plane descriptor.
581 */
582
583 #if PAN_ARCH >= 7
584 /* V7 and later treats faces as extra layers */
585 for (int layer = iview->first_layer; layer <= iview->last_layer; ++layer) {
586 for (int sample = 0; sample < nr_samples; ++sample) {
587 for (int level = iview->first_level; level <= iview->last_level; ++level) {
588 panfrost_emit_surface(iview, level, layer, sample, &payload);
589 }
590 }
591 }
592 #else
593 unsigned first_layer = iview->first_layer, last_layer = iview->last_layer;
594 unsigned face_count = 1;
595
596 if (iview->dim == MALI_TEXTURE_DIMENSION_CUBE) {
597 first_layer /= 6;
598 last_layer /= 6;
599 face_count = 6;
600 }
601
602 /* V6 and earlier has a different memory-layout */
603 for (int layer = first_layer; layer <= last_layer; ++layer) {
604 for (int level = iview->first_level; level <= iview->last_level; ++level) {
605 /* order of face and sample doesn't matter; we can only have multiple
606 * of one or the other (no support for multisampled cubemaps)
607 */
608 for (int face = 0; face < face_count; ++face) {
609 for (int sample = 0; sample < nr_samples; ++sample) {
610 panfrost_emit_surface(iview, level, (face_count * layer) + face,
611 sample, &payload);
612 }
613 }
614 }
615 }
616 #endif
617 }
618
619 #if PAN_ARCH <= 7
620 /* Map modifiers to mali_texture_layout for packing in a texture descriptor */
621
622 static enum mali_texture_layout
panfrost_modifier_to_layout(uint64_t modifier)623 panfrost_modifier_to_layout(uint64_t modifier)
624 {
625 if (drm_is_afbc(modifier))
626 return MALI_TEXTURE_LAYOUT_AFBC;
627 else if (modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED)
628 return MALI_TEXTURE_LAYOUT_TILED;
629 else if (modifier == DRM_FORMAT_MOD_LINEAR)
630 return MALI_TEXTURE_LAYOUT_LINEAR;
631 else
632 unreachable("Invalid modifer");
633 }
634 #endif
635
636 #if PAN_ARCH >= 7
637 void
GENX(panfrost_texture_swizzle_replicate_x)638 GENX(panfrost_texture_swizzle_replicate_x)(struct pan_image_view *iview)
639 {
640 /* v7+ doesn't have an _RRRR component order, combine the
641 * user swizzle with a .XXXX swizzle to emulate that. */
642 assert(util_format_is_depth_or_stencil(iview->format));
643
644 static const unsigned char replicate_x[4] = {
645 PIPE_SWIZZLE_X,
646 PIPE_SWIZZLE_X,
647 PIPE_SWIZZLE_X,
648 PIPE_SWIZZLE_X,
649 };
650
651 util_format_compose_swizzles(replicate_x, iview->swizzle, iview->swizzle);
652 }
653 #endif
654
655 #if PAN_ARCH == 7
656 void
GENX(panfrost_texture_afbc_reswizzle)657 GENX(panfrost_texture_afbc_reswizzle)(struct pan_image_view *iview)
658 {
659 /* v7 (only) restricts component orders when AFBC is in use.
660 * Rather than restrict AFBC for all non-canonical component orders, we use
661 * an allowed component order with an invertible swizzle composed.
662 * This allows us to support AFBC(BGR) as well as AFBC(RGB).
663 */
664 assert(!util_format_is_depth_or_stencil(iview->format));
665 assert(!panfrost_format_is_yuv(iview->format));
666 assert(panfrost_format_supports_afbc(PAN_ARCH, iview->format));
667
668 uint32_t mali_format =
669 GENX(panfrost_format_from_pipe_format)(iview->format)->hw;
670
671 enum mali_rgb_component_order orig = mali_format & BITFIELD_MASK(12);
672 struct pan_decomposed_swizzle decomposed = GENX(pan_decompose_swizzle)(orig);
673
674 /* Apply the new component order */
675 if (orig != decomposed.pre)
676 iview->format = util_format_rgb_to_bgr(iview->format);
677 /* Only RGB<->BGR should be allowed for AFBC */
678 assert(iview->format != PIPE_FORMAT_NONE);
679 assert(decomposed.pre ==
680 (GENX(panfrost_format_from_pipe_format)(iview->format)->hw &
681 BITFIELD_MASK(12)));
682
683 /* Compose the new swizzle */
684 util_format_compose_swizzles(decomposed.post, iview->swizzle,
685 iview->swizzle);
686 }
687 #endif
688
689 /*
690 * Generates a texture descriptor. Ideally, descriptors are immutable after the
691 * texture is created, so we can keep these hanging around in GPU memory in a
692 * dedicated BO and not have to worry. In practice there are some minor gotchas
693 * with this (the driver sometimes will change the format of a texture on the
694 * fly for compression) but it's fast enough to just regenerate the descriptor
695 * in those cases, rather than monkeypatching at drawtime. A texture descriptor
696 * consists of a 32-byte header followed by pointers.
697 */
698 void
GENX(panfrost_new_texture)699 GENX(panfrost_new_texture)(const struct pan_image_view *iview,
700 struct mali_texture_packed *out,
701 const struct panfrost_ptr *payload)
702 {
703 const struct util_format_description *desc =
704 util_format_description(iview->format);
705 const struct pan_image *first_plane = pan_image_view_get_first_plane(iview);
706 const struct pan_image_layout *layout = &first_plane->layout;
707 uint32_t mali_format =
708 GENX(panfrost_format_from_pipe_format)(iview->format)->hw;
709
710 if (desc->layout == UTIL_FORMAT_LAYOUT_ASTC && iview->astc.narrow &&
711 desc->colorspace != UTIL_FORMAT_COLORSPACE_SRGB) {
712 mali_format = MALI_PACK_FMT(RGBA8_UNORM, RGBA, L);
713 }
714
715 panfrost_emit_texture_payload(iview, payload->cpu);
716
717 unsigned array_size = iview->last_layer - iview->first_layer + 1;
718
719 /* If this is a cubemap, we expect the number of layers to be a multiple
720 * of 6.
721 */
722 if (iview->dim == MALI_TEXTURE_DIMENSION_CUBE) {
723 assert(array_size % 6 == 0);
724 array_size /= 6;
725 }
726
727 /* Multiplanar YUV textures require 2 surface descriptors. */
728 if (panfrost_format_is_yuv(iview->format) && PAN_ARCH >= 9 &&
729 pan_image_view_get_plane(iview, 1) != NULL)
730 array_size *= 2;
731
732 unsigned width, height, depth;
733
734 if (iview->buf.size) {
735 assert(iview->dim == MALI_TEXTURE_DIMENSION_1D);
736 assert(!iview->first_level && !iview->last_level);
737 assert(!iview->first_layer && !iview->last_layer);
738 assert(layout->nr_samples == 1);
739 assert(layout->height == 1 && layout->depth == 1);
740 assert(iview->buf.offset + iview->buf.size <= layout->width);
741 width = iview->buf.size;
742 height = 1;
743 depth = 1;
744 } else {
745 width = u_minify(layout->width, iview->first_level);
746 height = u_minify(layout->height, iview->first_level);
747 depth = u_minify(layout->depth, iview->first_level);
748 if (util_format_is_compressed(layout->format) &&
749 !util_format_is_compressed(iview->format)) {
750 width =
751 DIV_ROUND_UP(width, util_format_get_blockwidth(layout->format));
752 height =
753 DIV_ROUND_UP(height, util_format_get_blockheight(layout->format));
754 depth =
755 DIV_ROUND_UP(depth, util_format_get_blockdepth(layout->format));
756 assert(util_format_get_blockwidth(iview->format) == 1);
757 assert(util_format_get_blockheight(iview->format) == 1);
758 assert(util_format_get_blockheight(iview->format) == 1);
759 assert(iview->last_level == iview->first_level);
760 }
761 }
762
763 pan_pack(out, TEXTURE, cfg) {
764 cfg.dimension = iview->dim;
765 cfg.format = mali_format;
766 cfg.width = width;
767 cfg.height = height;
768 if (iview->dim == MALI_TEXTURE_DIMENSION_3D)
769 cfg.depth = depth;
770 else
771 cfg.sample_count = layout->nr_samples;
772 cfg.swizzle = panfrost_translate_swizzle_4(iview->swizzle);
773 #if PAN_ARCH >= 9
774 cfg.texel_interleave = (layout->modifier != DRM_FORMAT_MOD_LINEAR) ||
775 util_format_is_compressed(iview->format);
776 #else
777 cfg.texel_ordering = panfrost_modifier_to_layout(layout->modifier);
778 #endif
779 cfg.levels = iview->last_level - iview->first_level + 1;
780 cfg.array_size = array_size;
781
782 #if PAN_ARCH >= 6
783 cfg.surfaces = payload->gpu;
784
785 /* We specify API-level LOD clamps in the sampler descriptor
786 * and use these clamps simply for bounds checking.
787 */
788 cfg.minimum_lod = 0;
789 cfg.maximum_lod = cfg.levels - 1;
790 #endif
791 }
792 }
793
794 #if PAN_ARCH >= 9
795 enum mali_afbc_compression_mode
GENX(pan_afbc_compression_mode)796 GENX(pan_afbc_compression_mode)(enum pipe_format format)
797 {
798 /* There's a special case for texturing the stencil part from a combined
799 * depth/stencil texture, handle it separately.
800 */
801 if (format == PIPE_FORMAT_X24S8_UINT)
802 return MALI_AFBC_COMPRESSION_MODE_X24S8;
803
804 /* Otherwise, map canonical formats to the hardware enum. This only
805 * needs to handle the subset of formats returned by
806 * panfrost_afbc_format.
807 */
808 /* clang-format off */
809 switch (panfrost_afbc_format(PAN_ARCH, format)) {
810 case PAN_AFBC_MODE_R8: return MALI_AFBC_COMPRESSION_MODE_R8;
811 case PAN_AFBC_MODE_R8G8: return MALI_AFBC_COMPRESSION_MODE_R8G8;
812 case PAN_AFBC_MODE_R5G6B5: return MALI_AFBC_COMPRESSION_MODE_R5G6B5;
813 case PAN_AFBC_MODE_R4G4B4A4: return MALI_AFBC_COMPRESSION_MODE_R4G4B4A4;
814 case PAN_AFBC_MODE_R5G5B5A1: return MALI_AFBC_COMPRESSION_MODE_R5G5B5A1;
815 case PAN_AFBC_MODE_R8G8B8: return MALI_AFBC_COMPRESSION_MODE_R8G8B8;
816 case PAN_AFBC_MODE_R8G8B8A8: return MALI_AFBC_COMPRESSION_MODE_R8G8B8A8;
817 case PAN_AFBC_MODE_R10G10B10A2: return MALI_AFBC_COMPRESSION_MODE_R10G10B10A2;
818 case PAN_AFBC_MODE_R11G11B10: return MALI_AFBC_COMPRESSION_MODE_R11G11B10;
819 case PAN_AFBC_MODE_S8: return MALI_AFBC_COMPRESSION_MODE_S8;
820 case PAN_AFBC_MODE_INVALID: unreachable("Invalid AFBC format");
821 }
822 /* clang-format on */
823
824 unreachable("all AFBC formats handled");
825 }
826 #endif
827
828 #if PAN_ARCH >= 10
829 enum mali_afrc_format
GENX(pan_afrc_format)830 GENX(pan_afrc_format)(struct pan_afrc_format_info info, uint64_t modifier,
831 unsigned plane)
832 {
833 bool scan = panfrost_afrc_is_scan(modifier);
834
835 assert(info.bpc == 8 || info.bpc == 10);
836 assert(info.num_comps > 0 && info.num_comps <= 4);
837
838 switch (info.ichange_fmt) {
839 case PAN_AFRC_ICHANGE_FORMAT_RAW:
840 assert(plane == 0);
841
842 if (info.bpc == 8)
843 return (scan ? MALI_AFRC_FORMAT_R8_SCAN : MALI_AFRC_FORMAT_R8_ROT) +
844 (info.num_comps - 1);
845
846 assert(info.num_comps == 4);
847 return (scan ? MALI_AFRC_FORMAT_R10G10B10A10_SCAN
848 : MALI_AFRC_FORMAT_R10G10B10A10_ROT);
849
850 case PAN_AFRC_ICHANGE_FORMAT_YUV444:
851 if (info.bpc == 8) {
852 if (plane == 0 || info.num_planes == 3)
853 return (scan ? MALI_AFRC_FORMAT_R8_444_SCAN
854 : MALI_AFRC_FORMAT_R8_444_ROT);
855
856 return (scan ? MALI_AFRC_FORMAT_R8G8_444_SCAN
857 : MALI_AFRC_FORMAT_R8G8_444_ROT);
858 }
859
860 assert(info.num_planes == 3);
861 return (scan ? MALI_AFRC_FORMAT_R10_444_SCAN
862 : MALI_AFRC_FORMAT_R10_444_ROT);
863
864 case PAN_AFRC_ICHANGE_FORMAT_YUV422:
865 if (info.bpc == 8) {
866 if (plane == 0 || info.num_planes == 3)
867 return (scan ? MALI_AFRC_FORMAT_R8_422_SCAN
868 : MALI_AFRC_FORMAT_R8_422_ROT);
869
870 return (scan ? MALI_AFRC_FORMAT_R8G8_422_SCAN
871 : MALI_AFRC_FORMAT_R8G8_422_ROT);
872 }
873
874 if (plane == 0 || info.num_planes == 3)
875 return (scan ? MALI_AFRC_FORMAT_R10_422_SCAN
876 : MALI_AFRC_FORMAT_R10_422_ROT);
877
878 return (scan ? MALI_AFRC_FORMAT_R10G10_422_SCAN
879 : MALI_AFRC_FORMAT_R10G10_422_ROT);
880
881 case PAN_AFRC_ICHANGE_FORMAT_YUV420:
882 if (info.bpc == 8) {
883 if (plane == 0 || info.num_planes == 3)
884 return (scan ? MALI_AFRC_FORMAT_R8_420_SCAN
885 : MALI_AFRC_FORMAT_R8_420_ROT);
886
887 return (scan ? MALI_AFRC_FORMAT_R8G8_420_SCAN
888 : MALI_AFRC_FORMAT_R8G8_420_ROT);
889 }
890
891 if (plane == 0 || info.num_planes == 3)
892 return (scan ? MALI_AFRC_FORMAT_R10_420_SCAN
893 : MALI_AFRC_FORMAT_R10_420_ROT);
894
895 return (scan ? MALI_AFRC_FORMAT_R10G10_420_SCAN
896 : MALI_AFRC_FORMAT_R10G10_420_ROT);
897
898 default:
899 return MALI_AFRC_FORMAT_INVALID;
900 }
901 }
902
903 enum mali_afrc_block_size
GENX(pan_afrc_block_size)904 GENX(pan_afrc_block_size)(uint64_t modifier, unsigned index)
905 {
906 /* Clump size flag for planes 1 and 2 is shifted by 4 bits */
907 unsigned shift = index == 0 ? 0 : 4;
908 uint64_t flag = (modifier >> shift) & AFRC_FORMAT_MOD_CU_SIZE_MASK;
909
910 /* clang-format off */
911 switch (flag) {
912 case AFRC_FORMAT_MOD_CU_SIZE_16: return MALI_AFRC_BLOCK_SIZE_16;
913 case AFRC_FORMAT_MOD_CU_SIZE_24: return MALI_AFRC_BLOCK_SIZE_24;
914 case AFRC_FORMAT_MOD_CU_SIZE_32: return MALI_AFRC_BLOCK_SIZE_32;
915 default: unreachable("invalid code unit size");
916 }
917 /* clang-format on */
918 }
919 #endif
920