• 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  *
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