]> git.cworth.org Git - fips/blobdiff - metrics.c
Free all fip-allocated data when the program exits
[fips] / metrics.c
index 77d7321b7f7e393682d9ef76abb314eddd6da74e..5c5cd1b77585cf881260e75cfab023ed754102c4 100644 (file)
--- a/metrics.c
+++ b/metrics.c
 
 #include <stdio.h>
 #include <stdlib.h>
-
+#include <assert.h>
 #include <sys/time.h>
 
 #include "fips-dispatch-gl.h"
 
 #include "metrics.h"
+#include "xmalloc.h"
+
+/* Timer query */
+typedef struct timer_query
+{
+       unsigned id;
+
+       metrics_op_t op;
+       struct timer_query *next;
+} timer_query_t;
 
-typedef struct counter
+/* Performance-monitor query */
+typedef struct monitor
 {
        unsigned id;
 
        metrics_op_t op;
-       struct counter *next;
-} counter_t;
+       struct monitor *next;
+} monitor_t;
+
 
 typedef struct op_metrics
 {
@@ -45,14 +57,43 @@ typedef struct op_metrics
         */
        metrics_op_t op;
        double time_ns;
+
+       double **counters;
 } op_metrics_t;
 
+typedef struct metrics_group_info
+{
+       GLuint id;
+       char *name;
+
+       GLuint num_counters;
+       GLuint max_active_counters;
+
+       GLuint *counters;
+       char **counter_names;
+       GLuint *counter_types;
+
+} metrics_group_info_t;
+
+typedef struct metrics_info
+{
+       int initialized;
+
+       unsigned num_groups;
+       metrics_group_info_t *groups;
+} metrics_info_t;
+
 typedef struct context
 {
+       metrics_info_t metrics_info;
+
        metrics_op_t op;
 
-       counter_t *counter_head;
-       counter_t *counter_tail;
+       timer_query_t *timer_head;
+       timer_query_t *timer_tail;
+
+       monitor_t *monitor_head;
+       monitor_t *monitor_tail;
 
        unsigned num_op_metrics;
        op_metrics_t *op_metrics;
@@ -66,6 +107,123 @@ context_t current_context;
 int frames;
 int verbose;
 
+static void
+metrics_group_info_init (metrics_group_info_t *group, GLuint id)
+{
+       GLsizei length;
+       unsigned i;
+
+       group->id = id;
+
+       /* Get name */
+       glGetPerfMonitorGroupStringAMD (id, 0, &length, NULL);
+
+       group->name = xmalloc (length + 1);
+
+       glGetPerfMonitorGroupStringAMD (id, length + 1, NULL, group->name);
+
+       /* Get number of counters */
+       group->num_counters = 0;
+       group->max_active_counters = 0;
+       glGetPerfMonitorCountersAMD (group->id,
+                                    (int *) &group->num_counters,
+                                    (int *) &group->max_active_counters,
+                                    0, NULL);
+
+       /* Get counter numbers */
+       group->counters = xmalloc (group->num_counters * sizeof (GLuint));
+
+       glGetPerfMonitorCountersAMD (group->id, NULL, NULL,
+                                    group->num_counters,
+                                    group->counters);
+
+       /* Get counter names */
+       group->counter_names = xmalloc (group->num_counters * sizeof (char *));
+       group->counter_types = xmalloc (group->num_counters * sizeof (GLuint));
+
+       for (i = 0; i < group->num_counters; i++) {
+               glGetPerfMonitorCounterInfoAMD (group->id,
+                                               group->counters[i],
+                                               GL_COUNTER_TYPE_AMD,
+                                               &group->counter_types[i]);
+
+               /* We assume that all peformance counters are made
+                * available as uint32 values. The code calling
+                * CONSUME in accumulate_program_metrics will need to
+                * be extended to accomodate other counter values. */
+               if (group->counter_types[i] != GL_UNSIGNED_INT) {
+                       fprintf (stderr, "fips: Internal error: No support for non-uint counter values\n");
+                       exit (1);
+               }
+
+               glGetPerfMonitorCounterStringAMD (group->id,
+                                                 group->counters[i],
+                                                 0, &length, NULL);
+
+               group->counter_names[i] = xmalloc (length + 1);
+
+               glGetPerfMonitorCounterStringAMD (group->id,
+                                                 group->counters[i],
+                                                 length + 1, NULL,
+                                                 group->counter_names[i]);
+       }
+}
+
+static void
+metrics_group_info_fini (metrics_group_info_t *group)
+{
+       unsigned i;
+
+       for (i = 0; i < group->num_counters; i++)
+               free (group->counter_names[i]);
+
+       free (group->counter_types);
+       free (group->counter_names);
+       free (group->counters);
+
+       free (group->name);
+}
+
+static void
+metrics_info_fini (metrics_info_t *info);
+
+void
+metrics_info_init (void)
+{
+       unsigned i;
+       GLuint *group_ids;
+       metrics_info_t *metrics_info = &current_context.metrics_info;
+
+       if (metrics_info->initialized)
+               metrics_info_fini (metrics_info);
+
+       glGetPerfMonitorGroupsAMD ((int *) &metrics_info->num_groups, 0, NULL);
+
+       group_ids = xmalloc (metrics_info->num_groups * sizeof (GLuint));
+
+       glGetPerfMonitorGroupsAMD (NULL, metrics_info->num_groups, group_ids);
+
+       metrics_info->groups = xmalloc (metrics_info->num_groups * sizeof (metrics_group_info_t));
+
+       for (i = 0; i < metrics_info->num_groups; i++)
+               metrics_group_info_init (&metrics_info->groups[i], i);
+
+       free (group_ids);
+
+       metrics_info->initialized = 1;
+}
+
+static void
+metrics_info_fini (metrics_info_t *info)
+{
+       unsigned i;
+
+       for (i = 0; i < info->num_groups; i++)
+               metrics_group_info_fini (&info->groups[i]);
+
+       free (info->groups);
+}
+
 static const char *
 metrics_op_string (metrics_op_t op)
 {
@@ -103,7 +261,7 @@ metrics_op_string (metrics_op_t op)
        case METRICS_OP_TEX_IMAGE:
                return "glTexImage*(+)";
        default:
-               fprintf (stderr, "Internal error: "
+               fprintf (stderr, "fips: Internal error: "
                         "Unknown metrics op value: %d\n", op);
                exit (1);
        }
@@ -114,34 +272,78 @@ metrics_op_string (metrics_op_t op)
 void
 metrics_counter_start (void)
 {
-       counter_t *counter;
+       context_t *ctx = &current_context;
+       timer_query_t *timer;
+       monitor_t *monitor;
+       unsigned i;
 
-       counter = malloc (sizeof(counter_t));
-       if (counter == NULL) {
-               fprintf (stderr, "Out of memory\n");
-               exit (1);
+       /* Create new timer query, add to list */
+       timer = xmalloc (sizeof (timer_query_t));
+
+       timer->op = ctx->op;
+       timer->next = NULL;
+
+       if (ctx->timer_tail) {
+               ctx->timer_tail->next = timer;
+               ctx->timer_tail = timer;
+       } else {
+               ctx->timer_tail = timer;
+               ctx->timer_head = timer;
        }
 
-       glGenQueries (1, &counter->id);
+       /* Create a new performance-monitor query */
+       monitor = xmalloc (sizeof (monitor_t));
 
-       counter->op = current_context.op;
-       counter->next = NULL;
+       monitor->op = ctx->op;
+       monitor->next = NULL;
 
-       if (current_context.counter_tail) {
-               current_context.counter_tail->next = counter;
-               current_context.counter_tail = counter;
+       if (ctx->monitor_tail) {
+               ctx->monitor_tail->next = monitor;
+               ctx->monitor_tail = monitor;
        } else {
-               current_context.counter_tail = counter;
-               current_context.counter_head = counter;
+               ctx->monitor_tail = monitor;
+               ctx->monitor_head = monitor;
        }
 
-       glBeginQuery (GL_TIME_ELAPSED, counter->id);
+       /* Initialize the timer_query and monitor objects */
+       glGenQueries (1, &timer->id);
+
+       glGenPerfMonitorsAMD (1, &monitor->id);
+
+       for (i = 0; i < ctx->metrics_info.num_groups; i++)
+       {
+               metrics_group_info_t *group;
+               int num_counters;
+
+               group = &ctx->metrics_info.groups[i];
+
+               num_counters = group->num_counters;
+               if (group->max_active_counters < group->num_counters)
+               {
+                       fprintf (stderr, "Warning: Only monitoring %d/%d counters from group %d\n",
+                                group->max_active_counters,
+                                group->num_counters, i);
+                       num_counters = group->max_active_counters;
+
+               }
+
+               glSelectPerfMonitorCountersAMD(monitor->id,
+                                              GL_TRUE, group->id,
+                                              num_counters,
+                                              group->counters);
+       }
+
+       /* Start the queries */
+       glBeginQuery (GL_TIME_ELAPSED, timer->id);
+
+       glBeginPerfMonitorAMD (monitor->id);
 }
 
 void
 metrics_counter_stop (void)
 {
        glEndQuery (GL_TIME_ELAPSED);
+       glEndPerfMonitorAMD (current_context.monitor_tail->id);
 }
 
 void
@@ -157,23 +359,91 @@ metrics_get_current_op (void)
 }
 
 static void
-accumulate_program_time (metrics_op_t op, unsigned time_ns)
+op_metrics_init (context_t *ctx, op_metrics_t *metrics, metrics_op_t op)
+{
+       metrics_info_t *info = &ctx->metrics_info;
+       unsigned i, j;
+
+       metrics->op = op;
+       metrics->time_ns = 0.0;
+
+       metrics->counters = xmalloc (sizeof(double *) * info->num_groups);
+
+       for (i = 0; i < info->num_groups; i++) {
+               metrics->counters[i] = xmalloc (sizeof (double) *
+                                               info->groups[i].num_counters);
+               for (j = 0; j < info->groups[i].num_counters; j++)
+                       metrics->counters[i][j] = 0.0;
+       }
+}
+
+static op_metrics_t *
+ctx_get_op_metrics (context_t *ctx, metrics_op_t op)
 {
-       context_t *ctx = &current_context;
        unsigned i;
 
-       if (op >= ctx->num_op_metrics) {
+       if (op >= ctx->num_op_metrics)
+       {
                ctx->op_metrics = realloc (ctx->op_metrics,
                                           (op + 1) * sizeof (op_metrics_t));
-               for (i = ctx->num_op_metrics; i < op + 1; i++) {
-                       ctx->op_metrics[i].op = i;
-                       ctx->op_metrics[i].time_ns = 0.0;
-               }
+               for (i = ctx->num_op_metrics; i < op + 1; i++)
+                       op_metrics_init (ctx, &ctx->op_metrics[i], i);
 
                ctx->num_op_metrics = op + 1;
        }
 
-       ctx->op_metrics[op].time_ns += time_ns;
+       return &ctx->op_metrics[op];
+}
+
+static void
+accumulate_program_metrics (metrics_op_t op, GLuint *result, GLuint size)
+{
+#define CONSUME(var)                                                   \
+       if (p + sizeof(var) > ((unsigned char *) result) + size)        \
+       {                                                               \
+               fprintf (stderr, "Unexpected end-of-buffer while "      \
+                        "parsing results\n");                          \
+               break;                                                  \
+       }                                                               \
+       (var) = *((typeof(var) *) p);                                   \
+       p += sizeof(var);
+
+       context_t *ctx = &current_context;
+       unsigned char *p = (unsigned char *) result;
+
+       while (p < ((unsigned char *) result) + size)
+       {
+               GLuint group_id, counter_id, counter_index;
+               metrics_group_info_t *group;
+               uint32_t value;
+               unsigned i;
+
+               CONSUME (group_id);
+               CONSUME (counter_id);
+               CONSUME (value);
+
+               assert (group_id < ctx->metrics_info.num_groups);
+               group = &ctx->metrics_info.groups[group_id];
+
+               for (i = 0; i < group->num_counters; i++) {
+                       if (group->counters[i] == counter_id)
+                               break;
+               }
+               counter_index = i;
+               assert (counter_index < group->num_counters);
+
+               ctx->op_metrics[op].counters[group_id][counter_index] += value;
+       }
+}
+
+static void
+accumulate_program_time (metrics_op_t op, unsigned time_ns)
+{
+       op_metrics_t *metrics;
+
+       metrics = ctx_get_op_metrics (&current_context, op);
+
+       metrics->time_ns += time_ns;
 }
 
 static int
@@ -190,13 +460,57 @@ time_compare(const void *in_a, const void *in_b, void *arg)
        return 0;
 }
 
+static void
+print_op_metrics (context_t *ctx, op_metrics_t *metric, double total)
+{
+       metrics_info_t *info = &ctx->metrics_info;
+       metrics_group_info_t *group;
+       const char *op_string;
+       unsigned i, group_id, counter;
+       double value;
+
+       /* Since we sparsely fill the array based on program
+        * id, many "programs" have no time.
+        */
+       if (metric->time_ns == 0.0)
+               return;
+
+       op_string = metrics_op_string (metric->op);
+
+       printf ("%s", op_string);
+       if (metric->op >= METRICS_OP_SHADER) {
+               printf (" %d:", metric->op - METRICS_OP_SHADER);
+       } else {
+               printf (":");
+               for (i = strlen (op_string); i < 20; i++)
+                       printf (" ");
+       }
+
+       printf ("\t%7.2f ms (% 2.1f%%)",
+               metric->time_ns / 1e6,
+               metric->time_ns / total * 100);
+
+       printf ("[");
+       for (group_id = 0; group_id < info->num_groups; group_id++) {
+               group = &info->groups[group_id];
+               for (counter = 0; counter < group->num_counters; counter++) {
+                       value = metric->counters[group_id][counter];
+                       if (value == 0.0)
+                               continue;
+                       printf ("%s: %.2f ", group->counter_names[counter],
+                               value / 1e6);
+               }
+       }
+       printf ("]\n");
+}
+
 static void
 print_program_metrics (void)
 {
        context_t *ctx = &current_context;
-       unsigned i, j;
        int *sorted; /* Sorted indices into the ctx->op_metrics */
        double total = 0;
+       unsigned i;
 
        /* Make a sorted list of the operations by time used, and figure
         * out the total so we can print percentages.
@@ -209,30 +523,10 @@ print_program_metrics (void)
        qsort_r(sorted, ctx->num_op_metrics, sizeof(*sorted),
                time_compare, ctx->op_metrics);
 
-       for (i = 0; i < ctx->num_op_metrics; i++) {
-               const char *op_string;
-               op_metrics_t *metric =&ctx->op_metrics[sorted[i]];
-
-               /* Since we sparsely fill the array based on program
-                * id, many "programs" have no time.
-                */
-               if (metric->time_ns == 0.0)
-                       continue;
-
-               op_string = metrics_op_string (metric->op);
-
-               printf ("%s", op_string);
-               if (metric->op >= METRICS_OP_SHADER) {
-                       printf (" %d:", metric->op - METRICS_OP_SHADER);
-               } else {
-                       printf (":");
-                       for (j = strlen (op_string); j < 20; j++)
-                               printf (" ");
-               }
-               printf ("\t%7.2f ms (% 2.1f%%)\n",
-                       metric->time_ns / 1e6,
-                       metric->time_ns / total * 100);
-       }
+       for (i = 0; i < ctx->num_op_metrics; i++)
+               print_op_metrics (ctx, &ctx->op_metrics[sorted[i]], total);
+
+       free (sorted);
 }
 
 /* Called at program exit */
@@ -241,6 +535,8 @@ metrics_exit (void)
 {
        if (verbose)
                printf ("fips: terminating\n");
+
+       metrics_info_fini (&current_context.metrics_info);
 }
 
 
@@ -264,29 +560,70 @@ metrics_end_frame (void)
        frames++;
        gettimeofday (&tv_now, NULL);
 
-       /* Consume all counters that are ready. */
-       counter_t *counter = current_context.counter_head;
+       /* Consume all timer queries that are ready. */
+       timer_query_t *timer = current_context.timer_head;
 
-       while (counter) {
+       while (timer) {
                GLuint available, elapsed;
 
-               glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT_AVAILABLE,
-                                    &available);
+               glGetQueryObjectuiv (timer->id,
+                                    GL_QUERY_RESULT_AVAILABLE, &available);
+               if (! available)
+                       break;
+
+               glGetQueryObjectuiv (timer->id,
+                                    GL_QUERY_RESULT, &elapsed);
+
+               accumulate_program_time (timer->op, elapsed);
+
+               current_context.timer_head = timer->next;
+               if (current_context.timer_head == NULL)
+                       current_context.timer_tail = NULL;
+
+               glDeleteQueries (1, &timer->id);
+
+               free (timer);
+               timer = current_context.timer_head;
+       }
+
+       /* And similarly for all performance monitors that are ready. */
+       monitor_t *monitor = current_context.monitor_head;
+
+       while (monitor) {
+               GLuint available, result_size, *result;
+               GLint bytes_written;
+
+               glGetPerfMonitorCounterDataAMD (monitor->id,
+                                               GL_PERFMON_RESULT_AVAILABLE_AMD,
+                                               sizeof (available), &available,
+                                               NULL);
                if (! available)
                        break;
 
-               glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT, &elapsed);
+               glGetPerfMonitorCounterDataAMD (monitor->id,
+                                               GL_PERFMON_RESULT_SIZE_AMD,
+                                               sizeof (result_size),
+                                               &result_size, NULL);
+
+               result = xmalloc (result_size);
+
+               glGetPerfMonitorCounterDataAMD (monitor->id,
+                                               GL_PERFMON_RESULT_AMD,
+                                               result_size, result,
+                                               &bytes_written);
+
+               accumulate_program_metrics (monitor->op, result, result_size);
 
-               accumulate_program_time (counter->op, elapsed);
+               free (result);
 
-               current_context.counter_head = counter->next;
-               if (current_context.counter_head == NULL)
-                       current_context.counter_tail = NULL;
+               current_context.monitor_head = monitor->next;
+               if (current_context.monitor_head == NULL)
+                       current_context.monitor_tail = NULL;
 
-               glDeleteQueries (1, &counter->id);
+               glDeletePerfMonitorsAMD (1, &monitor->id);
 
-               free (counter);
-               counter = current_context.counter_head;
+               free (monitor);
+               monitor = current_context.monitor_head;
        }
 
        if (frames % 60 == 0) {