1/*
2 * Copyright (C) 2022 Collabora, Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "pan_texture.h"
25
26#include <gtest/gtest.h>
27
28TEST(BlockSize, Linear)
29{
30   enum pipe_format format[] = {
31      PIPE_FORMAT_R32G32B32_FLOAT,
32      PIPE_FORMAT_R8G8B8_UNORM,
33      PIPE_FORMAT_ETC2_RGB8,
34      PIPE_FORMAT_ASTC_5x5
35   };
36
37   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
38      struct pan_block_size blk = panfrost_block_size(DRM_FORMAT_MOD_LINEAR, format[i]);
39
40      EXPECT_EQ(blk.width, 1);
41      EXPECT_EQ(blk.height, 1);
42   }
43}
44
45TEST(BlockSize, UInterleavedRegular)
46{
47   enum pipe_format format[] = {
48      PIPE_FORMAT_R32G32B32_FLOAT,
49      PIPE_FORMAT_R8G8B8_UNORM,
50   };
51
52   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
53      struct pan_block_size blk = panfrost_block_size(DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED, format[i]);
54
55      EXPECT_EQ(blk.width, 16);
56      EXPECT_EQ(blk.height, 16);
57   }
58}
59
60TEST(BlockSize, UInterleavedBlockCompressed)
61{
62   enum pipe_format format[] = {
63      PIPE_FORMAT_ETC2_RGB8,
64      PIPE_FORMAT_ASTC_5x5
65   };
66
67   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
68      struct pan_block_size blk = panfrost_block_size(DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED, format[i]);
69
70      EXPECT_EQ(blk.width, 4);
71      EXPECT_EQ(blk.height, 4);
72   }
73}
74
75TEST(BlockSize, AFBCFormatInvariant16x16)
76{
77   enum pipe_format format[] = {
78      PIPE_FORMAT_R32G32B32_FLOAT,
79      PIPE_FORMAT_R8G8B8_UNORM,
80      PIPE_FORMAT_ETC2_RGB8,
81      PIPE_FORMAT_ASTC_5x5
82   };
83
84   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
85                AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
86                AFBC_FORMAT_MOD_SPARSE |
87                AFBC_FORMAT_MOD_YTR);
88
89   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
90      struct pan_block_size blk = panfrost_block_size(modifier, format[i]);
91
92      EXPECT_EQ(blk.width, 16);
93      EXPECT_EQ(blk.height, 16);
94   }
95}
96
97TEST(BlockSize, AFBCFormatInvariant32x8)
98{
99   enum pipe_format format[] = {
100      PIPE_FORMAT_R32G32B32_FLOAT,
101      PIPE_FORMAT_R8G8B8_UNORM,
102      PIPE_FORMAT_ETC2_RGB8,
103      PIPE_FORMAT_ASTC_5x5
104   };
105
106   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
107                AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
108                AFBC_FORMAT_MOD_SPARSE |
109                AFBC_FORMAT_MOD_YTR);
110
111   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
112      struct pan_block_size blk = panfrost_block_size(modifier, format[i]);
113
114      EXPECT_EQ(blk.width, 32);
115      EXPECT_EQ(blk.height, 8);
116   }
117}
118
119TEST(BlockSize, AFBCSuperblock16x16)
120{
121   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
122                AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
123                AFBC_FORMAT_MOD_SPARSE |
124                AFBC_FORMAT_MOD_YTR);
125
126   EXPECT_EQ(panfrost_afbc_superblock_size(modifier).width, 16);
127   EXPECT_EQ(panfrost_afbc_superblock_width(modifier), 16);
128
129   EXPECT_EQ(panfrost_afbc_superblock_size(modifier).height, 16);
130   EXPECT_EQ(panfrost_afbc_superblock_height(modifier), 16);
131
132   EXPECT_FALSE(panfrost_afbc_is_wide(modifier));
133}
134
135TEST(BlockSize, AFBCSuperblock32x8)
136{
137   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
138                AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
139                AFBC_FORMAT_MOD_SPARSE);
140
141   EXPECT_EQ(panfrost_afbc_superblock_size(modifier).width, 32);
142   EXPECT_EQ(panfrost_afbc_superblock_width(modifier), 32);
143
144   EXPECT_EQ(panfrost_afbc_superblock_size(modifier).height, 8);
145   EXPECT_EQ(panfrost_afbc_superblock_height(modifier), 8);
146
147   EXPECT_TRUE(panfrost_afbc_is_wide(modifier));
148}
149
150TEST(BlockSize, AFBCSuperblock64x4)
151{
152   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
153                AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
154                AFBC_FORMAT_MOD_SPARSE);
155
156   EXPECT_EQ(panfrost_afbc_superblock_size(modifier).width, 64);
157   EXPECT_EQ(panfrost_afbc_superblock_width(modifier), 64);
158
159   EXPECT_EQ(panfrost_afbc_superblock_size(modifier).height, 4);
160   EXPECT_EQ(panfrost_afbc_superblock_height(modifier), 4);
161
162   EXPECT_TRUE(panfrost_afbc_is_wide(modifier));
163}
164
165/* Calculate Bifrost line stride, since we have reference formulas for Bifrost
166 * stride calculations.
167 */
168static uint32_t pan_afbc_line_stride(uint64_t modifier, uint32_t width)
169{
170   return pan_afbc_stride_blocks(modifier, pan_afbc_row_stride(modifier, width));
171}
172
173/* Which form of the stride we specify is hardware specific (row stride for
174 * Valhall, line stride for Bifrost). However, the layout code is hardware
175 * independent, so we test both row stride and line stride calculations.
176 */
177TEST(AFBCStride, Linear)
178{
179   uint64_t modifiers[] = {
180      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
181                              AFBC_FORMAT_MOD_SPARSE),
182      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
183                              AFBC_FORMAT_MOD_SPARSE),
184      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
185                              AFBC_FORMAT_MOD_SPARSE),
186   };
187
188   for (unsigned m = 0; m < ARRAY_SIZE(modifiers); ++m) {
189      uint64_t modifier = modifiers[m];
190
191      uint32_t sw = panfrost_afbc_superblock_width(modifier);
192      uint32_t cases[] = { 1, 4, 17, 39 };
193
194      for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
195         uint32_t width = sw * cases[i];
196
197         EXPECT_EQ(pan_afbc_row_stride(modifier, width),
198               16 * DIV_ROUND_UP(width, sw));
199
200         EXPECT_EQ(pan_afbc_line_stride(modifier, width),
201               DIV_ROUND_UP(width, sw));
202      }
203   }
204}
205
206TEST(AFBCStride, Tiled)
207{
208   uint64_t modifiers[] = {
209      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
210                              AFBC_FORMAT_MOD_TILED |
211                              AFBC_FORMAT_MOD_SPARSE),
212      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
213                              AFBC_FORMAT_MOD_TILED |
214                              AFBC_FORMAT_MOD_SPARSE),
215      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
216                              AFBC_FORMAT_MOD_TILED |
217                              AFBC_FORMAT_MOD_SPARSE),
218   };
219
220   for (unsigned m = 0; m < ARRAY_SIZE(modifiers); ++m) {
221      uint64_t modifier = modifiers[m];
222
223      uint32_t sw = panfrost_afbc_superblock_width(modifier);
224      uint32_t cases[] = { 1, 4, 17, 39 };
225
226      for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
227         uint32_t width = sw * 8 * cases[i];
228
229         EXPECT_EQ(pan_afbc_row_stride(modifier, width),
230               16 * DIV_ROUND_UP(width, (sw * 8)) * 8 * 8);
231
232         EXPECT_EQ(pan_afbc_line_stride(modifier, width),
233               DIV_ROUND_UP(width, sw * 8) * 8);
234      }
235   }
236}
237
238TEST(LegacyStride, FromLegacyLinear)
239{
240   EXPECT_EQ(panfrost_from_legacy_stride(1920 * 4, PIPE_FORMAT_R8G8B8A8_UINT, DRM_FORMAT_MOD_LINEAR), 1920 * 4);
241   EXPECT_EQ(panfrost_from_legacy_stride(53, PIPE_FORMAT_R8_SNORM, DRM_FORMAT_MOD_LINEAR), 53);
242   EXPECT_EQ(panfrost_from_legacy_stride(60, PIPE_FORMAT_ETC2_RGB8, DRM_FORMAT_MOD_LINEAR), 60);
243}
244
245TEST(LegacyStride, FromLegacyInterleaved)
246{
247   EXPECT_EQ(panfrost_from_legacy_stride(1920 * 4, PIPE_FORMAT_R8G8B8A8_UINT,
248            DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED),
249            1920 * 4 * 16);
250
251   EXPECT_EQ(panfrost_from_legacy_stride(53, PIPE_FORMAT_R8_SNORM,
252            DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED), 53 * 16);
253
254   EXPECT_EQ(panfrost_from_legacy_stride(60, PIPE_FORMAT_ETC2_RGB8,
255            DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED), 60 * 4);
256}
257
258TEST(LegacyStride, FromLegacyAFBC)
259{
260   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
261                AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
262                AFBC_FORMAT_MOD_SPARSE |
263                AFBC_FORMAT_MOD_YTR);
264
265   EXPECT_EQ(panfrost_from_legacy_stride(1920 * 4, PIPE_FORMAT_R8G8B8A8_UINT, modifier), 60 * 16);
266   EXPECT_EQ(panfrost_from_legacy_stride(64, PIPE_FORMAT_R8_SNORM, modifier), 2 * 16);
267}
268
269/* dEQP-GLES3.functional.texture.format.compressed.etc1_2d_pot */
270TEST(Layout, ImplicitLayoutInterleavedETC2)
271{
272   struct pan_image_layout l = {
273      .modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
274      .format = PIPE_FORMAT_ETC2_RGB8,
275      .width = 128,
276      .height = 128,
277      .depth = 1,
278      .nr_samples = 1,
279      .dim = MALI_TEXTURE_DIMENSION_2D,
280      .nr_slices = 8
281   };
282
283   unsigned offsets[9] = {
284      0, 8192, 10240, 10752, 10880, 11008, 11136, 11264, 11392
285   };
286
287   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
288
289   for (unsigned i = 0; i < 8; ++i) {
290      unsigned size = (offsets[i + 1] - offsets[i]);
291      EXPECT_EQ(l.slices[i].offset, offsets[i]);
292
293      if (size == 64)
294         EXPECT_TRUE(l.slices[i].size < 64);
295      else
296         EXPECT_EQ(l.slices[i].size, size);
297   }
298}
299
300TEST(Layout, ImplicitLayoutInterleavedASTC5x5)
301{
302   struct pan_image_layout l = {
303      .modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
304      .format = PIPE_FORMAT_ASTC_5x5,
305      .width = 50,
306      .height = 50,
307      .depth = 1,
308      .nr_samples = 1,
309      .dim = MALI_TEXTURE_DIMENSION_2D,
310      .nr_slices = 1
311   };
312
313   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
314
315   /* The image is 50x50 pixels, with 5x5 blocks. So it is a 10x10 grid of ASTC
316    * blocks. 4x4 tiles of ASTC blocks are u-interleaved, so we have to round up
317    * to a 12x12 grid. So we need space for 144 ASTC blocks. Each ASTC block is
318    * 16 bytes (128-bits), so we require 2304 bytes, with a row stride of 12 *
319    * 16 * 4 = 192 bytes.
320    */
321   EXPECT_EQ(l.slices[0].offset, 0);
322   EXPECT_EQ(l.slices[0].row_stride, 768);
323   EXPECT_EQ(l.slices[0].surface_stride, 2304);
324   EXPECT_EQ(l.slices[0].size, 2304);
325}
326
327TEST(Layout, ImplicitLayoutLinearASTC5x5)
328{
329   struct pan_image_layout l = {
330      .modifier = DRM_FORMAT_MOD_LINEAR,
331      .format = PIPE_FORMAT_ASTC_5x5,
332      .width = 50,
333      .height = 50,
334      .depth = 1,
335      .nr_samples = 1,
336      .dim = MALI_TEXTURE_DIMENSION_2D,
337      .nr_slices = 1
338   };
339
340   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
341
342   /* The image is 50x50 pixels, with 5x5 blocks. So it is a 10x10 grid of ASTC
343    * blocks. Each ASTC block is 16 bytes, so the row stride is 160 bytes,
344    * rounded up to the cache line (192 bytes).  There are 10 rows, so we have
345    * 1920 bytes total.
346    */
347   EXPECT_EQ(l.slices[0].offset, 0);
348   EXPECT_EQ(l.slices[0].row_stride, 192);
349   EXPECT_EQ(l.slices[0].surface_stride, 1920);
350   EXPECT_EQ(l.slices[0].size, 1920);
351}
352
353/* dEQP-GLES3.functional.texture.format.unsized.rgba_unsigned_byte_3d_pot */
354TEST(AFBCLayout, Linear3D)
355{
356   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
357                        AFBC_FORMAT_MOD_SPARSE);
358
359   struct pan_image_layout l = {
360      .modifier = modifier,
361      .format = PIPE_FORMAT_R8G8B8A8_UNORM,
362      .width = 8,
363      .height = 32,
364      .depth = 16,
365      .nr_samples = 1,
366      .dim = MALI_TEXTURE_DIMENSION_3D,
367      .nr_slices = 1
368   };
369
370   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
371
372   /* AFBC Surface stride is bytes between consecutive surface headers, which is
373    * the header size since this is a 3D texture. At superblock size 16x16, the 8x32
374    * layer has 1x2 superblocks, so the header size is 2 * 16 = 32 bytes,
375    * rounded up to cache line 64.
376    *
377    * There is only 1 superblock per row, so the row stride is the bytes per 1
378    * header block = 16.
379    *
380    * There are 16 layers of size 64 so afbc.header_size = 16 * 64 = 1024.
381    *
382    * Each 16x16 superblock consumes 16 * 16 * 4 = 1024 bytes. There are 2 * 1 *
383    * 16 superblocks in the image, so body size is 32768.
384    */
385   EXPECT_EQ(l.slices[0].offset, 0);
386   EXPECT_EQ(l.slices[0].row_stride, 16);
387   EXPECT_EQ(l.slices[0].afbc.header_size, 1024);
388   EXPECT_EQ(l.slices[0].afbc.body_size, 32768);
389   EXPECT_EQ(l.slices[0].afbc.surface_stride, 64);
390   EXPECT_EQ(l.slices[0].surface_stride, 2048); /* XXX: Not meaningful? */
391   EXPECT_EQ(l.slices[0].size, 32768); /* XXX: Not used by anything and wrong */
392}
393
394TEST(AFBCLayout, Tiled16x16)
395{
396   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
397                        AFBC_FORMAT_MOD_TILED |
398                        AFBC_FORMAT_MOD_SPARSE);
399
400   struct pan_image_layout l = {
401      .modifier = modifier,
402      .format = PIPE_FORMAT_R8G8B8A8_UNORM,
403      .width = 917,
404      .height = 417,
405      .depth = 1,
406      .nr_samples = 1,
407      .dim = MALI_TEXTURE_DIMENSION_2D,
408      .nr_slices = 1
409   };
410
411   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
412
413   /* The image is 917x417. Superblocks are 16x16, so there are 58x27
414    * superblocks. Superblocks are grouped into 8x8 tiles, so there are 8x4
415    * tiles of superblocks. So the row stride is 16 * 8 * 8 * 8 = 8192 bytes.
416    * There are 4 tiles vertically, so the header is 8192 * 4 = 32768 bytes.
417    * This is already 4096-byte aligned.
418    *
419    * Each tile of superblock contains 128x128 pixels and each pixel is 4 bytes,
420    * so tiles are 65536 bytes, meaning the payload is 8 * 4 * 65536 = 2097152
421    * bytes.
422    *
423    * In total, the AFBC surface is 32768 + 2097152 = 2129920 bytes.
424    */
425   EXPECT_EQ(l.slices[0].offset, 0);
426   EXPECT_EQ(l.slices[0].row_stride, 8192);
427   EXPECT_EQ(l.slices[0].afbc.header_size, 32768);
428   EXPECT_EQ(l.slices[0].afbc.body_size, 2097152);
429   EXPECT_EQ(l.slices[0].surface_stride, 2129920);
430   EXPECT_EQ(l.slices[0].size, 2129920);
431}
432
433TEST(AFBCLayout, Linear16x16Minimal)
434{
435   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
436                        AFBC_FORMAT_MOD_SPARSE);
437
438   struct pan_image_layout l = {
439      .modifier = modifier,
440      .format = PIPE_FORMAT_R8_UNORM,
441      .width = 1,
442      .height = 1,
443      .depth = 1,
444      .nr_samples = 1,
445      .dim = MALI_TEXTURE_DIMENSION_2D,
446      .nr_slices = 1
447   };
448
449   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
450
451   /* Image is 1x1 to test for correct alignment everywhere. */
452   EXPECT_EQ(l.slices[0].offset, 0);
453   EXPECT_EQ(l.slices[0].row_stride, 16);
454   EXPECT_EQ(l.slices[0].afbc.header_size, 64);
455   EXPECT_EQ(l.slices[0].afbc.body_size, 32 * 8);
456   EXPECT_EQ(l.slices[0].surface_stride, 64 + (32 * 8));
457   EXPECT_EQ(l.slices[0].size, 64 + (32 * 8));
458}
459
460TEST(AFBCLayout, Tiled16x16Minimal)
461{
462   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
463                        AFBC_FORMAT_MOD_TILED |
464                        AFBC_FORMAT_MOD_SPARSE);
465
466   struct pan_image_layout l = {
467      .modifier = modifier,
468      .format = PIPE_FORMAT_R8_UNORM,
469      .width = 1,
470      .height = 1,
471      .depth = 1,
472      .nr_samples = 1,
473      .dim = MALI_TEXTURE_DIMENSION_2D,
474      .nr_slices = 1
475   };
476
477   ASSERT_TRUE(pan_image_layout_init(&l, NULL));
478
479   /* Image is 1x1 to test for correct alignment everywhere. */
480   EXPECT_EQ(l.slices[0].offset, 0);
481   EXPECT_EQ(l.slices[0].row_stride, 16 * 8 * 8);
482   EXPECT_EQ(l.slices[0].afbc.header_size, 4096);
483   EXPECT_EQ(l.slices[0].afbc.body_size, 32 * 8 * 8 * 8);
484   EXPECT_EQ(l.slices[0].surface_stride, 4096 + (32 * 8 * 8 * 8));
485   EXPECT_EQ(l.slices[0].size, 4096 + (32 * 8 * 8 * 8));
486}
487