]> git.cworth.org Git - fips/blob - metrics.c
Restore metrics op after temporarily changing for non-shader operation
[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
37         metrics_op_t op;
38         struct counter *next;
39 } counter_t;
40
41 typedef struct op_metrics
42 {
43         /* This happens to also be the index into the
44          * ctx->op_metrics array currently
45          */
46         metrics_op_t op;
47         double time_ns;
48 } op_metrics_t;
49
50 typedef struct context
51 {
52         metrics_op_t op;
53
54         counter_t *counter_head;
55         counter_t *counter_tail;
56
57         unsigned num_op_metrics;
58         op_metrics_t *op_metrics;
59 } context_t;
60
61 /* FIXME: Need a map from integers to context objects and track the
62  * current context with glXMakeContextCurrent, eglMakeCurrent, etc. */
63
64 context_t current_context;
65
66 int frames;
67 int verbose;
68
69 static const char *
70 metrics_op_string (metrics_op_t op)
71 {
72         if (op >= METRICS_OP_SHADER)
73                 return "Shader program";
74
75         switch (op)
76         {
77         case METRICS_OP_ACCUM:
78                 return "glAccum*(+)";
79         case METRICS_OP_BUFFER_DATA:
80                 return "glBufferData(+)";
81         case METRICS_OP_BUFFER_SUB_DATA:
82                 return "glCopyBufferSubData*";
83         case METRICS_OP_BITMAP:
84                 return "glBitmap*";
85         case METRICS_OP_BLIT_FRAMEBUFFER:
86                 return "glBlitFramebuffer*";
87         case METRICS_OP_CLEAR:
88                 return "glClear(+)";
89         case METRICS_OP_CLEAR_BUFFER_DATA:
90                 return "glCearBufferData(+)";
91         case METRICS_OP_CLEAR_TEX_IMAGE:
92                 return "glClearTexImage(+)";
93         case METRICS_OP_COPY_PIXELS:
94                 return "glCopyPixels";
95         case METRICS_OP_COPY_TEX_IMAGE:
96                 return "glCopyTexImage(+)";
97         case METRICS_OP_DRAW_PIXELS:
98                 return "glDrawPixels";
99         case METRICS_OP_GET_TEX_IMAGE:
100                 return "glGetTexImage(+)";
101         case METRICS_OP_READ_PIXELS:
102                 return "glReadPixels*";
103         case METRICS_OP_TEX_IMAGE:
104                 return "glTexImage*(+)";
105         default:
106                 fprintf (stderr, "Internal error: "
107                          "Unknown metrics op value: %d\n", op);
108                 exit (1);
109         }
110
111         return "";
112 }
113
114 void
115 metrics_counter_start (void)
116 {
117         counter_t *counter;
118
119         counter = malloc (sizeof(counter_t));
120         if (counter == NULL) {
121                 fprintf (stderr, "Out of memory\n");
122                 exit (1);
123         }
124
125         glGenQueries (1, &counter->id);
126
127         counter->op = current_context.op;
128         counter->next = NULL;
129
130         if (current_context.counter_tail) {
131                 current_context.counter_tail->next = counter;
132                 current_context.counter_tail = counter;
133         } else {
134                 current_context.counter_tail = counter;
135                 current_context.counter_head = counter;
136         }
137
138         glBeginQuery (GL_TIME_ELAPSED, counter->id);
139 }
140
141 void
142 metrics_counter_stop (void)
143 {
144         glEndQuery (GL_TIME_ELAPSED);
145 }
146
147 void
148 metrics_set_current_op (metrics_op_t op)
149 {
150         current_context.op = op;
151 }
152
153 metrics_op_t
154 metrics_get_current_op (void)
155 {
156         return current_context.op;
157 }
158
159 static void
160 accumulate_program_time (metrics_op_t op, unsigned time_ns)
161 {
162         context_t *ctx = &current_context;
163         unsigned i;
164
165         if (op >= ctx->num_op_metrics) {
166                 ctx->op_metrics = realloc (ctx->op_metrics,
167                                            (op + 1) * sizeof (op_metrics_t));
168                 for (i = ctx->num_op_metrics; i < op + 1; i++) {
169                         ctx->op_metrics[i].op = i;
170                         ctx->op_metrics[i].time_ns = 0.0;
171                 }
172
173                 ctx->num_op_metrics = op + 1;
174         }
175
176         ctx->op_metrics[op].time_ns += time_ns;
177 }
178
179 static int
180 time_compare(const void *in_a, const void *in_b, void *arg)
181 {
182         int a = *(const int *)in_a;
183         int b = *(const int *)in_b;
184         struct op_metrics *metrics = arg;
185
186         if (metrics[a].time_ns < metrics[b].time_ns)
187                 return -1;
188         if (metrics[a].time_ns > metrics[b].time_ns)
189                 return 1;
190         return 0;
191 }
192
193 static void
194 print_program_metrics (void)
195 {
196         context_t *ctx = &current_context;
197         unsigned i, j;
198         int *sorted; /* Sorted indices into the ctx->op_metrics */
199         double total = 0;
200
201         /* Make a sorted list of the operations by time used, and figure
202          * out the total so we can print percentages.
203          */
204         sorted = calloc(ctx->num_op_metrics, sizeof(*sorted));
205         for (i = 0; i < ctx->num_op_metrics; i++) {
206                 sorted[i] = i;
207                 total += ctx->op_metrics[i].time_ns;
208         }
209         qsort_r(sorted, ctx->num_op_metrics, sizeof(*sorted),
210                 time_compare, ctx->op_metrics);
211
212         for (i = 0; i < ctx->num_op_metrics; i++) {
213                 const char *op_string;
214                 op_metrics_t *metric =&ctx->op_metrics[sorted[i]];
215
216                 /* Since we sparsely fill the array based on program
217                  * id, many "programs" have no time.
218                  */
219                 if (metric->time_ns == 0.0)
220                         continue;
221
222                 op_string = metrics_op_string (metric->op);
223
224                 printf ("%s", op_string);
225                 if (metric->op >= METRICS_OP_SHADER) {
226                         printf (" %d:", metric->op - METRICS_OP_SHADER);
227                 } else {
228                         printf (":");
229                         for (j = strlen (op_string); j < 20; j++)
230                                 printf (" ");
231                 }
232                 printf ("\t%7.2f ms (% 2.1f%%)\n",
233                         metric->time_ns / 1e6,
234                         metric->time_ns / total * 100);
235         }
236 }
237
238 /* Called at program exit */
239 static void
240 metrics_exit (void)
241 {
242         if (verbose)
243                 printf ("fips: terminating\n");
244 }
245
246
247 void
248 metrics_end_frame (void)
249 {
250         static int initialized = 0;
251         static struct timeval tv_start, tv_now;
252
253         if (! initialized) {
254                 gettimeofday (&tv_start, NULL);
255                 atexit (metrics_exit);
256                 if (getenv ("FIPS_VERBOSE"))
257                         verbose = 1;
258                 initialized = 1;
259         }
260
261         if (verbose)
262                 printf ("fips: frame %d complete\n", frames);
263
264         frames++;
265         gettimeofday (&tv_now, NULL);
266
267         /* Consume all counters that are ready. */
268         counter_t *counter = current_context.counter_head;
269
270         while (counter) {
271                 GLuint available, elapsed;
272
273                 glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT_AVAILABLE,
274                                      &available);
275                 if (! available)
276                         break;
277
278                 glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT, &elapsed);
279
280                 accumulate_program_time (counter->op, elapsed);
281
282                 current_context.counter_head = counter->next;
283                 if (current_context.counter_head == NULL)
284                         current_context.counter_tail = NULL;
285
286                 glDeleteQueries (1, &counter->id);
287
288                 free (counter);
289                 counter = current_context.counter_head;
290         }
291
292         if (frames % 60 == 0) {
293                 double fps;
294
295                 fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
296                                          (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
297
298                 printf("FPS: %.3f\n", fps);
299
300                 print_program_metrics ();
301         }
302 }