]> git.cworth.org Git - fips/blob - metrics.c
fdd794a726863bba4a4b756d81be9d95e82b10d1
[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 "context.h"
33 #include "metrics-info.h"
34 #include "xmalloc.h"
35
36 int frames;
37 int verbose;
38
39 #define MAX_MONITORS_IN_FLIGHT 1000
40
41 /* Timer query */
42 typedef struct timer_query
43 {
44         unsigned id;
45
46         metrics_op_t op;
47         struct timer_query *next;
48 } timer_query_t;
49
50 /* Performance-monitor query */
51 typedef struct monitor
52 {
53         unsigned id;
54
55         metrics_op_t op;
56         struct monitor *next;
57 } monitor_t;
58
59 typedef struct op_metrics
60 {
61         /* This happens to also be the index into the
62          * ctx->op_metrics array currently
63          */
64         metrics_op_t op;
65         double time_ns;
66
67         double **counters;
68 } op_metrics_t;
69
70 struct metrics
71 {
72         metrics_op_t op;
73
74         /* GL_TIME_ELAPSED query for which glEndQuery has not yet
75          * been called. */
76         unsigned timer_begun_id;
77
78         /* GL_TIME_ELAPSED queries for which glEndQuery has been
79          * called, (but results have not yet been queried). */
80         timer_query_t *timer_head;
81         timer_query_t *timer_tail;
82
83         /* Performance monitor for which glEndPerfMonitorAMD has not
84          * yet been called. */
85         unsigned monitor_begun_id;
86
87         /* Performance monitors for which glEndPerfMonitorAMD has
88          * been called, (but results have not yet been queried). */
89         monitor_t *monitor_head;
90         monitor_t *monitor_tail;
91
92         int monitors_in_flight;
93
94         unsigned num_op_metrics;
95         op_metrics_t *op_metrics;
96 };
97
98 static const char *
99 metrics_op_string (metrics_op_t op)
100 {
101         if (op >= METRICS_OP_SHADER)
102                 return "Shader program";
103
104         switch (op)
105         {
106         case METRICS_OP_ACCUM:
107                 return "glAccum*(+)";
108         case METRICS_OP_BUFFER_DATA:
109                 return "glBufferData(+)";
110         case METRICS_OP_BUFFER_SUB_DATA:
111                 return "glCopyBufferSubData*";
112         case METRICS_OP_BITMAP:
113                 return "glBitmap*";
114         case METRICS_OP_BLIT_FRAMEBUFFER:
115                 return "glBlitFramebuffer*";
116         case METRICS_OP_CLEAR:
117                 return "glClear(+)";
118         case METRICS_OP_CLEAR_BUFFER_DATA:
119                 return "glCearBufferData(+)";
120         case METRICS_OP_CLEAR_TEX_IMAGE:
121                 return "glClearTexImage(+)";
122         case METRICS_OP_COPY_PIXELS:
123                 return "glCopyPixels";
124         case METRICS_OP_COPY_TEX_IMAGE:
125                 return "glCopyTexImage(+)";
126         case METRICS_OP_DRAW_PIXELS:
127                 return "glDrawPixels";
128         case METRICS_OP_GET_TEX_IMAGE:
129                 return "glGetTexImage(+)";
130         case METRICS_OP_READ_PIXELS:
131                 return "glReadPixels*";
132         case METRICS_OP_TEX_IMAGE:
133                 return "glTexImage*(+)";
134         default:
135                 fprintf (stderr, "fips: Internal error: "
136                          "Unknown metrics op value: %d\n", op);
137                 exit (1);
138         }
139
140         return "";
141 }
142
143 void
144 metrics_counter_start (void)
145 {
146         context_t *ctx = context_get_current ();
147         metrics_t *metrics = ctx->metrics;
148         unsigned i;
149
150         /* Initialize the timer_query and monitor objects */
151         glGenQueries (1, &metrics->timer_begun_id);
152
153         glGenPerfMonitorsAMD (1, &metrics->monitor_begun_id);
154
155         for (i = 0; i < ctx->metrics_info.num_groups; i++)
156         {
157                 metrics_group_info_t *group;
158                 int num_counters;
159
160                 group = &ctx->metrics_info.groups[i];
161
162                 num_counters = group->num_counters;
163                 if (group->max_active_counters < group->num_counters)
164                 {
165                         fprintf (stderr, "Warning: Only monitoring %d/%d counters from group %d\n",
166                                  group->max_active_counters,
167                                  group->num_counters, i);
168                         num_counters = group->max_active_counters;
169
170                 }
171
172                 glSelectPerfMonitorCountersAMD(metrics->monitor_begun_id,
173                                                GL_TRUE, group->id,
174                                                num_counters,
175                                                group->counter_ids);
176         }
177
178         /* Start the queries */
179         glBeginQuery (GL_TIME_ELAPSED, metrics->timer_begun_id);
180
181         glBeginPerfMonitorAMD (metrics->monitor_begun_id);
182 }
183
184 void
185 metrics_counter_stop (void)
186 {
187         context_t *ctx = context_get_current ();
188         metrics_t *metrics = ctx->metrics;
189         timer_query_t *timer;
190         monitor_t *monitor;
191
192         /* Stop the current timer and monitor. */
193         glEndQuery (GL_TIME_ELAPSED);
194         glEndPerfMonitorAMD (metrics->monitor_begun_id);
195
196         /* Add these IDs to our lists of outstanding queries and
197          * monitors so the results can be collected later. */
198         timer = xmalloc (sizeof (timer_query_t));
199
200         timer->op = metrics->op;
201         timer->id = metrics->timer_begun_id;
202         timer->next = NULL;
203
204         if (metrics->timer_tail) {
205                 metrics->timer_tail->next = timer;
206                 metrics->timer_tail = timer;
207         } else {
208                 metrics->timer_tail = timer;
209                 metrics->timer_head = timer;
210         }
211
212         /* Create a new performance-monitor query */
213         monitor = xmalloc (sizeof (monitor_t));
214
215         monitor->op = metrics->op;
216         monitor->id = metrics->monitor_begun_id;
217         monitor->next = NULL;
218
219         if (metrics->monitor_tail) {
220                 metrics->monitor_tail->next = monitor;
221                 metrics->monitor_tail = monitor;
222         } else {
223                 metrics->monitor_tail = monitor;
224                 metrics->monitor_head = monitor;
225         }
226
227         metrics->monitors_in_flight++;
228
229         /* Avoid being a resource hog and collect outstanding results
230          * once we have sent off a large number of
231          * queries. (Presumably, many of the outstanding queries are
232          * available by now.)
233          */
234         if (metrics->monitors_in_flight > MAX_MONITORS_IN_FLIGHT)
235                 metrics_collect_available ();
236 }
237
238 void
239 metrics_set_current_op (metrics_op_t op)
240 {
241         context_t *ctx = context_get_current ();
242         metrics_t *metrics = ctx->metrics;
243
244         metrics->op = op;
245 }
246
247 metrics_op_t
248 metrics_get_current_op (void)
249 {
250         context_t *ctx = context_get_current ();
251         metrics_t *metrics = ctx->metrics;
252
253         return metrics->op;
254 }
255
256 static void
257 op_metrics_init (context_t *ctx, op_metrics_t *metrics, metrics_op_t op)
258 {
259         metrics_info_t *info = &ctx->metrics_info;
260         unsigned i, j;
261
262         metrics->op = op;
263         metrics->time_ns = 0.0;
264
265         metrics->counters = xmalloc (sizeof(double *) * info->num_groups);
266
267         for (i = 0; i < info->num_groups; i++) {
268                 metrics->counters[i] = xmalloc (sizeof (double) *
269                                                 info->groups[i].num_counters);
270                 for (j = 0; j < info->groups[i].num_counters; j++)
271                         metrics->counters[i][j] = 0.0;
272         }
273 }
274
275 static op_metrics_t *
276 ctx_get_op_metrics (context_t *ctx, metrics_op_t op)
277 {
278         metrics_t *metrics = ctx->metrics;
279         unsigned i;
280
281         if (op >= metrics->num_op_metrics)
282         {
283                 metrics->op_metrics = realloc (metrics->op_metrics,
284                                                (op + 1) * sizeof (op_metrics_t));
285                 for (i = metrics->num_op_metrics; i < op + 1; i++)
286                         op_metrics_init (ctx, &metrics->op_metrics[i], i);
287
288                 metrics->num_op_metrics = op + 1;
289         }
290
291         return &metrics->op_metrics[op];
292 }
293
294 static void
295 accumulate_program_metrics (metrics_op_t op, GLuint *result, GLuint size)
296 {
297 #define CONSUME(var)                                                    \
298         if (p + sizeof(var) > ((unsigned char *) result) + size)        \
299         {                                                               \
300                 fprintf (stderr, "Unexpected end-of-buffer while "      \
301                          "parsing results\n");                          \
302                 break;                                                  \
303         }                                                               \
304         (var) = *((typeof(var) *) p);                                   \
305         p += sizeof(var);
306
307         context_t *ctx = context_get_current ();
308         metrics_info_t *info = &ctx->metrics_info;
309         op_metrics_t *metrics = ctx_get_op_metrics (ctx, op);
310         unsigned char *p = (unsigned char *) result;
311
312         while (p < ((unsigned char *) result) + size)
313         {
314                 GLuint group_id, group_index;
315                 GLuint counter_id, counter_index;
316                 metrics_group_info_t *group;
317                 double value;
318                 unsigned i;
319
320                 CONSUME (group_id);
321                 CONSUME (counter_id);
322
323                 for (i = 0; i < info->num_groups; i++) {
324                         if (info->groups[i].id == group_id)
325                                 break;
326                 }
327                 group_index = i;
328                 assert (group_index < info->num_groups);
329                 group = &info->groups[group_index];
330
331                 for (i = 0; i < group->num_counters; i++) {
332                         if (group->counter_ids[i] == counter_id)
333                                 break;
334                 }
335                 counter_index = i;
336                 assert (counter_index < group->num_counters);
337
338                 switch (group->counter_types[counter_index])
339                 {
340                         uint uint_value;
341                         uint64_t uint64_value;
342                         float float_value;
343                 case GL_UNSIGNED_INT:
344                         CONSUME (uint_value);
345                         value = uint_value;
346                         break;
347                 case GL_UNSIGNED_INT64_AMD:
348                         CONSUME (uint64_value);
349                         value = uint64_value;
350                         break;
351                 case GL_PERCENTAGE_AMD:
352                 case GL_FLOAT:
353                         CONSUME (float_value);
354                         value = float_value;
355                         break;
356                 default:
357                         fprintf (stderr, "fips: Warning: Unknown counter value type (%d)\n",
358                                  group->counter_types[counter_index]);
359                         value = 0.0;
360                         break;
361                 }
362
363                 metrics->counters[group_index][counter_index] += value;
364         }
365 }
366
367 static void
368 accumulate_program_time (metrics_op_t op, unsigned time_ns)
369 {
370         context_t *ctx = context_get_current ();
371         op_metrics_t *metrics;
372
373         metrics = ctx_get_op_metrics (ctx, op);
374
375         metrics->time_ns += time_ns;
376 }
377
378 typedef struct per_stage_metrics
379 {
380         op_metrics_t *metrics;
381         shader_stage_info_t *stage;
382         double time_ns;
383         double active;
384 } per_stage_metrics_t;
385
386 static int
387 _is_shader_stage_counter (metrics_info_t *info,
388                           unsigned group_index,
389                           unsigned counter_index)
390 {
391         shader_stage_info_t *stage;
392         unsigned i;
393
394         for (i = 0; i < info->num_shader_stages; i++) {
395                 stage = &info->stages[i];
396
397                 if (stage->active_group_index == group_index &&
398                     stage->active_counter_index == counter_index)
399                 {
400                         return 1;
401                 }
402
403                 if (stage->stall_group_index == group_index &&
404                     stage->stall_counter_index == counter_index)
405                 {
406                         return 1;
407                 }
408         }
409
410         return 0;
411 }
412
413 static void
414 print_per_stage_metrics (context_t *ctx,
415                          per_stage_metrics_t *per_stage,
416                          double total)
417 {
418         metrics_info_t *info = &ctx->metrics_info;
419         op_metrics_t *metric = per_stage->metrics;
420         metrics_group_info_t *group;
421         const char *op_string;
422         unsigned group_index, counter;
423         double value;
424
425         /* Don't print anything for stages with no alloted time. */
426         if (per_stage->time_ns == 0.0)
427                 return;
428
429         op_string = metrics_op_string (metric->op);
430
431         printf ("%21s", op_string);
432
433         if (metric->op >= METRICS_OP_SHADER) {
434                 printf (" %3d", metric->op - METRICS_OP_SHADER);
435         } else {
436                 printf ("    ");
437
438         }
439
440         if (per_stage->stage)
441                 printf (" %cS:", per_stage->stage->name[0]);
442         else
443                 printf ("   :");
444
445         printf ("\t%7.2f ms (%4.1f%%)",
446                 per_stage->time_ns / 1e6,
447                 per_stage->time_ns / total * 100);
448
449         if (per_stage->active)
450                 printf (", %4.1f%% active", per_stage->active * 100);
451
452         printf ("\n");
453
454         /* I'm not seeing a lot of value printing the rest of these
455          * performance counters by default yet. Use --verbose to get
456          * them for now. */
457         if (! verbose)
458                 return;
459
460         printf ("[");
461         for (group_index = 0; group_index < info->num_groups; group_index++) {
462                 group = &info->groups[group_index];
463                 for (counter = 0; counter < group->num_counters; counter++) {
464
465                         /* Don't print this counter value if it's a
466                          * per-stage cycle counter, (which we have
467                          * already accounted for). */
468                         if (_is_shader_stage_counter (info, group_index, counter))
469                                 continue;
470
471                         value = metric->counters[group_index][counter];
472                         if (value == 0.0)
473                                 continue;
474                         printf ("%s: %.2f ", group->counter_names[counter],
475                                 value / 1e6);
476                 }
477         }
478         printf ("]\n");
479 }
480
481 static int
482 time_compare(const void *in_a, const void *in_b, void *arg unused)
483 {
484         const per_stage_metrics_t *a = in_a;
485         const per_stage_metrics_t *b = in_b;
486
487
488         if (a->time_ns < b->time_ns)
489                 return -1;
490         if (a->time_ns > b->time_ns)
491                 return 1;
492         return 0;
493 }
494
495 static void
496 print_program_metrics (void)
497 {
498         context_t *ctx = context_get_current ();
499         metrics_t *metrics = ctx->metrics;
500         metrics_info_t *info = &ctx->metrics_info;
501         unsigned num_shader_stages = info->num_shader_stages;
502         per_stage_metrics_t *sorted, *per_stage;
503         double total_time, op_cycles;
504         op_metrics_t *op;
505         unsigned group_index, counter_index;
506         unsigned i, j, num_sorted;
507
508         /* Make a sorted list of the per-stage operations by time
509          * used, and figure out the total so we can print percentages.
510          */
511         num_sorted = metrics->num_op_metrics * num_shader_stages;
512
513         sorted = xmalloc (sizeof (*sorted) * num_sorted);
514
515         total_time = 0.0;
516
517         for (i = 0; i < metrics->num_op_metrics; i++) {
518
519                 op = &metrics->op_metrics[i];
520
521                 /* Accumulate total time across all ops. */
522                 total_time += op->time_ns;
523
524                 /* Also, find total cycles in all stages of this op. */
525                 op_cycles = 0.0;
526
527                 for (j = 0; j < num_shader_stages; j++) {
528                         /* Active cycles */
529                         group_index = info->stages[j].active_group_index;
530                         counter_index = info->stages[j].active_counter_index;
531                         op_cycles += op->counters[group_index][counter_index];
532
533                         /* Stall cycles */
534                         group_index = info->stages[j].stall_group_index;
535                         counter_index = info->stages[j].stall_counter_index;
536                         op_cycles += op->counters[group_index][counter_index];
537                 }
538
539                 for (j = 0; j < num_shader_stages; j++) {
540                         double active_cycles, stall_cycles, stage_cycles;
541
542                         /* Active cycles */
543                         group_index = info->stages[j].active_group_index;
544                         counter_index = info->stages[j].active_counter_index;
545                         active_cycles = op->counters[group_index][counter_index];
546
547                         /* Stall cycles */
548                         group_index = info->stages[j].stall_group_index;
549                         counter_index = info->stages[j].stall_counter_index;
550                         stall_cycles = op->counters[group_index][counter_index];
551
552                         stage_cycles = active_cycles + stall_cycles;
553
554                         per_stage = &sorted[i * num_shader_stages + j];
555                         per_stage->metrics = op;
556
557                         if (op_cycles) {
558                                 per_stage->stage = &info->stages[j];
559                                 per_stage->time_ns = op->time_ns * (stage_cycles / op_cycles);
560                         } else {
561                                 /* If we don't have any per-stage cycle counts
562                                  * for this operation, then use the first
563                                  * stage as a placeholder for all the time,
564                                  * but NULL-ify the stage info so that the
565                                  * report doesn't lie about this time being
566                                  * from any particular stage. */
567                                 per_stage->stage = NULL;
568                                 if (j == 0) {
569                                         per_stage->time_ns = op->time_ns;
570                                 } else {
571                                         per_stage->time_ns = 0.0;
572                                 }
573                         }
574
575                         if (stage_cycles) {
576                                 per_stage->active = active_cycles / stage_cycles;
577                         } else {
578                                 per_stage->active = 0.0;
579                         }
580                 }
581         }
582
583         qsort_r (sorted, num_sorted, sizeof (*sorted),
584                  time_compare, metrics->op_metrics);
585
586         for (i = 0; i < num_sorted; i++)
587                 print_per_stage_metrics (ctx, &sorted[i], total_time);
588
589         free (sorted);
590 }
591
592 /* Called at program exit.
593  *
594  * This is similar to metrics_info_fini, but only frees any used
595  * memory. Notably, it does not call any OpenGL functions, (since the
596  * OpenGL context no longer exists at program exit).
597  */
598 static void
599 metrics_exit (void)
600 {
601         context_t *ctx = context_get_current ();
602         metrics_t *metrics = ctx->metrics;
603         metrics_info_t *info = &ctx->metrics_info;
604         unsigned i, j;
605         timer_query_t *timer, *timer_next;
606         monitor_t *monitor, *monitor_next;
607
608         if (verbose)
609                 printf ("fips: terminating\n");
610
611         if (! info->initialized)
612                 return;
613
614         for (timer = metrics->timer_head;
615              timer;
616              timer = timer_next)
617         {
618                 timer_next = timer->next;
619                 free (timer);
620         }
621
622         for (monitor = metrics->monitor_head;
623              monitor;
624              monitor = monitor_next)
625         {
626                 monitor_next = monitor->next;
627                 free (monitor);
628         }
629
630         for (i = 0; i < info->num_groups; i++) {
631                 metrics_group_info_t *group = &info->groups[i];
632
633                 for (j = 0; j < group->num_counters; i++)
634                         free (group->counter_names[j]);
635
636                 free (group->counter_types);
637                 free (group->counter_names);
638                 free (group->counter_ids);
639
640                 free (group->name);
641         }
642
643         free (info->groups);
644
645         for (i = 0; i < info->num_shader_stages; i++)
646                 free (info->stages[i].name);
647
648         free (info->stages);
649 }
650
651 void
652 metrics_collect_available (void)
653 {
654         context_t *ctx = context_get_current ();
655         metrics_t *metrics = ctx->metrics;
656
657         /* Consume all timer queries that are ready. */
658         timer_query_t *timer = metrics->timer_head;
659
660         while (timer) {
661                 GLuint available, elapsed;
662
663                 glGetQueryObjectuiv (timer->id,
664                                      GL_QUERY_RESULT_AVAILABLE, &available);
665                 if (! available)
666                         break;
667
668                 glGetQueryObjectuiv (timer->id,
669                                      GL_QUERY_RESULT, &elapsed);
670
671                 accumulate_program_time (timer->op, elapsed);
672
673                 metrics->timer_head = timer->next;
674                 if (metrics->timer_head == NULL)
675                         metrics->timer_tail = NULL;
676
677                 glDeleteQueries (1, &timer->id);
678
679                 free (timer);
680                 timer = metrics->timer_head;
681         }
682
683         /* And similarly for all performance monitors that are ready. */
684         monitor_t *monitor = metrics->monitor_head;
685
686         while (monitor) {
687                 GLuint available, result_size, *result;
688                 GLint bytes_written;
689
690                 glGetPerfMonitorCounterDataAMD (monitor->id,
691                                                 GL_PERFMON_RESULT_AVAILABLE_AMD,
692                                                 sizeof (available), &available,
693                                                 NULL);
694                 if (! available)
695                         break;
696
697                 glGetPerfMonitorCounterDataAMD (monitor->id,
698                                                 GL_PERFMON_RESULT_SIZE_AMD,
699                                                 sizeof (result_size),
700                                                 &result_size, NULL);
701
702                 result = xmalloc (result_size);
703
704                 glGetPerfMonitorCounterDataAMD (monitor->id,
705                                                 GL_PERFMON_RESULT_AMD,
706                                                 result_size, result,
707                                                 &bytes_written);
708
709                 accumulate_program_metrics (monitor->op, result, result_size);
710
711                 free (result);
712
713                 metrics->monitor_head = monitor->next;
714                 if (metrics->monitor_head == NULL)
715                         metrics->monitor_tail = NULL;
716
717                 glDeletePerfMonitorsAMD (1, &monitor->id);
718
719                 free (monitor);
720
721                 metrics->monitors_in_flight--;
722
723                 monitor = metrics->monitor_head;
724         }
725 }
726
727
728 void
729 metrics_end_frame (void)
730 {
731         static int initialized = 0;
732         static struct timeval tv_start, tv_now;
733
734         if (! initialized) {
735                 gettimeofday (&tv_start, NULL);
736                 atexit (metrics_exit);
737                 if (getenv ("FIPS_VERBOSE"))
738                         verbose = 1;
739                 initialized = 1;
740         }
741
742         frames++;
743
744         metrics_collect_available ();
745
746         if (frames % 15 == 0) {
747                 double fps;
748
749                 gettimeofday (&tv_now, NULL);
750
751                 fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
752                                          (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
753
754                 printf("FPS: %.3f\n", fps);
755
756                 print_program_metrics ();
757         }
758 }
759
760 metrics_t *
761 metrics_create (void)
762 {
763         metrics_t *metrics;
764
765         metrics = xmalloc (sizeof (metrics_t));
766
767         metrics->op = 0;
768
769         metrics->timer_begun_id = 0;
770
771         metrics->timer_head = NULL;
772         metrics->timer_tail = NULL;
773
774         metrics->monitor_begun_id = 0;
775
776         metrics->monitor_head = NULL;
777         metrics->monitor_tail = NULL;
778
779         metrics->monitors_in_flight = 0;
780
781         metrics->num_op_metrics = 0;
782         metrics->op_metrics = NULL;
783
784         return metrics;
785 }
786
787 void
788 metrics_fini (metrics_t *metrics)
789 {
790         timer_query_t *timer, *timer_next;
791         monitor_t *monitor, *monitor_next;
792
793         /* Discard and cleanup any outstanding queries. */
794         if (metrics->timer_begun_id) {
795                 glEndQuery (GL_TIME_ELAPSED);
796                 glDeleteQueries (1, &metrics->timer_begun_id);
797                 metrics->timer_begun_id = 0;
798         }
799
800         for (timer = metrics->timer_head;
801              timer;
802              timer = timer_next)
803         {
804                 glDeleteQueries (1, &timer->id);
805                 timer_next = timer->next;
806                 free (timer);
807         }
808         metrics->timer_head = NULL;
809         metrics->timer_tail = NULL;
810
811         if (metrics->monitor_begun_id) {
812                 glEndPerfMonitorAMD (metrics->monitor_begun_id);
813                 glDeletePerfMonitorsAMD (1, &metrics->monitor_begun_id);
814                 metrics->monitor_begun_id = 0;
815         }
816
817         for (monitor = metrics->monitor_head;
818              monitor;
819              monitor = monitor_next)
820         {
821                 glDeletePerfMonitorsAMD (1, &monitor->id);
822                 monitor_next = monitor->next;
823                 free (monitor);
824         }
825         metrics->monitor_head = NULL;
826         metrics->monitor_tail = NULL;
827
828         metrics->monitors_in_flight = 0;
829 }
830
831 void
832 metrics_destroy (metrics_t *metrics)
833 {
834         metrics_fini (metrics);
835
836         free (metrics);
837 }