]> git.cworth.org Git - fips/blob - metrics.c
Close two memory leaks in fips metrics reporting.
[fips] / metrics.c
1 /* Copyright © 2013, Intel Corporation
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21
22 #define _GNU_SOURCE
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <sys/time.h>
28
29 #include "fips-dispatch-gl.h"
30
31 #include "metrics.h"
32 #include "xmalloc.h"
33
34 /* Timer query */
35 typedef struct timer_query
36 {
37         unsigned id;
38
39         metrics_op_t op;
40         struct timer_query *next;
41 } timer_query_t;
42
43 /* Performance-monitor query */
44 typedef struct monitor
45 {
46         unsigned id;
47
48         metrics_op_t op;
49         struct monitor *next;
50 } monitor_t;
51
52
53 typedef struct op_metrics
54 {
55         /* This happens to also be the index into the
56          * ctx->op_metrics array currently
57          */
58         metrics_op_t op;
59         double time_ns;
60
61         double **counters;
62 } op_metrics_t;
63
64 typedef struct metrics_group_info
65 {
66         GLuint id;
67         char *name;
68
69         GLuint num_counters;
70         GLuint max_active_counters;
71
72         GLuint *counters;
73         char **counter_names;
74         GLuint *counter_types;
75
76 } metrics_group_info_t;
77
78 typedef struct metrics_info
79 {
80         unsigned num_groups;
81         metrics_group_info_t *groups;
82 } metrics_info_t;
83
84 typedef struct context
85 {
86         metrics_info_t metrics_info;
87
88         metrics_op_t op;
89
90         timer_query_t *timer_head;
91         timer_query_t *timer_tail;
92
93         monitor_t *monitor_head;
94         monitor_t *monitor_tail;
95
96         unsigned num_op_metrics;
97         op_metrics_t *op_metrics;
98 } context_t;
99
100 /* FIXME: Need a map from integers to context objects and track the
101  * current context with glXMakeContextCurrent, eglMakeCurrent, etc. */
102
103 context_t current_context;
104
105 int frames;
106 int verbose;
107
108 static void
109 metrics_group_info_init (metrics_group_info_t *group, GLuint id)
110 {
111         GLsizei length;
112         unsigned i;
113
114         group->id = id;
115
116         /* Get name */
117         glGetPerfMonitorGroupStringAMD (id, 0, &length, NULL);
118
119         group->name = xmalloc (length + 1);
120
121         glGetPerfMonitorGroupStringAMD (id, length + 1, NULL, group->name);
122
123         /* Get number of counters */
124         group->num_counters = 0;
125         group->max_active_counters = 0;
126         glGetPerfMonitorCountersAMD (group->id,
127                                      (int *) &group->num_counters,
128                                      (int *) &group->max_active_counters,
129                                      0, NULL);
130
131         /* Get counter numbers */
132         group->counters = xmalloc (group->num_counters * sizeof (GLuint));
133
134         glGetPerfMonitorCountersAMD (group->id, NULL, NULL,
135                                      group->num_counters,
136                                      group->counters);
137
138         /* Get counter names */
139         group->counter_names = xmalloc (group->num_counters * sizeof (char *));
140         group->counter_types = xmalloc (group->num_counters * sizeof (GLuint));
141
142         for (i = 0; i < group->num_counters; i++) {
143                 glGetPerfMonitorCounterInfoAMD (group->id,
144                                                 group->counters[i],
145                                                 GL_COUNTER_TYPE_AMD,
146                                                 &group->counter_types[i]);
147
148                 /* We assume that all peformance counters are made
149                  * available as uint32 values. The code calling
150                  * CONSUME in accumulate_program_metrics will need to
151                  * be extended to accomodate other counter values. */
152                 if (group->counter_types[i] != GL_UNSIGNED_INT) {
153                         fprintf (stderr, "fips: Internal error: No support for non-uint counter values\n");
154                         exit (1);
155                 }
156
157                 glGetPerfMonitorCounterStringAMD (group->id,
158                                                   group->counters[i],
159                                                   0, &length, NULL);
160
161                 group->counter_names[i] = xmalloc (length + 1);
162
163                 glGetPerfMonitorCounterStringAMD (group->id,
164                                                   group->counters[i],
165                                                   length + 1, NULL,
166                                                   group->counter_names[i]);
167         }
168 }
169
170 void
171 metrics_info_init (void)
172 {
173         unsigned i;
174         GLuint *group_ids;
175         metrics_info_t *metrics_info = &current_context.metrics_info;
176
177         glGetPerfMonitorGroupsAMD ((int *) &metrics_info->num_groups, 0, NULL);
178
179         group_ids = xmalloc (metrics_info->num_groups * sizeof (GLuint));
180
181         glGetPerfMonitorGroupsAMD (NULL, metrics_info->num_groups, group_ids);
182
183         metrics_info->groups = xmalloc (metrics_info->num_groups * sizeof (metrics_group_info_t));
184
185         for (i = 0; i < metrics_info->num_groups; i++)
186                 metrics_group_info_init (&metrics_info->groups[i], i);
187
188         free (group_ids);
189 }
190
191 static const char *
192 metrics_op_string (metrics_op_t op)
193 {
194         if (op >= METRICS_OP_SHADER)
195                 return "Shader program";
196
197         switch (op)
198         {
199         case METRICS_OP_ACCUM:
200                 return "glAccum*(+)";
201         case METRICS_OP_BUFFER_DATA:
202                 return "glBufferData(+)";
203         case METRICS_OP_BUFFER_SUB_DATA:
204                 return "glCopyBufferSubData*";
205         case METRICS_OP_BITMAP:
206                 return "glBitmap*";
207         case METRICS_OP_BLIT_FRAMEBUFFER:
208                 return "glBlitFramebuffer*";
209         case METRICS_OP_CLEAR:
210                 return "glClear(+)";
211         case METRICS_OP_CLEAR_BUFFER_DATA:
212                 return "glCearBufferData(+)";
213         case METRICS_OP_CLEAR_TEX_IMAGE:
214                 return "glClearTexImage(+)";
215         case METRICS_OP_COPY_PIXELS:
216                 return "glCopyPixels";
217         case METRICS_OP_COPY_TEX_IMAGE:
218                 return "glCopyTexImage(+)";
219         case METRICS_OP_DRAW_PIXELS:
220                 return "glDrawPixels";
221         case METRICS_OP_GET_TEX_IMAGE:
222                 return "glGetTexImage(+)";
223         case METRICS_OP_READ_PIXELS:
224                 return "glReadPixels*";
225         case METRICS_OP_TEX_IMAGE:
226                 return "glTexImage*(+)";
227         default:
228                 fprintf (stderr, "fips: Internal error: "
229                          "Unknown metrics op value: %d\n", op);
230                 exit (1);
231         }
232
233         return "";
234 }
235
236 void
237 metrics_counter_start (void)
238 {
239         context_t *ctx = &current_context;
240         timer_query_t *timer;
241         monitor_t *monitor;
242         unsigned i;
243
244         /* Create new timer query, add to list */
245         timer = xmalloc (sizeof (timer_query_t));
246
247         timer->op = ctx->op;
248         timer->next = NULL;
249
250         if (ctx->timer_tail) {
251                 ctx->timer_tail->next = timer;
252                 ctx->timer_tail = timer;
253         } else {
254                 ctx->timer_tail = timer;
255                 ctx->timer_head = timer;
256         }
257
258         /* Create a new performance-monitor query */
259         monitor = xmalloc (sizeof (monitor_t));
260
261         monitor->op = ctx->op;
262         monitor->next = NULL;
263
264         if (ctx->monitor_tail) {
265                 ctx->monitor_tail->next = monitor;
266                 ctx->monitor_tail = monitor;
267         } else {
268                 ctx->monitor_tail = monitor;
269                 ctx->monitor_head = monitor;
270         }
271
272         /* Initialize the timer_query and monitor objects */
273         glGenQueries (1, &timer->id);
274
275         glGenPerfMonitorsAMD (1, &monitor->id);
276
277         for (i = 0; i < ctx->metrics_info.num_groups; i++)
278         {
279                 metrics_group_info_t *group;
280                 int num_counters;
281
282                 group = &ctx->metrics_info.groups[i];
283
284                 num_counters = group->num_counters;
285                 if (group->max_active_counters < group->num_counters)
286                 {
287                         fprintf (stderr, "Warning: Only monitoring %d/%d counters from group %d\n",
288                                  group->max_active_counters,
289                                  group->num_counters, i);
290                         num_counters = group->max_active_counters;
291
292                 }
293
294                 glSelectPerfMonitorCountersAMD(monitor->id,
295                                                GL_TRUE, group->id,
296                                                num_counters,
297                                                group->counters);
298         }
299
300         /* Start the queries */
301         glBeginQuery (GL_TIME_ELAPSED, timer->id);
302
303         glBeginPerfMonitorAMD (monitor->id);
304 }
305
306 void
307 metrics_counter_stop (void)
308 {
309         glEndQuery (GL_TIME_ELAPSED);
310         glEndPerfMonitorAMD (current_context.monitor_tail->id);
311 }
312
313 void
314 metrics_set_current_op (metrics_op_t op)
315 {
316         current_context.op = op;
317 }
318
319 metrics_op_t
320 metrics_get_current_op (void)
321 {
322         return current_context.op;
323 }
324
325 static void
326 op_metrics_init (context_t *ctx, op_metrics_t *metrics, metrics_op_t op)
327 {
328         metrics_info_t *info = &ctx->metrics_info;
329         unsigned i, j;
330
331         metrics->op = op;
332         metrics->time_ns = 0.0;
333
334         metrics->counters = xmalloc (sizeof(double *) * info->num_groups);
335
336         for (i = 0; i < info->num_groups; i++) {
337                 metrics->counters[i] = xmalloc (sizeof (double) *
338                                                 info->groups[i].num_counters);
339                 for (j = 0; j < info->groups[i].num_counters; j++)
340                         metrics->counters[i][j] = 0.0;
341         }
342 }
343
344 static op_metrics_t *
345 ctx_get_op_metrics (context_t *ctx, metrics_op_t op)
346 {
347         unsigned i;
348
349         if (op >= ctx->num_op_metrics)
350         {
351                 ctx->op_metrics = realloc (ctx->op_metrics,
352                                            (op + 1) * sizeof (op_metrics_t));
353                 for (i = ctx->num_op_metrics; i < op + 1; i++)
354                         op_metrics_init (ctx, &ctx->op_metrics[i], i);
355
356                 ctx->num_op_metrics = op + 1;
357         }
358
359         return &ctx->op_metrics[op];
360 }
361
362 static void
363 accumulate_program_metrics (metrics_op_t op, GLuint *result, GLuint size)
364 {
365 #define CONSUME(var)                                                    \
366         if (p + sizeof(var) > ((unsigned char *) result) + size)        \
367         {                                                               \
368                 fprintf (stderr, "Unexpected end-of-buffer while "      \
369                          "parsing results\n");                          \
370                 break;                                                  \
371         }                                                               \
372         (var) = *((typeof(var) *) p);                                   \
373         p += sizeof(var);
374
375         context_t *ctx = &current_context;
376         unsigned char *p = (unsigned char *) result;
377
378         while (p < ((unsigned char *) result) + size)
379         {
380                 GLuint group_id, counter_id, counter_index;
381                 metrics_group_info_t *group;
382                 uint32_t value;
383                 unsigned i;
384
385                 CONSUME (group_id);
386                 CONSUME (counter_id);
387                 CONSUME (value);
388
389                 assert (group_id < ctx->metrics_info.num_groups);
390                 group = &ctx->metrics_info.groups[group_id];
391
392                 for (i = 0; i < group->num_counters; i++) {
393                         if (group->counters[i] == counter_id)
394                                 break;
395                 }
396                 counter_index = i;
397                 assert (counter_index < group->num_counters);
398
399                 ctx->op_metrics[op].counters[group_id][counter_index] += value;
400         }
401 }
402
403 static void
404 accumulate_program_time (metrics_op_t op, unsigned time_ns)
405 {
406         op_metrics_t *metrics;
407
408         metrics = ctx_get_op_metrics (&current_context, op);
409
410         metrics->time_ns += time_ns;
411 }
412
413 static int
414 time_compare(const void *in_a, const void *in_b, void *arg)
415 {
416         int a = *(const int *)in_a;
417         int b = *(const int *)in_b;
418         struct op_metrics *metrics = arg;
419
420         if (metrics[a].time_ns < metrics[b].time_ns)
421                 return -1;
422         if (metrics[a].time_ns > metrics[b].time_ns)
423                 return 1;
424         return 0;
425 }
426
427 static void
428 print_op_metrics (context_t *ctx, op_metrics_t *metric, double total)
429 {
430         metrics_info_t *info = &ctx->metrics_info;
431         metrics_group_info_t *group;
432         const char *op_string;
433         unsigned i, group_id, counter;
434         double value;
435
436         /* Since we sparsely fill the array based on program
437          * id, many "programs" have no time.
438          */
439         if (metric->time_ns == 0.0)
440                 return;
441
442         op_string = metrics_op_string (metric->op);
443
444         printf ("%s", op_string);
445         if (metric->op >= METRICS_OP_SHADER) {
446                 printf (" %d:", metric->op - METRICS_OP_SHADER);
447         } else {
448                 printf (":");
449                 for (i = strlen (op_string); i < 20; i++)
450                         printf (" ");
451         }
452
453         printf ("\t%7.2f ms (% 2.1f%%)",
454                 metric->time_ns / 1e6,
455                 metric->time_ns / total * 100);
456
457         printf ("[");
458         for (group_id = 0; group_id < info->num_groups; group_id++) {
459                 group = &info->groups[group_id];
460                 for (counter = 0; counter < group->num_counters; counter++) {
461                         value = metric->counters[group_id][counter];
462                         if (value == 0.0)
463                                 continue;
464                         printf ("%s: %.2f ", group->counter_names[counter],
465                                 value / 1e6);
466                 }
467         }
468         printf ("]\n");
469 }
470
471 static void
472 print_program_metrics (void)
473 {
474         context_t *ctx = &current_context;
475         int *sorted; /* Sorted indices into the ctx->op_metrics */
476         double total = 0;
477         unsigned i;
478
479         /* Make a sorted list of the operations by time used, and figure
480          * out the total so we can print percentages.
481          */
482         sorted = calloc(ctx->num_op_metrics, sizeof(*sorted));
483         for (i = 0; i < ctx->num_op_metrics; i++) {
484                 sorted[i] = i;
485                 total += ctx->op_metrics[i].time_ns;
486         }
487         qsort_r(sorted, ctx->num_op_metrics, sizeof(*sorted),
488                 time_compare, ctx->op_metrics);
489
490         for (i = 0; i < ctx->num_op_metrics; i++)
491                 print_op_metrics (ctx, &ctx->op_metrics[sorted[i]], total);
492
493         free (sorted);
494 }
495
496 /* Called at program exit */
497 static void
498 metrics_exit (void)
499 {
500         if (verbose)
501                 printf ("fips: terminating\n");
502 }
503
504
505 void
506 metrics_end_frame (void)
507 {
508         static int initialized = 0;
509         static struct timeval tv_start, tv_now;
510
511         if (! initialized) {
512                 gettimeofday (&tv_start, NULL);
513                 atexit (metrics_exit);
514                 if (getenv ("FIPS_VERBOSE"))
515                         verbose = 1;
516                 initialized = 1;
517         }
518
519         if (verbose)
520                 printf ("fips: frame %d complete\n", frames);
521
522         frames++;
523         gettimeofday (&tv_now, NULL);
524
525         /* Consume all timer queries that are ready. */
526         timer_query_t *timer = current_context.timer_head;
527
528         while (timer) {
529                 GLuint available, elapsed;
530
531                 glGetQueryObjectuiv (timer->id,
532                                      GL_QUERY_RESULT_AVAILABLE, &available);
533                 if (! available)
534                         break;
535
536                 glGetQueryObjectuiv (timer->id,
537                                      GL_QUERY_RESULT, &elapsed);
538
539                 accumulate_program_time (timer->op, elapsed);
540
541                 current_context.timer_head = timer->next;
542                 if (current_context.timer_head == NULL)
543                         current_context.timer_tail = NULL;
544
545                 glDeleteQueries (1, &timer->id);
546
547                 free (timer);
548                 timer = current_context.timer_head;
549         }
550
551         /* And similarly for all performance monitors that are ready. */
552         monitor_t *monitor = current_context.monitor_head;
553
554         while (monitor) {
555                 GLuint available, result_size, *result;
556                 GLint bytes_written;
557
558                 glGetPerfMonitorCounterDataAMD (monitor->id,
559                                                 GL_PERFMON_RESULT_AVAILABLE_AMD,
560                                                 sizeof (available), &available,
561                                                 NULL);
562                 if (! available)
563                         break;
564
565                 glGetPerfMonitorCounterDataAMD (monitor->id,
566                                                 GL_PERFMON_RESULT_SIZE_AMD,
567                                                 sizeof (result_size),
568                                                 &result_size, NULL);
569
570                 result = xmalloc (result_size);
571
572                 glGetPerfMonitorCounterDataAMD (monitor->id,
573                                                 GL_PERFMON_RESULT_AMD,
574                                                 result_size, result,
575                                                 &bytes_written);
576
577                 accumulate_program_metrics (monitor->op, result, result_size);
578
579                 free (result);
580
581                 current_context.monitor_head = monitor->next;
582                 if (current_context.monitor_head == NULL)
583                         current_context.monitor_tail = NULL;
584
585                 glDeletePerfMonitorsAMD (1, &monitor->id);
586
587                 free (monitor);
588                 monitor = current_context.monitor_head;
589         }
590
591         if (frames % 60 == 0) {
592                 double fps;
593
594                 fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
595                                          (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
596
597                 printf("FPS: %.3f\n", fps);
598
599                 print_program_metrics ();
600         }
601 }