1/*
2 * Copyright © 2020 Google, Inc.
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 <getopt.h>
25#include <inttypes.h>
26#include <locale.h>
27#include <stdlib.h>
28#include <xf86drm.h>
29
30#include "util/u_math.h"
31
32#include "perfcntrs/freedreno_perfcntr.h"
33
34#include "main.h"
35
36static void
37dump_float(void *buf, int sz)
38{
39   uint8_t *ptr = (uint8_t *)buf;
40   uint8_t *end = ptr + sz - 3;
41   int i = 0;
42
43   while (ptr < end) {
44      uint32_t d = 0;
45
46      printf((i % 8) ? " " : "\t");
47
48      d |= *(ptr++) << 0;
49      d |= *(ptr++) << 8;
50      d |= *(ptr++) << 16;
51      d |= *(ptr++) << 24;
52
53      printf("%8f", uif(d));
54
55      if ((i % 8) == 7) {
56         printf("\n");
57      }
58
59      i++;
60   }
61
62   if (i % 8) {
63      printf("\n");
64   }
65}
66
67static void
68dump_hex(void *buf, int sz)
69{
70   uint8_t *ptr = (uint8_t *)buf;
71   uint8_t *end = ptr + sz;
72   int i = 0;
73
74   while (ptr < end) {
75      uint32_t d = 0;
76
77      printf((i % 8) ? " " : "\t");
78
79      d |= *(ptr++) << 0;
80      d |= *(ptr++) << 8;
81      d |= *(ptr++) << 16;
82      d |= *(ptr++) << 24;
83
84      printf("%08x", d);
85
86      if ((i % 8) == 7) {
87         printf("\n");
88      }
89
90      i++;
91   }
92
93   if (i % 8) {
94      printf("\n");
95   }
96}
97
98static const char *shortopts = "df:g:hp:";
99
100static const struct option longopts[] = {
101   {"disasm", no_argument, 0, 'd'},         {"file", required_argument, 0, 'f'},
102   {"groups", required_argument, 0, 'g'},   {"help", no_argument, 0, 'h'},
103   {"perfcntr", required_argument, 0, 'p'}, {0, 0, 0, 0}};
104
105static void
106usage(const char *name)
107{
108   printf(
109      "Usage: %s [-dfgh]\n"
110      "\n"
111      "options:\n"
112      "    -d, --disasm             print disassembled shader\n"
113      "    -f, --file=FILE          read shader from file (instead of stdin)\n"
114      "    -g, --groups=X,Y,Z       use specified group size\n"
115      "    -h, --help               show this message\n"
116      "    -p, --perfcntr=LIST      sample specified performance counters "
117      "(comma\n"
118      "                             separated list)\n",
119      name);
120}
121
122/* performance counter description: */
123static unsigned num_groups;
124static const struct fd_perfcntr_group *groups;
125
126/* Track enabled counters per group: */
127static unsigned *enabled_counters;
128
129static void
130setup_counter(const char *name, struct perfcntr *c)
131{
132   for (int i = 0; i < num_groups; i++) {
133      const struct fd_perfcntr_group *group = &groups[i];
134
135      for (int j = 0; j < group->num_countables; j++) {
136         const struct fd_perfcntr_countable *countable = &group->countables[j];
137
138         if (strcmp(name, countable->name) != 0)
139            continue;
140
141         /*
142          * Allocate a counter to use to monitor the requested countable:
143          */
144         if (enabled_counters[i] >= group->num_counters) {
145            errx(-1, "Too many counters selected in group: %s", group->name);
146         }
147
148         unsigned idx = enabled_counters[i]++;
149         const struct fd_perfcntr_counter *counter = &group->counters[idx];
150
151         /*
152          * And initialize the perfcntr struct, pulling together the info
153          * about selected counter and countable, to simplify life for the
154          * backend:
155          */
156         c->name = name;
157         c->select_reg = counter->select_reg;
158         c->counter_reg_lo = counter->counter_reg_lo;
159         c->counter_reg_hi = counter->counter_reg_hi;
160         c->selector = countable->selector;
161
162         return;
163      }
164   }
165
166   errx(-1, "could not find countable: %s", name);
167}
168
169static struct perfcntr *
170parse_perfcntrs(const struct fd_dev_id *dev_id, const char *perfcntrstr,
171                unsigned *num_perfcntrs)
172{
173   struct perfcntr *counters = NULL;
174   char *cnames, *s;
175   unsigned cnt = 0;
176
177   groups = fd_perfcntrs(dev_id, &num_groups);
178   enabled_counters = calloc(num_groups, sizeof(enabled_counters[0]));
179
180   cnames = strdup(perfcntrstr);
181   while ((s = strstr(cnames, ","))) {
182      char *name = cnames;
183      s[0] = '\0';
184      cnames = &s[1];
185
186      counters = realloc(counters, ++cnt * sizeof(counters[0]));
187      setup_counter(name, &counters[cnt - 1]);
188   }
189
190   char *name = cnames;
191   counters = realloc(counters, ++cnt * sizeof(counters[0]));
192   setup_counter(name, &counters[cnt - 1]);
193
194   *num_perfcntrs = cnt;
195
196   return counters;
197}
198
199int
200main(int argc, char **argv)
201{
202   FILE *in = stdin;
203   const char *perfcntrstr = NULL;
204   struct perfcntr *perfcntrs = NULL;
205   unsigned num_perfcntrs = 0;
206   bool disasm = false;
207   uint32_t grid[3] = {0};
208   int opt, ret;
209
210   setlocale(LC_NUMERIC, "en_US.UTF-8");
211
212   while ((opt = getopt_long_only(argc, argv, shortopts, longopts, NULL)) !=
213          -1) {
214      switch (opt) {
215      case 'd':
216         disasm = true;
217         break;
218      case 'f':
219         in = fopen(optarg, "r");
220         if (!in)
221            err(1, "could not open '%s'", optarg);
222         break;
223      case 'g':
224         ret = sscanf(optarg, "%u,%u,%u", &grid[0], &grid[1], &grid[2]);
225         if (ret != 3)
226            goto usage;
227         break;
228      case 'h':
229         goto usage;
230      case 'p':
231         perfcntrstr = optarg;
232         break;
233      default:
234         printf("unrecognized arg: %c\n", opt);
235         goto usage;
236      }
237   }
238
239   struct fd_device *dev = fd_device_open();
240   struct fd_pipe *pipe = fd_pipe_new(dev, FD_PIPE_3D);
241
242   const struct fd_dev_id *dev_id = fd_pipe_dev_id(pipe);
243
244   printf("got gpu: %s\n", fd_dev_name(dev_id));
245
246   struct backend *backend;
247   switch (fd_dev_gen(dev_id)) {
248   case 4:
249      backend = a4xx_init(dev, dev_id);
250      break;
251   case 6:
252      backend = a6xx_init(dev, dev_id);
253      break;
254   default:
255      err(1, "unsupported gpu generation: a%uxx", fd_dev_gen(dev_id));
256   }
257
258   struct kernel *kernel = backend->assemble(backend, in);
259   printf("localsize: %dx%dx%d\n", kernel->local_size[0], kernel->local_size[1],
260          kernel->local_size[2]);
261   for (int i = 0; i < kernel->num_bufs; i++) {
262      printf("buf[%d]: size=%u\n", i, kernel->buf_sizes[i]);
263      kernel->bufs[i] = fd_bo_new(dev, kernel->buf_sizes[i] * 4, 0, "buf[%d]", i);
264   }
265
266   if (disasm)
267      backend->disassemble(kernel, stdout);
268
269   if (grid[0] == 0)
270      return 0;
271
272   struct fd_submit *submit = fd_submit_new(pipe);
273
274   if (perfcntrstr) {
275      if (!backend->set_perfcntrs) {
276         err(1, "performance counters not supported");
277      }
278      perfcntrs = parse_perfcntrs(dev_id, perfcntrstr, &num_perfcntrs);
279      backend->set_perfcntrs(backend, perfcntrs, num_perfcntrs);
280   }
281
282   backend->emit_grid(kernel, grid, submit);
283
284   struct fd_submit_fence fence = {};
285   util_queue_fence_init(&fence.ready);
286
287   fd_submit_flush(submit, -1, &fence);
288
289   util_queue_fence_wait(&fence.ready);
290
291   for (int i = 0; i < kernel->num_bufs; i++) {
292      fd_bo_cpu_prep(kernel->bufs[i], pipe, FD_BO_PREP_READ);
293      void *map = fd_bo_map(kernel->bufs[i]);
294
295      printf("buf[%d]:\n", i);
296      dump_hex(map, kernel->buf_sizes[i] * 4);
297      dump_float(map, kernel->buf_sizes[i] * 4);
298   }
299
300   if (perfcntrstr) {
301      uint64_t results[num_perfcntrs];
302      backend->read_perfcntrs(backend, results);
303
304      for (unsigned i = 0; i < num_perfcntrs; i++) {
305         printf("%s:\t%'" PRIu64 "\n", perfcntrs[i].name, results[i]);
306      }
307   }
308
309   return 0;
310
311usage:
312   usage(argv[0]);
313   return -1;
314}
315