1 /* Make the test not meaningless when asserts are disabled. */
2#undef NDEBUG
3
4#include <assert.h>
5#include <inttypes.h>
6#include <stdio.h>
7#include <stdlib.h>
8
9#include <amdgpu.h>
10#include "drm-uapi/amdgpu_drm.h"
11#include "drm-uapi/drm_fourcc.h"
12
13#include "ac_surface.h"
14#include "util/macros.h"
15#include "util/u_math.h"
16#include "util/u_vector.h"
17#include "util/mesa-sha1.h"
18#include "addrlib/inc/addrinterface.h"
19
20#include "ac_surface_test_common.h"
21
22/*
23 * The main goal of this test is making sure that we do
24 * not change the meaning of existing modifiers.
25 */
26
27struct test_entry {
28   /* key part */
29   uint64_t modifier;
30   unsigned w;
31   unsigned h;
32   enum pipe_format format;
33
34   /* debug info */
35   const char *name;
36   uint8_t pipes;
37   uint8_t rb;
38   uint8_t banks_or_pkrs;
39   uint8_t se;
40
41   /* value to determine uniqueness */
42   unsigned char hash[20];
43
44   /* u_vector requires power of two sizing */
45   char padding[sizeof(void*) == 8 ? 8 : 16];
46};
47
48static uint64_t
49block_count(unsigned w, unsigned h, unsigned elem_bits, unsigned block_bits,
50            unsigned *aligned_pitch, unsigned *aligned_height)
51{
52   unsigned align_bits = block_bits - elem_bits;
53   unsigned w_align = 1 << (align_bits / 2 + align_bits % 2);
54   unsigned h_align = 1 << (align_bits / 2);
55
56   w = align(w, w_align);
57   h = align(h, h_align);
58
59   if (aligned_pitch)
60      *aligned_pitch = w;
61
62   if (aligned_height)
63      *aligned_height = h;
64   return ((uint64_t)w * h) >> align_bits;
65}
66
67
68static ADDR2_COMPUTE_DCC_ADDRFROMCOORD_INPUT
69get_addr_from_coord_base(ADDR_HANDLE addrlib, const struct radeon_surf *surf,
70                         unsigned w, unsigned h, enum pipe_format format,
71                         bool rb_aligned, bool pipe_aligned)
72{
73   ADDR2_COMPUTE_DCCINFO_INPUT din = {0};
74   ADDR2_COMPUTE_DCCINFO_OUTPUT dout = {0};
75   din.size = sizeof(ADDR2_COMPUTE_DCCINFO_INPUT);
76   dout.size = sizeof(ADDR2_COMPUTE_DCCINFO_OUTPUT);
77
78   din.swizzleMode = surf->u.gfx9.swizzle_mode;
79   din.resourceType = ADDR_RSRC_TEX_2D;
80   din.bpp = util_format_get_blocksizebits(format);
81   din.unalignedWidth = w;
82   din.unalignedHeight = h;
83   din.numSlices = 1;
84   din.numMipLevels = 1;
85   din.numFrags = 1;
86   din.dccKeyFlags.pipeAligned = surf->u.gfx9.color.dcc.pipe_aligned;
87   din.dccKeyFlags.rbAligned = surf->u.gfx9.color.dcc.rb_aligned;
88   din.dataSurfaceSize = surf->surf_size;
89
90   ADDR_E_RETURNCODE ret = Addr2ComputeDccInfo(addrlib, &din, &dout);
91   assert(ret == ADDR_OK);
92
93   ADDR2_COMPUTE_DCC_ADDRFROMCOORD_INPUT dcc_input = {0};
94   dcc_input.size = sizeof(dcc_input);
95   dcc_input.swizzleMode = surf->u.gfx9.swizzle_mode;
96   dcc_input.resourceType = ADDR_RSRC_TEX_2D;
97   dcc_input.bpp = din.bpp;
98   dcc_input.numSlices = 1;
99   dcc_input.numMipLevels = 1;
100   dcc_input.numFrags = 1;
101   dcc_input.dccKeyFlags.pipeAligned = pipe_aligned;
102   dcc_input.dccKeyFlags.rbAligned = rb_aligned;
103   dcc_input.pitch = dout.pitch;
104   dcc_input.height = dout.height;
105   dcc_input.compressBlkWidth = dout.compressBlkWidth;
106   dcc_input.compressBlkHeight = dout.compressBlkHeight;
107   dcc_input.compressBlkDepth = dout.compressBlkDepth;
108   dcc_input.metaBlkWidth     = dout.metaBlkWidth;
109   dcc_input.metaBlkHeight    = dout.metaBlkHeight;
110   dcc_input.metaBlkDepth     = dout.metaBlkDepth;
111   return dcc_input;
112}
113
114static
115void generate_hash(struct ac_addrlib *ac_addrlib,
116                   struct test_entry *entry,
117                   const struct radeon_surf *surf)
118{
119   ADDR_HANDLE addrlib = ac_addrlib_get_handle(ac_addrlib);
120
121   srandom(53);
122   struct mesa_sha1 ctx;
123   _mesa_sha1_init(&ctx);
124
125   _mesa_sha1_update(&ctx, &surf->total_size, sizeof(surf->total_size));
126   _mesa_sha1_update(&ctx, &surf->meta_offset, sizeof(surf->meta_offset));
127   _mesa_sha1_update(&ctx, &surf->display_dcc_offset, sizeof(surf->display_dcc_offset));
128   _mesa_sha1_update(&ctx, &surf->u.gfx9.color.display_dcc_pitch_max,
129                     sizeof(surf->u.gfx9.color.display_dcc_pitch_max));
130
131   ADDR2_COMPUTE_SURFACE_ADDRFROMCOORD_INPUT input = {0};
132   input.size = sizeof(input);
133   input.swizzleMode = surf->u.gfx9.swizzle_mode;
134   input.resourceType = ADDR_RSRC_TEX_2D;
135   input.bpp = util_format_get_blocksizebits(entry->format);
136   input.unalignedWidth = entry->w;
137   input.unalignedHeight = entry->h;
138   input.numSlices = 1;
139   input.numMipLevels = 1;
140   input.numSamples = 1;
141   input.numFrags = 1;
142   input.pitchInElement = surf->u.gfx9.surf_pitch;
143
144   ADDR2_COMPUTE_DCC_ADDRFROMCOORD_INPUT dcc_input = {0};
145   if (surf->meta_offset) {
146      dcc_input = get_addr_from_coord_base(addrlib, surf, entry->w,
147                                           entry->h, entry->format,
148                                           surf->u.gfx9.color.dcc.rb_aligned,
149                                           surf->u.gfx9.color.dcc.pipe_aligned);
150   }
151
152   ADDR2_COMPUTE_DCC_ADDRFROMCOORD_INPUT display_dcc_input = {0};
153   if (surf->display_dcc_offset) {
154      display_dcc_input = get_addr_from_coord_base(addrlib, surf, entry->w,
155                                                   entry->h, entry->format,
156                                                   false, false);
157   }
158
159   for (unsigned i = 0; i < 1000; ++i) {
160      int32_t x, y;
161      x = random();
162      y = random();
163
164      input.x = (x & INT_MAX) % entry->w;
165      input.y = (y & INT_MAX) % entry->h;
166
167      ADDR2_COMPUTE_SURFACE_ADDRFROMCOORD_OUTPUT output = {0};
168      output.size = sizeof(output);
169
170      ADDR_E_RETURNCODE ret = Addr2ComputeSurfaceAddrFromCoord(addrlib, &input, &output);
171      assert(ret == ADDR_OK);
172
173      _mesa_sha1_update(&ctx, &output.addr, sizeof(output.addr));
174
175      if (surf->meta_offset) {
176         dcc_input.x = (x & INT_MAX) % entry->w;
177         dcc_input.y = (y & INT_MAX) % entry->h;
178
179         ADDR2_COMPUTE_DCC_ADDRFROMCOORD_OUTPUT dcc_output = {0};
180         dcc_output.size = sizeof(dcc_output);
181
182         ret = Addr2ComputeDccAddrFromCoord(addrlib, &dcc_input, &dcc_output);
183         assert(ret == ADDR_OK);
184
185         _mesa_sha1_update(&ctx, &dcc_output.addr, sizeof(dcc_output.addr));
186      }
187
188      if (surf->display_dcc_offset) {
189         display_dcc_input.x = (x & INT_MAX) % entry->w;
190         display_dcc_input.y = (y & INT_MAX) % entry->h;
191
192         ADDR2_COMPUTE_DCC_ADDRFROMCOORD_OUTPUT dcc_output = {0};
193         dcc_output.size = sizeof(dcc_output);
194
195         ret = Addr2ComputeDccAddrFromCoord(addrlib, &display_dcc_input, &dcc_output);
196         assert(ret == ADDR_OK);
197
198         _mesa_sha1_update(&ctx, &dcc_output.addr, sizeof(dcc_output.addr));
199      }
200   }
201
202   _mesa_sha1_final(&ctx, entry->hash);
203}
204
205static void test_modifier(const struct radeon_info *info,
206           const char *name,
207                          struct ac_addrlib *addrlib,
208                          uint64_t modifier,
209           enum pipe_format format,
210                          struct u_vector *test_entries)
211{
212   unsigned elem_bits = util_logbase2(util_format_get_blocksize(format));
213   const unsigned dims[][2] = {
214      {1, 1},
215      {1920, 1080},
216      {1366, 768},
217      {3840, 2160},
218      {233, 938},
219   };
220   for (unsigned i = 0; i < ARRAY_SIZE(dims); ++i) {
221      struct ac_surf_config config = (struct ac_surf_config) {
222         .info = (struct ac_surf_info) {
223            .width = dims[i][0],
224            .height = dims[i][1],
225            .depth = 1,
226            .samples = 1,
227            .storage_samples = 1,
228            .levels = 1,
229            .num_channels = 3,
230            .array_size = 1
231         },
232      };
233
234      struct test_entry entry = {
235         .modifier = modifier,
236         .w = config.info.width,
237         .h = config.info.height,
238         .format = format,
239         .name = name,
240         .pipes = G_0098F8_NUM_PIPES(info->gb_addr_config),
241         .rb = G_0098F8_NUM_RB_PER_SE(info->gb_addr_config) +
242               G_0098F8_NUM_SHADER_ENGINES_GFX9(info->gb_addr_config),
243         .se = G_0098F8_NUM_SHADER_ENGINES_GFX9(info->gb_addr_config),
244         .banks_or_pkrs = info->gfx_level >= GFX10 ?
245            G_0098F8_NUM_PKRS(info->gb_addr_config) : G_0098F8_NUM_BANKS(info->gb_addr_config)
246      };
247
248      struct radeon_surf surf = (struct radeon_surf) {
249         .blk_w = 1,
250         .blk_h = 1,
251         .bpe = util_format_get_blocksize(format),
252         .modifier = modifier,
253      };
254
255      int r = ac_compute_surface(addrlib, info, &config, RADEON_SURF_MODE_2D, &surf);
256      assert(!r);
257
258      assert(surf.cmask_offset == 0);
259      assert(surf.fmask_offset == 0);
260
261      unsigned block_size_bits = surf.u.gfx9.swizzle_mode >= ADDR_SW_256KB_Z_X ? 18 : 16;
262
263      uint64_t surf_size;
264      unsigned aligned_pitch, aligned_height;
265      if (modifier != DRM_FORMAT_MOD_LINEAR) {
266         surf_size = block_count(dims[i][0], dims[i][1],
267                  elem_bits, block_size_bits, &aligned_pitch,
268                  &aligned_height) << block_size_bits;
269      } else {
270         aligned_pitch = align(dims[i][0], 256 / util_format_get_blocksize(format));
271         aligned_height = dims[i][1];
272         surf_size = align(dims[i][0] * util_format_get_blocksize(format), 256) * dims[i][1];
273      }
274
275
276      assert(surf.u.gfx9.surf_pitch == aligned_pitch);
277      assert(surf.u.gfx9.surf_height == aligned_height);
278      assert(surf.surf_size == surf_size);
279      uint64_t expected_offset = surf_size;
280
281      if (ac_modifier_has_dcc_retile(modifier)) {
282         unsigned dcc_align = info->gfx_level >= GFX10 ? 4096 : 65536;
283         unsigned dcc_pitch;
284         uint64_t dcc_size = block_count(dims[i][0], dims[i][1],
285                     elem_bits, 20, &dcc_pitch,
286                     NULL) << 12;
287
288         assert(surf.u.gfx9.color.display_dcc_size == align(dcc_size, dcc_align));
289         assert(surf.u.gfx9.color.display_dcc_pitch_max + 1 == dcc_pitch);
290         assert(surf.display_dcc_offset == expected_offset);
291
292         expected_offset += align(dcc_size, dcc_align);
293      } else
294         assert(!surf.display_dcc_offset);
295
296      if (ac_modifier_has_dcc(modifier)) {
297         uint64_t dcc_align = 1;
298         unsigned block_bits;
299         if (info->gfx_level >= GFX10) {
300            unsigned num_pipes = G_0098F8_NUM_PIPES(info->gb_addr_config);
301            if (info->gfx_level >= GFX10_3 &&
302                G_0098F8_NUM_PKRS(info->gb_addr_config) == num_pipes && num_pipes > 1)
303               ++num_pipes;
304            block_bits = 16 +
305               num_pipes +
306               G_0098F8_PIPE_INTERLEAVE_SIZE_GFX9(info->gb_addr_config);
307            block_bits = MAX2(block_bits, 20);
308            dcc_align = MAX2(4096, 256 <<
309                                  (num_pipes +
310                                   G_0098F8_PIPE_INTERLEAVE_SIZE_GFX9(info->gb_addr_config)));
311         } else {
312            block_bits = 18 +
313               G_0098F8_NUM_RB_PER_SE(info->gb_addr_config) +
314               G_0098F8_NUM_SHADER_ENGINES_GFX9(info->gb_addr_config);
315            block_bits = MAX2(block_bits, 20);
316            dcc_align = 65536;
317         }
318
319         expected_offset = align(expected_offset, dcc_align);
320         assert(surf.meta_offset == expected_offset);
321
322         uint64_t dcc_size = block_count(dims[i][0], dims[i][1],
323                     elem_bits, block_bits,
324                     NULL, NULL) << (block_bits - 8);
325         dcc_size = align64(dcc_size, dcc_align);
326         assert(surf.meta_size == dcc_size);
327
328         expected_offset += dcc_size;
329      } else
330         assert(!surf.meta_offset);
331
332      assert(surf.total_size == expected_offset);
333
334      generate_hash(addrlib, &entry, &surf);
335      *(struct test_entry*)u_vector_add(test_entries) = entry;
336   }
337
338}
339
340static void run_modifier_test(struct u_vector *test_entries, const char *name,
341                                  const struct radeon_info *info)
342{
343   struct ac_addrlib *addrlib = ac_addrlib_create(info, NULL);
344   assert(addrlib);
345
346   const struct ac_modifier_options options = {
347      .dcc = true,
348      .dcc_retile = true,
349   };
350
351   enum pipe_format formats[] = {
352      PIPE_FORMAT_R8_UNORM,
353      PIPE_FORMAT_R16_UNORM,
354      PIPE_FORMAT_R32_FLOAT,
355      PIPE_FORMAT_R32G32_FLOAT,
356      PIPE_FORMAT_R32G32B32A32_FLOAT
357   };
358   for (unsigned j = 0; j < ARRAY_SIZE(formats); ++j) {
359      unsigned mod_count = 0;
360      ac_get_supported_modifiers(info, &options, formats[j], &mod_count, NULL);
361
362      uint64_t *modifiers = malloc(sizeof(uint64_t) * mod_count);
363      ac_get_supported_modifiers(info, &options, formats[j], &mod_count, modifiers);
364
365      for (unsigned i = 0; i < mod_count; ++i) {
366         test_modifier(info, name, addrlib, modifiers[i], formats[j], test_entries);
367      }
368
369      free(modifiers);
370   }
371   ac_addrlib_destroy(addrlib);
372}
373
374static int compare_test_entry(const void *a, const void *b)
375{
376   return memcmp(a, b, sizeof(struct test_entry));
377}
378
379static bool test_entry_key_equal(const struct test_entry *a, const struct test_entry *b)
380{
381   return a->modifier == b->modifier && a->w == b->w && a->h == b->h && a->format == b->format;
382}
383
384static bool test_entry_value_equal(const struct test_entry *a, const struct test_entry *b)
385{
386   if (memcmp(a->hash, b->hash, sizeof(a->hash)))
387      return false;
388   return true;
389}
390
391static void print_test_entry(const struct test_entry *e)
392{
393   printf("%.16" PRIx64 " %.4d %.4d %.2d %s %d %d %d %d\n", e->modifier, e->w, e->h,
394          util_format_get_blocksize(e->format), e->name, e->pipes, e->rb, e->se, e->banks_or_pkrs);
395}
396
397int main()
398{
399   STATIC_ASSERT(sizeof(struct test_entry) == 64);
400
401   struct u_vector test_entries;
402   u_vector_init_pow2(&test_entries, 64, sizeof(struct test_entry));
403
404   for (unsigned i = 0; i < ARRAY_SIZE(testcases); ++i) {
405      struct radeon_info info = get_radeon_info(&testcases[i]);
406
407      run_modifier_test(&test_entries, testcases[i].name, &info);
408   }
409
410   qsort(u_vector_tail(&test_entries),
411         u_vector_length(&test_entries),
412         sizeof(struct test_entry),
413         compare_test_entry);
414
415   struct test_entry *cur, *prev = NULL, *prevprev = NULL;
416   bool mismatched_duplicates = false;
417   u_vector_foreach(cur, &test_entries) {
418      if (prev && test_entry_key_equal(cur, prev) &&
419          !test_entry_value_equal(cur, prev)) {
420         if (!prevprev || !test_entry_key_equal(prev, prevprev)) {
421            print_test_entry(prev);
422         }
423         print_test_entry(cur);
424         mismatched_duplicates = true;
425      }
426      prevprev = prev;
427      prev = cur;
428   }
429   assert(!mismatched_duplicates);
430
431   u_vector_finish(&test_entries);
432
433   return 0;
434}
435