• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &section, 0, level, payload);
567 #else
568    panfrost_emit_surface_with_stride(&section, 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