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