1 /* Copyright © 2013, Intel Corporation
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
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
29 #include "fips-dispatch-gl.h"
33 typedef struct counter
41 typedef struct op_metrics
43 /* This happens to also be the index into the
44 * ctx->op_metrics array currently
50 typedef struct context
54 counter_t *counter_head;
55 counter_t *counter_tail;
57 unsigned num_op_metrics;
58 op_metrics_t *op_metrics;
61 /* FIXME: Need a map from integers to context objects and track the
62 * current context with glXMakeContextCurrent, eglMakeCurrent, etc. */
64 context_t current_context;
70 metrics_op_string (metrics_op_t op)
72 if (op >= METRICS_OP_SHADER)
73 return "Shader program";
77 case METRICS_OP_ACCUM:
79 case METRICS_OP_BUFFER_DATA:
80 return "glBufferData(+)";
81 case METRICS_OP_BUFFER_SUB_DATA:
82 return "glCopyBufferSubData*";
83 case METRICS_OP_BITMAP:
85 case METRICS_OP_BLIT_FRAMEBUFFER:
86 return "glBlitFramebuffer*";
87 case METRICS_OP_CLEAR:
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*(+)";
106 fprintf (stderr, "Internal error: "
107 "Unknown metrics op value: %d\n", op);
115 metrics_counter_start (void)
119 counter = malloc (sizeof(counter_t));
120 if (counter == NULL) {
121 fprintf (stderr, "Out of memory\n");
125 glGenQueries (1, &counter->id);
127 counter->op = current_context.op;
128 counter->next = NULL;
130 if (current_context.counter_tail) {
131 current_context.counter_tail->next = counter;
132 current_context.counter_tail = counter;
134 current_context.counter_tail = counter;
135 current_context.counter_head = counter;
138 glBeginQuery (GL_TIME_ELAPSED, counter->id);
142 metrics_counter_stop (void)
144 glEndQuery (GL_TIME_ELAPSED);
148 metrics_set_current_op (metrics_op_t op)
150 current_context.op = op;
154 accumulate_program_time (metrics_op_t op, unsigned time_ns)
156 context_t *ctx = ¤t_context;
159 if (op >= ctx->num_op_metrics) {
160 ctx->op_metrics = realloc (ctx->op_metrics,
161 (op + 1) * sizeof (op_metrics_t));
162 for (i = ctx->num_op_metrics; i < op + 1; i++) {
163 ctx->op_metrics[i].op = i;
164 ctx->op_metrics[i].time_ns = 0.0;
167 ctx->num_op_metrics = op + 1;
170 ctx->op_metrics[op].time_ns += time_ns;
174 time_compare(const void *in_a, const void *in_b, void *arg)
176 int a = *(const int *)in_a;
177 int b = *(const int *)in_b;
178 struct op_metrics *metrics = arg;
180 if (metrics[a].time_ns < metrics[b].time_ns)
182 if (metrics[a].time_ns > metrics[b].time_ns)
188 print_program_metrics (void)
190 context_t *ctx = ¤t_context;
192 int *sorted; /* Sorted indices into the ctx->op_metrics */
195 /* Make a sorted list of the operations by time used, and figure
196 * out the total so we can print percentages.
198 sorted = calloc(ctx->num_op_metrics, sizeof(*sorted));
199 for (i = 0; i < ctx->num_op_metrics; i++) {
201 total += ctx->op_metrics[i].time_ns;
203 qsort_r(sorted, ctx->num_op_metrics, sizeof(*sorted),
204 time_compare, ctx->op_metrics);
206 for (i = 0; i < ctx->num_op_metrics; i++) {
207 const char *op_string;
208 op_metrics_t *metric =&ctx->op_metrics[sorted[i]];
210 /* Since we sparsely fill the array based on program
211 * id, many "programs" have no time.
213 if (metric->time_ns == 0.0)
216 op_string = metrics_op_string (metric->op);
218 printf ("%s", op_string);
219 if (metric->op >= METRICS_OP_SHADER) {
220 printf (" %d:", metric->op - METRICS_OP_SHADER);
223 for (j = strlen (op_string); j < 20; j++)
226 printf ("\t%7.2f ms (% 2.1f%%)\n",
227 metric->time_ns / 1e6,
228 metric->time_ns / total * 100);
232 /* Called at program exit */
237 printf ("fips: terminating\n");
242 metrics_end_frame (void)
244 static int initialized = 0;
245 static struct timeval tv_start, tv_now;
248 gettimeofday (&tv_start, NULL);
249 atexit (metrics_exit);
250 if (getenv ("FIPS_VERBOSE"))
256 printf ("fips: frame %d complete\n", frames);
259 gettimeofday (&tv_now, NULL);
261 /* Consume all counters that are ready. */
262 counter_t *counter = current_context.counter_head;
265 GLuint available, elapsed;
267 glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT_AVAILABLE,
272 glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT, &elapsed);
274 accumulate_program_time (counter->op, elapsed);
276 current_context.counter_head = counter->next;
277 if (current_context.counter_head == NULL)
278 current_context.counter_tail = NULL;
280 glDeleteQueries (1, &counter->id);
283 counter = current_context.counter_head;
286 if (frames % 60 == 0) {
289 fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
290 (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
292 printf("FPS: %.3f\n", fps);
294 print_program_metrics ();