mesa: Fix misplaced includes of "main/uniforms.h".
[mesa.git] / src / mesa / main / performance_monitor.c
1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * \file performance_monitor.c
26 * Core Mesa support for the AMD_performance_monitor extension.
27 *
28 * In order to implement this extension, start by defining two enums:
29 * one for Groups, and one for Counters. These will be used as indexes into
30 * arrays, so they should start at 0 and increment from there.
31 *
32 * Counter IDs need to be globally unique. That is, you can't have counter 7
33 * in group A and counter 7 in group B. A global enum of all available
34 * counters is a convenient way to guarantee this.
35 */
36
37 #include <stdbool.h>
38 #include "glheader.h"
39 #include "context.h"
40 #include "enums.h"
41 #include "hash.h"
42 #include "macros.h"
43 #include "mtypes.h"
44 #include "performance_monitor.h"
45 #include "bitset.h"
46 #include "ralloc.h"
47
48 void
49 _mesa_init_performance_monitors(struct gl_context *ctx)
50 {
51 ctx->PerfMonitor.Monitors = _mesa_NewHashTable();
52 ctx->PerfMonitor.NumGroups = 0;
53 ctx->PerfMonitor.Groups = NULL;
54 }
55
56 static struct gl_perf_monitor_object *
57 new_performance_monitor(struct gl_context *ctx, GLuint index)
58 {
59 unsigned i;
60 struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx);
61
62 if (m == NULL)
63 return NULL;
64
65 m->ActiveGroups =
66 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
67
68 m->ActiveCounters =
69 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
70
71 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
72 goto fail;
73
74 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
75 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
76
77 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
78 BITSET_WORDS(g->NumCounters));
79 if (m->ActiveCounters[i] == NULL)
80 goto fail;
81 }
82
83 return m;
84
85 fail:
86 ralloc_free(m->ActiveGroups);
87 ralloc_free(m->ActiveCounters);
88 ctx->Driver.DeletePerfMonitor(ctx, m);
89 return NULL;
90 }
91
92 static inline struct gl_perf_monitor_object *
93 lookup_monitor(struct gl_context *ctx, GLuint id)
94 {
95 return (struct gl_perf_monitor_object *)
96 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
97 }
98
99 static inline const struct gl_perf_monitor_group *
100 get_group(const struct gl_context *ctx, GLuint id)
101 {
102 if (id >= ctx->PerfMonitor.NumGroups)
103 return NULL;
104
105 return &ctx->PerfMonitor.Groups[id];
106 }
107
108 static inline const struct gl_perf_monitor_counter *
109 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
110 {
111 if (id >= group_obj->NumCounters)
112 return NULL;
113
114 return &group_obj->Counters[id];
115 }
116
117 /*****************************************************************************/
118
119 void GLAPIENTRY
120 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
121 GLuint *groups)
122 {
123 GET_CURRENT_CONTEXT(ctx);
124
125 if (numGroups != NULL)
126 *numGroups = ctx->PerfMonitor.NumGroups;
127
128 if (groupsSize > 0 && groups != NULL) {
129 unsigned i;
130 unsigned n = MIN2(groupsSize, ctx->PerfMonitor.NumGroups);
131
132 /* We just use the index in the Groups array as the ID. */
133 for (i = 0; i < n; i++)
134 groups[i] = i;
135 }
136 }
137
138 void GLAPIENTRY
139 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
140 GLint *maxActiveCounters,
141 GLsizei countersSize, GLuint *counters)
142 {
143 GET_CURRENT_CONTEXT(ctx);
144 const struct gl_perf_monitor_group *group_obj = get_group(ctx, group);
145 if (group_obj == NULL) {
146 _mesa_error(ctx, GL_INVALID_VALUE,
147 "glGetPerfMonitorCountersAMD(invalid group)");
148 return;
149 }
150
151 if (maxActiveCounters != NULL)
152 *maxActiveCounters = group_obj->MaxActiveCounters;
153
154 if (numCounters != NULL)
155 *numCounters = group_obj->NumCounters;
156
157 if (counters != NULL) {
158 unsigned i;
159 unsigned n = MIN2(group_obj->NumCounters, countersSize);
160 for (i = 0; i < n; i++) {
161 /* We just use the index in the Counters array as the ID. */
162 counters[i] = i;
163 }
164 }
165 }
166
167 void GLAPIENTRY
168 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
169 GLsizei *length, GLchar *groupString)
170 {
171 GET_CURRENT_CONTEXT(ctx);
172
173 const struct gl_perf_monitor_group *group_obj = get_group(ctx, group);
174
175 if (group_obj == NULL) {
176 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
177 return;
178 }
179
180 if (bufSize == 0) {
181 /* Return the number of characters that would be required to hold the
182 * group string, excluding the null terminator.
183 */
184 if (length != NULL)
185 *length = strlen(group_obj->Name);
186 } else {
187 if (length != NULL)
188 *length = MIN2(strlen(group_obj->Name), bufSize);
189 if (groupString != NULL)
190 strncpy(groupString, group_obj->Name, bufSize);
191 }
192 }
193
194 void GLAPIENTRY
195 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
196 GLsizei bufSize, GLsizei *length,
197 GLchar *counterString)
198 {
199 GET_CURRENT_CONTEXT(ctx);
200
201 const struct gl_perf_monitor_group *group_obj;
202 const struct gl_perf_monitor_counter *counter_obj;
203
204 group_obj = get_group(ctx, group);
205
206 if (group_obj == NULL) {
207 _mesa_error(ctx, GL_INVALID_VALUE,
208 "glGetPerfMonitorCounterStringAMD(invalid group)");
209 return;
210 }
211
212 counter_obj = get_counter(group_obj, counter);
213
214 if (counter_obj == NULL) {
215 _mesa_error(ctx, GL_INVALID_VALUE,
216 "glGetPerfMonitorCounterStringAMD(invalid counter)");
217 return;
218 }
219
220 if (bufSize == 0) {
221 /* Return the number of characters that would be required to hold the
222 * counter string, excluding the null terminator.
223 */
224 if (length != NULL)
225 *length = strlen(counter_obj->Name);
226 } else {
227 if (length != NULL)
228 *length = MIN2(strlen(counter_obj->Name), bufSize);
229 if (counterString != NULL)
230 strncpy(counterString, counter_obj->Name, bufSize);
231 }
232 }
233
234 void GLAPIENTRY
235 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
236 GLvoid *data)
237 {
238 GET_CURRENT_CONTEXT(ctx);
239
240 const struct gl_perf_monitor_group *group_obj;
241 const struct gl_perf_monitor_counter *counter_obj;
242
243 group_obj = get_group(ctx, group);
244
245 if (group_obj == NULL) {
246 _mesa_error(ctx, GL_INVALID_VALUE,
247 "glGetPerfMonitorCounterInfoAMD(invalid group)");
248 return;
249 }
250
251 counter_obj = get_counter(group_obj, counter);
252
253 if (counter_obj == NULL) {
254 _mesa_error(ctx, GL_INVALID_VALUE,
255 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
256 return;
257 }
258
259 switch (pname) {
260 case GL_COUNTER_TYPE_AMD:
261 *((GLenum *) data) = counter_obj->Type;
262 break;
263
264 case GL_COUNTER_RANGE_AMD:
265 switch (counter_obj->Type) {
266 case GL_FLOAT:
267 case GL_PERCENTAGE_AMD: {
268 float *f_data = data;
269 f_data[0] = counter_obj->Minimum.f;
270 f_data[1] = counter_obj->Maximum.f;
271 break;
272 }
273 case GL_UNSIGNED_INT: {
274 uint32_t *u32_data = data;
275 u32_data[0] = counter_obj->Minimum.u32;
276 u32_data[1] = counter_obj->Maximum.u32;
277 break;
278 }
279 case GL_UNSIGNED_INT64_AMD: {
280 uint64_t *u64_data = data;
281 u64_data[0] = counter_obj->Minimum.u64;
282 u64_data[1] = counter_obj->Maximum.u64;
283 break;
284 }
285 default:
286 assert(!"Should not get here: invalid counter type");
287 }
288 break;
289
290 default:
291 _mesa_error(ctx, GL_INVALID_ENUM,
292 "glGetPerfMonitorCounterInfoAMD(pname)");
293 return;
294 }
295 }
296
297 void GLAPIENTRY
298 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
299 {
300 GLuint first;
301 GET_CURRENT_CONTEXT(ctx);
302
303 if (MESA_VERBOSE & VERBOSE_API)
304 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
305
306 if (n < 0) {
307 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
308 return;
309 }
310
311 if (monitors == NULL)
312 return;
313
314 /* We don't actually need them to be contiguous, but this is what
315 * the rest of Mesa does, so we may as well.
316 */
317 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
318 if (first) {
319 GLsizei i;
320 for (i = 0; i < n; i++) {
321 struct gl_perf_monitor_object *m =
322 new_performance_monitor(ctx, first + i);
323 if (!m) {
324 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
325 return;
326 }
327 monitors[i] = first + i;
328 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
329 }
330 } else {
331 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
332 return;
333 }
334 }
335
336 void GLAPIENTRY
337 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
338 {
339 GLint i;
340 GET_CURRENT_CONTEXT(ctx);
341
342 if (MESA_VERBOSE & VERBOSE_API)
343 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
344
345 if (n < 0) {
346 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
347 return;
348 }
349
350 if (monitors == NULL)
351 return;
352
353 for (i = 0; i < n; i++) {
354 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
355
356 if (m) {
357 /* Give the driver a chance to stop the monitor if it's active. */
358 if (m->Active)
359 ctx->Driver.ResetPerfMonitor(ctx, m);
360
361 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
362 ralloc_free(m->ActiveGroups);
363 ralloc_free(m->ActiveCounters);
364 ctx->Driver.DeletePerfMonitor(ctx, m);
365 } else {
366 /* "INVALID_VALUE error will be generated if any of the monitor IDs
367 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
368 * reference a valid generated monitor ID."
369 */
370 _mesa_error(ctx, GL_INVALID_VALUE,
371 "glDeletePerfMonitorsAMD(invalid monitor)");
372 }
373 }
374 }
375
376 void GLAPIENTRY
377 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
378 GLuint group, GLint numCounters,
379 GLuint *counterList)
380 {
381 GET_CURRENT_CONTEXT(ctx);
382 unsigned i;
383 struct gl_perf_monitor_object *m;
384 const struct gl_perf_monitor_group *group_obj;
385
386 m = lookup_monitor(ctx, monitor);
387
388 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
389 * SelectPerfMonitorCountersAMD does not reference a monitor created by
390 * GenPerfMonitorsAMD."
391 */
392 if (m == NULL) {
393 _mesa_error(ctx, GL_INVALID_VALUE,
394 "glSelectPerfMonitorCountersAMD(invalid monitor)");
395 return;
396 }
397
398 group_obj = get_group(ctx, group);
399
400 /* "INVALID_VALUE error will be generated if the <group> parameter to
401 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
402 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
403 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
404 */
405 if (group_obj == NULL) {
406 _mesa_error(ctx, GL_INVALID_VALUE,
407 "glSelectPerfMonitorCountersAMD(invalid group)");
408 return;
409 }
410
411 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
412 * SelectPerfMonitorCountersAMD is less than 0."
413 */
414 if (numCounters < 0) {
415 _mesa_error(ctx, GL_INVALID_VALUE,
416 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
417 return;
418 }
419
420 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
421 * results for that monitor become invalidated and the result queries
422 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
423 */
424 ctx->Driver.ResetPerfMonitor(ctx, m);
425
426 /* Sanity check the counter ID list. */
427 for (i = 0; i < numCounters; i++) {
428 if (counterList[i] >= group_obj->NumCounters) {
429 _mesa_error(ctx, GL_INVALID_VALUE,
430 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
431 return;
432 }
433 }
434
435 if (enable) {
436 /* Enable the counters */
437 for (i = 0; i < numCounters; i++) {
438 ++m->ActiveGroups[group];
439 BITSET_SET(m->ActiveCounters[group], counterList[i]);
440 }
441 } else {
442 /* Disable the counters */
443 for (i = 0; i < numCounters; i++) {
444 --m->ActiveGroups[group];
445 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
446 }
447 }
448 }
449
450 void GLAPIENTRY
451 _mesa_BeginPerfMonitorAMD(GLuint monitor)
452 {
453 GET_CURRENT_CONTEXT(ctx);
454
455 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
456
457 if (m == NULL) {
458 _mesa_error(ctx, GL_INVALID_VALUE,
459 "glBeginPerfMonitorAMD(invalid monitor)");
460 return;
461 }
462
463 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
464 * called when a performance monitor is already active."
465 */
466 if (m->Active) {
467 _mesa_error(ctx, GL_INVALID_OPERATION,
468 "glBeginPerfMonitor(already active)");
469 return;
470 }
471
472 /* The driver is free to return false if it can't begin monitoring for
473 * any reason. This translates into an INVALID_OPERATION error.
474 */
475 if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
476 m->Active = true;
477 } else {
478 _mesa_error(ctx, GL_INVALID_OPERATION,
479 "glBeginPerfMonitor(driver unable to begin monitoring)");
480 }
481 }
482
483 void GLAPIENTRY
484 _mesa_EndPerfMonitorAMD(GLuint monitor)
485 {
486 GET_CURRENT_CONTEXT(ctx);
487
488 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
489
490 if (m == NULL) {
491 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
492 return;
493 }
494
495 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
496 * when a performance monitor is not currently started."
497 */
498 if (!m->Active) {
499 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)");
500 return;
501 }
502
503 ctx->Driver.EndPerfMonitor(ctx, m);
504
505 m->Active = false;
506 }
507
508 /**
509 * Return the number of bytes needed to store a monitor's result.
510 */
511 static unsigned
512 perf_monitor_result_size(const struct gl_context *ctx,
513 const struct gl_perf_monitor_object *m)
514 {
515 unsigned group, counter;
516 unsigned size = 0;
517
518 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
519 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
520 for (counter = 0; counter < g->NumCounters; counter++) {
521 const struct gl_perf_monitor_counter *c = &g->Counters[counter];
522
523 if (!BITSET_TEST(m->ActiveCounters[group], counter))
524 continue;
525
526 size += sizeof(uint32_t); /* Group ID */
527 size += sizeof(uint32_t); /* Counter ID */
528 size += _mesa_perf_monitor_counter_size(c);
529 }
530 }
531 return size;
532 }
533
534 void GLAPIENTRY
535 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
536 GLsizei dataSize, GLuint *data,
537 GLint *bytesWritten)
538 {
539 GET_CURRENT_CONTEXT(ctx);
540
541 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
542
543 if (m == NULL) {
544 _mesa_error(ctx, GL_INVALID_VALUE,
545 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
546 return;
547 }
548
549 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
550 if (data == NULL) {
551 _mesa_error(ctx, GL_INVALID_OPERATION,
552 "glGetPerfMonitorCounterDataAMD(data == NULL)");
553 return;
554 }
555
556 /* We need at least enough room for a single value. */
557 if (dataSize < sizeof(GLuint)) {
558 if (bytesWritten != NULL)
559 *bytesWritten = 0;
560 return;
561 }
562
563 /* AMD appears to return 0 for all queries unless a result is available. */
564 if (!ctx->Driver.IsPerfMonitorResultAvailable(ctx, m)) {
565 *data = 0;
566 if (bytesWritten != NULL)
567 *bytesWritten = sizeof(GLuint);
568 return;
569 }
570
571 switch (pname) {
572 case GL_PERFMON_RESULT_AVAILABLE_AMD:
573 *data = 1;
574 if (bytesWritten != NULL)
575 *bytesWritten = sizeof(GLuint);
576 break;
577 case GL_PERFMON_RESULT_SIZE_AMD:
578 *data = perf_monitor_result_size(ctx, m);
579 if (bytesWritten != NULL)
580 *bytesWritten = sizeof(GLuint);
581 break;
582 case GL_PERFMON_RESULT_AMD:
583 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
584 break;
585 default:
586 _mesa_error(ctx, GL_INVALID_ENUM,
587 "glGetPerfMonitorCounterDataAMD(pname)");
588 }
589 }
590
591 /**
592 * Returns how many bytes a counter's value takes up.
593 */
594 unsigned
595 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
596 {
597 switch (c->Type) {
598 case GL_FLOAT:
599 case GL_PERCENTAGE_AMD:
600 return sizeof(GLfloat);
601 case GL_UNSIGNED_INT:
602 return sizeof(GLuint);
603 case GL_UNSIGNED_INT64_AMD:
604 return sizeof(uint64_t);
605 default:
606 assert(!"Should not get here: invalid counter type");
607 return 0;
608 }
609 }