]> git.cworth.org Git - fips/blob - metrics.c
e00bb6348851adb9a9a1d0033e0b59c23f1d3195
[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
27 #include <sys/time.h>
28
29 #include "fips-dispatch-gl.h"
30
31 #include "metrics.h"
32
33 typedef struct counter
34 {
35         unsigned id;
36         unsigned program;
37         struct counter *next;
38 } counter_t;
39
40 typedef struct program_metrics
41 {
42         /* This happens to also be the index into the
43          * ctx->program_metrics array currently
44          */
45         unsigned id;
46         double time_ns;
47 } program_metrics_t;
48
49 typedef struct context
50 {
51         unsigned int program;
52
53         counter_t *counter_head;
54         counter_t *counter_tail;
55
56         unsigned num_program_metrics;
57         program_metrics_t *program_metrics;
58 } context_t;
59
60 /* FIXME: Need a map from integers to context objects and track the
61  * current context with glXMakeContextCurrent, eglMakeCurrent, etc. */
62
63 context_t current_context;
64
65 int frames;
66 int verbose;
67
68 unsigned
69 metrics_counter_new (void)
70 {
71         counter_t *counter;
72
73         counter = malloc (sizeof(counter_t));
74         if (counter == NULL) {
75                 fprintf (stderr, "Out of memory\n");
76                 exit (1);
77         }
78
79         glGenQueries (1, &counter->id);
80
81         counter->program = current_context.program;
82         counter->next = NULL;
83
84         if (current_context.counter_tail) {
85                 current_context.counter_tail->next = counter;
86                 current_context.counter_tail = counter;
87         } else {
88                 current_context.counter_tail = counter;
89                 current_context.counter_head = counter;
90         }
91
92         return counter->id;
93 }
94
95 void
96 metrics_counter_start (unsigned counter)
97 {
98         glBeginQuery (GL_TIME_ELAPSED, counter);
99 }
100
101 void
102 metrics_counter_stop (void)
103 {
104         glEndQuery (GL_TIME_ELAPSED);
105 }
106
107 void
108 metrics_set_current_program (unsigned program)
109 {
110         current_context.program = program;
111 }
112
113 static void
114 accumulate_program_time (unsigned program_id, unsigned time_ns)
115 {
116         context_t *ctx = &current_context;
117         unsigned i;
118
119         if (program_id >= ctx->num_program_metrics) {
120                 ctx->program_metrics = realloc (ctx->program_metrics,
121                                                 (program_id + 1) * sizeof (program_metrics_t));
122                 for (i = ctx->num_program_metrics; i < program_id + 1; i++) {
123                         ctx->program_metrics[i].id = i;
124                         ctx->program_metrics[i].time_ns = 0.0;
125                 }
126
127                 ctx->num_program_metrics = program_id + 1;
128         }
129
130         ctx->program_metrics[program_id].time_ns += time_ns;
131 }
132
133 static int
134 time_compare(const void *in_a, const void *in_b, void *arg)
135 {
136         int a = *(const int *)in_a;
137         int b = *(const int *)in_b;
138         struct program_metrics *metrics = arg;
139
140         if (metrics[a].time_ns < metrics[b].time_ns)
141                 return -1;
142         if (metrics[a].time_ns > metrics[b].time_ns)
143                 return 1;
144         return 0;
145 }
146
147 static void
148 print_program_metrics (void)
149 {
150         context_t *ctx = &current_context;
151         unsigned i;
152         int *sorted; /* Sorted indices into the ctx->program_metrics */
153         double total = 0;
154
155         /* Make a sorted list of the programs by time used, and figure
156          * out to total so we can print percentages.
157          */
158         sorted = calloc(ctx->num_program_metrics, sizeof(*sorted));
159         for (i = 0; i < ctx->num_program_metrics; i++) {
160                 sorted[i] = i;
161                 total += ctx->program_metrics[i].time_ns;
162         }
163         qsort_r(sorted, ctx->num_program_metrics, sizeof(*sorted),
164                 time_compare, ctx->program_metrics);
165
166         for (i = 0; i < ctx->num_program_metrics; i++) {
167                 struct program_metrics *metric =
168                         &ctx->program_metrics[sorted[i]];
169
170                 /* Since we sparsely fill the array based on program
171                  * id, many "programs" have no time.
172                  */
173                 if (metric->time_ns == 0.0)
174                         continue;
175
176                 printf ("Program %d:\t%7.2f ms (% 2.1f%%)\n",
177                         metric->id, metric->time_ns / 1e6,
178                         metric->time_ns / total * 100);
179         }
180 }
181
182 /* Called at program exit */
183 static void
184 metrics_exit (void)
185 {
186         if (verbose)
187                 printf ("fips: terminating\n");
188 }
189
190
191 void
192 metrics_end_frame (void)
193 {
194         static int initialized = 0;
195         static struct timeval tv_start, tv_now;
196
197         if (! initialized) {
198                 gettimeofday (&tv_start, NULL);
199                 atexit (metrics_exit);
200                 if (getenv ("FIPS_VERBOSE"))
201                         verbose = 1;
202                 initialized = 1;
203         }
204
205         if (verbose)
206                 printf ("fips: frame %d complete\n", frames);
207
208         frames++;
209         gettimeofday (&tv_now, NULL);
210
211         /* Consume all counters that are ready. */
212         counter_t *counter = current_context.counter_head;
213
214         while (counter) {
215                 GLuint available, elapsed;
216
217                 glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT_AVAILABLE,
218                                      &available);
219                 if (! available)
220                         break;
221
222                 glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT, &elapsed);
223
224                 accumulate_program_time (counter->program, elapsed);
225
226                 current_context.counter_head = counter->next;
227                 if (current_context.counter_head == NULL)
228                         current_context.counter_tail = NULL;
229
230                 glDeleteQueries (1, &counter->id);
231
232                 free (counter);
233                 counter = current_context.counter_head;
234         }
235
236         if (frames % 60 == 0) {
237                 double fps;
238
239                 fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
240                                          (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
241
242                 printf("FPS: %.3f\n", fps);
243
244                 print_program_metrics ();
245         }
246 }