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