• 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_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, &section, 0, level, payload);
566 #else
567    panfrost_emit_surface_with_stride(&section, 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