mesa/version: only enable GL4.1 with correct limits.
[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 "util/bitset.h"
46 #include "util/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 inline void
57 init_groups(struct gl_context *ctx)
58 {
59 if (unlikely(!ctx->PerfMonitor.Groups))
60 ctx->Driver.InitPerfMonitorGroups(ctx);
61 }
62
63 static struct gl_perf_monitor_object *
64 new_performance_monitor(struct gl_context *ctx, GLuint index)
65 {
66 unsigned i;
67 struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx);
68
69 if (m == NULL)
70 return NULL;
71
72 m->Name = index;
73
74 m->Active = false;
75
76 m->ActiveGroups =
77 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
78
79 m->ActiveCounters =
80 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
81
82 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
83 goto fail;
84
85 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
86 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
87
88 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
89 BITSET_WORDS(g->NumCounters));
90 if (m->ActiveCounters[i] == NULL)
91 goto fail;
92 }
93
94 return m;
95
96 fail:
97 ralloc_free(m->ActiveGroups);
98 ralloc_free(m->ActiveCounters);
99 ctx->Driver.DeletePerfMonitor(ctx, m);
100 return NULL;
101 }
102
103 static void
104 free_performance_monitor(GLuint key, void *data, void *user)
105 {
106 struct gl_perf_monitor_object *m = data;
107 struct gl_context *ctx = user;
108
109 ralloc_free(m->ActiveGroups);
110 ralloc_free(m->ActiveCounters);
111 ctx->Driver.DeletePerfMonitor(ctx, m);
112 }
113
114 void
115 _mesa_free_performance_monitors(struct gl_context *ctx)
116 {
117 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
118 free_performance_monitor, ctx);
119 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
120 }
121
122 static inline struct gl_perf_monitor_object *
123 lookup_monitor(struct gl_context *ctx, GLuint id)
124 {
125 return (struct gl_perf_monitor_object *)
126 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
127 }
128
129 static inline const struct gl_perf_monitor_group *
130 get_group(const struct gl_context *ctx, GLuint id)
131 {
132 if (id >= ctx->PerfMonitor.NumGroups)
133 return NULL;
134
135 return &ctx->PerfMonitor.Groups[id];
136 }
137
138 static inline const struct gl_perf_monitor_counter *
139 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
140 {
141 if (id >= group_obj->NumCounters)
142 return NULL;
143
144 return &group_obj->Counters[id];
145 }
146
147 /*****************************************************************************/
148
149 void GLAPIENTRY
150 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
151 GLuint *groups)
152 {
153 GET_CURRENT_CONTEXT(ctx);
154 init_groups(ctx);
155
156 if (numGroups != NULL)
157 *numGroups = ctx->PerfMonitor.NumGroups;
158
159 if (groupsSize > 0 && groups != NULL) {
160 unsigned i;
161 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
162
163 /* We just use the index in the Groups array as the ID. */
164 for (i = 0; i < n; i++)
165 groups[i] = i;
166 }
167 }
168
169 void GLAPIENTRY
170 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
171 GLint *maxActiveCounters,
172 GLsizei countersSize, GLuint *counters)
173 {
174 GET_CURRENT_CONTEXT(ctx);
175 const struct gl_perf_monitor_group *group_obj;
176
177 init_groups(ctx);
178
179 group_obj = get_group(ctx, group);
180 if (group_obj == NULL) {
181 _mesa_error(ctx, GL_INVALID_VALUE,
182 "glGetPerfMonitorCountersAMD(invalid group)");
183 return;
184 }
185
186 if (maxActiveCounters != NULL)
187 *maxActiveCounters = group_obj->MaxActiveCounters;
188
189 if (numCounters != NULL)
190 *numCounters = group_obj->NumCounters;
191
192 if (counters != NULL) {
193 unsigned i;
194 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
195 for (i = 0; i < n; i++) {
196 /* We just use the index in the Counters array as the ID. */
197 counters[i] = i;
198 }
199 }
200 }
201
202 void GLAPIENTRY
203 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
204 GLsizei *length, GLchar *groupString)
205 {
206 GET_CURRENT_CONTEXT(ctx);
207 const struct gl_perf_monitor_group *group_obj;
208
209 init_groups(ctx);
210
211 group_obj = get_group(ctx, group);
212 if (group_obj == NULL) {
213 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
214 return;
215 }
216
217 if (bufSize == 0) {
218 /* Return the number of characters that would be required to hold the
219 * group string, excluding the null terminator.
220 */
221 if (length != NULL)
222 *length = strlen(group_obj->Name);
223 } else {
224 if (length != NULL)
225 *length = MIN2(strlen(group_obj->Name), bufSize);
226 if (groupString != NULL)
227 strncpy(groupString, group_obj->Name, bufSize);
228 }
229 }
230
231 void GLAPIENTRY
232 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
233 GLsizei bufSize, GLsizei *length,
234 GLchar *counterString)
235 {
236 GET_CURRENT_CONTEXT(ctx);
237
238 const struct gl_perf_monitor_group *group_obj;
239 const struct gl_perf_monitor_counter *counter_obj;
240
241 init_groups(ctx);
242
243 group_obj = get_group(ctx, group);
244
245 if (group_obj == NULL) {
246 _mesa_error(ctx, GL_INVALID_VALUE,
247 "glGetPerfMonitorCounterStringAMD(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 "glGetPerfMonitorCounterStringAMD(invalid counter)");
256 return;
257 }
258
259 if (bufSize == 0) {
260 /* Return the number of characters that would be required to hold the
261 * counter string, excluding the null terminator.
262 */
263 if (length != NULL)
264 *length = strlen(counter_obj->Name);
265 } else {
266 if (length != NULL)
267 *length = MIN2(strlen(counter_obj->Name), bufSize);
268 if (counterString != NULL)
269 strncpy(counterString, counter_obj->Name, bufSize);
270 }
271 }
272
273 void GLAPIENTRY
274 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
275 GLvoid *data)
276 {
277 GET_CURRENT_CONTEXT(ctx);
278
279 const struct gl_perf_monitor_group *group_obj;
280 const struct gl_perf_monitor_counter *counter_obj;
281
282 init_groups(ctx);
283
284 group_obj = get_group(ctx, group);
285
286 if (group_obj == NULL) {
287 _mesa_error(ctx, GL_INVALID_VALUE,
288 "glGetPerfMonitorCounterInfoAMD(invalid group)");
289 return;
290 }
291
292 counter_obj = get_counter(group_obj, counter);
293
294 if (counter_obj == NULL) {
295 _mesa_error(ctx, GL_INVALID_VALUE,
296 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
297 return;
298 }
299
300 switch (pname) {
301 case GL_COUNTER_TYPE_AMD:
302 *((GLenum *) data) = counter_obj->Type;
303 break;
304
305 case GL_COUNTER_RANGE_AMD:
306 switch (counter_obj->Type) {
307 case GL_FLOAT:
308 case GL_PERCENTAGE_AMD: {
309 float *f_data = data;
310 f_data[0] = counter_obj->Minimum.f;
311 f_data[1] = counter_obj->Maximum.f;
312 break;
313 }
314 case GL_UNSIGNED_INT: {
315 uint32_t *u32_data = data;
316 u32_data[0] = counter_obj->Minimum.u32;
317 u32_data[1] = counter_obj->Maximum.u32;
318 break;
319 }
320 case GL_UNSIGNED_INT64_AMD: {
321 uint64_t *u64_data = data;
322 u64_data[0] = counter_obj->Minimum.u64;
323 u64_data[1] = counter_obj->Maximum.u64;
324 break;
325 }
326 default:
327 assert(!"Should not get here: invalid counter type");
328 }
329 break;
330
331 default:
332 _mesa_error(ctx, GL_INVALID_ENUM,
333 "glGetPerfMonitorCounterInfoAMD(pname)");
334 return;
335 }
336 }
337
338 void GLAPIENTRY
339 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
340 {
341 GLuint first;
342 GET_CURRENT_CONTEXT(ctx);
343
344 if (MESA_VERBOSE & VERBOSE_API)
345 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
346
347 init_groups(ctx);
348
349 if (n < 0) {
350 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
351 return;
352 }
353
354 if (monitors == NULL)
355 return;
356
357 /* We don't actually need them to be contiguous, but this is what
358 * the rest of Mesa does, so we may as well.
359 */
360 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
361 if (first) {
362 GLsizei i;
363 for (i = 0; i < n; i++) {
364 struct gl_perf_monitor_object *m =
365 new_performance_monitor(ctx, first + i);
366 if (!m) {
367 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
368 return;
369 }
370 monitors[i] = first + i;
371 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
372 }
373 } else {
374 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
375 return;
376 }
377 }
378
379 void GLAPIENTRY
380 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
381 {
382 GLint i;
383 GET_CURRENT_CONTEXT(ctx);
384
385 if (MESA_VERBOSE & VERBOSE_API)
386 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
387
388 if (n < 0) {
389 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
390 return;
391 }
392
393 if (monitors == NULL)
394 return;
395
396 for (i = 0; i < n; i++) {
397 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
398
399 if (m) {
400 /* Give the driver a chance to stop the monitor if it's active. */
401 if (m->Active) {
402 ctx->Driver.ResetPerfMonitor(ctx, m);
403 m->Ended = false;
404 }
405
406 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
407 ralloc_free(m->ActiveGroups);
408 ralloc_free(m->ActiveCounters);
409 ctx->Driver.DeletePerfMonitor(ctx, m);
410 } else {
411 /* "INVALID_VALUE error will be generated if any of the monitor IDs
412 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
413 * reference a valid generated monitor ID."
414 */
415 _mesa_error(ctx, GL_INVALID_VALUE,
416 "glDeletePerfMonitorsAMD(invalid monitor)");
417 }
418 }
419 }
420
421 void GLAPIENTRY
422 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
423 GLuint group, GLint numCounters,
424 GLuint *counterList)
425 {
426 GET_CURRENT_CONTEXT(ctx);
427 int i;
428 struct gl_perf_monitor_object *m;
429 const struct gl_perf_monitor_group *group_obj;
430
431 m = lookup_monitor(ctx, monitor);
432
433 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
434 * SelectPerfMonitorCountersAMD does not reference a monitor created by
435 * GenPerfMonitorsAMD."
436 */
437 if (m == NULL) {
438 _mesa_error(ctx, GL_INVALID_VALUE,
439 "glSelectPerfMonitorCountersAMD(invalid monitor)");
440 return;
441 }
442
443 group_obj = get_group(ctx, group);
444
445 /* "INVALID_VALUE error will be generated if the <group> parameter to
446 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
447 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
448 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
449 */
450 if (group_obj == NULL) {
451 _mesa_error(ctx, GL_INVALID_VALUE,
452 "glSelectPerfMonitorCountersAMD(invalid group)");
453 return;
454 }
455
456 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
457 * SelectPerfMonitorCountersAMD is less than 0."
458 */
459 if (numCounters < 0) {
460 _mesa_error(ctx, GL_INVALID_VALUE,
461 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
462 return;
463 }
464
465 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
466 * results for that monitor become invalidated and the result queries
467 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
468 */
469 ctx->Driver.ResetPerfMonitor(ctx, m);
470
471 /* Sanity check the counter ID list. */
472 for (i = 0; i < numCounters; i++) {
473 if (counterList[i] >= group_obj->NumCounters) {
474 _mesa_error(ctx, GL_INVALID_VALUE,
475 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
476 return;
477 }
478 }
479
480 if (enable) {
481 /* Enable the counters */
482 for (i = 0; i < numCounters; i++) {
483 if (!BITSET_TEST(m->ActiveCounters[group], counterList[i])) {
484 ++m->ActiveGroups[group];
485 BITSET_SET(m->ActiveCounters[group], counterList[i]);
486 }
487 }
488 } else {
489 /* Disable the counters */
490 for (i = 0; i < numCounters; i++) {
491 if (BITSET_TEST(m->ActiveCounters[group], counterList[i])) {
492 --m->ActiveGroups[group];
493 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
494 }
495 }
496 }
497 }
498
499 void GLAPIENTRY
500 _mesa_BeginPerfMonitorAMD(GLuint monitor)
501 {
502 GET_CURRENT_CONTEXT(ctx);
503
504 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
505
506 if (m == NULL) {
507 _mesa_error(ctx, GL_INVALID_VALUE,
508 "glBeginPerfMonitorAMD(invalid monitor)");
509 return;
510 }
511
512 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
513 * called when a performance monitor is already active."
514 */
515 if (m->Active) {
516 _mesa_error(ctx, GL_INVALID_OPERATION,
517 "glBeginPerfMonitor(already active)");
518 return;
519 }
520
521 /* The driver is free to return false if it can't begin monitoring for
522 * any reason. This translates into an INVALID_OPERATION error.
523 */
524 if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
525 m->Active = true;
526 m->Ended = false;
527 } else {
528 _mesa_error(ctx, GL_INVALID_OPERATION,
529 "glBeginPerfMonitor(driver unable to begin monitoring)");
530 }
531 }
532
533 void GLAPIENTRY
534 _mesa_EndPerfMonitorAMD(GLuint monitor)
535 {
536 GET_CURRENT_CONTEXT(ctx);
537
538 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
539
540 if (m == NULL) {
541 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
542 return;
543 }
544
545 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
546 * when a performance monitor is not currently started."
547 */
548 if (!m->Active) {
549 _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfMonitor(not active)");
550 return;
551 }
552
553 ctx->Driver.EndPerfMonitor(ctx, m);
554
555 m->Active = false;
556 m->Ended = true;
557 }
558
559 /**
560 * Return the number of bytes needed to store a monitor's result.
561 */
562 static unsigned
563 perf_monitor_result_size(const struct gl_context *ctx,
564 const struct gl_perf_monitor_object *m)
565 {
566 unsigned group, counter;
567 unsigned size = 0;
568
569 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
570 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
571
572 BITSET_FOREACH_SET(counter, m->ActiveCounters[group], g->NumCounters) {
573 const struct gl_perf_monitor_counter *c = &g->Counters[counter];
574
575 size += sizeof(uint32_t); /* Group ID */
576 size += sizeof(uint32_t); /* Counter ID */
577 size += _mesa_perf_monitor_counter_size(c);
578 }
579 }
580 return size;
581 }
582
583 void GLAPIENTRY
584 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
585 GLsizei dataSize, GLuint *data,
586 GLint *bytesWritten)
587 {
588 GET_CURRENT_CONTEXT(ctx);
589
590 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
591 bool result_available;
592
593 if (m == NULL) {
594 _mesa_error(ctx, GL_INVALID_VALUE,
595 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
596 return;
597 }
598
599 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
600 if (data == NULL) {
601 _mesa_error(ctx, GL_INVALID_OPERATION,
602 "glGetPerfMonitorCounterDataAMD(data == NULL)");
603 return;
604 }
605
606 /* We need at least enough room for a single value. */
607 if (dataSize < sizeof(GLuint)) {
608 if (bytesWritten != NULL)
609 *bytesWritten = 0;
610 return;
611 }
612
613 /* If the monitor has never ended, there is no result. */
614 result_available = m->Ended &&
615 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
616
617 /* AMD appears to return 0 for all queries unless a result is available. */
618 if (!result_available) {
619 *data = 0;
620 if (bytesWritten != NULL)
621 *bytesWritten = sizeof(GLuint);
622 return;
623 }
624
625 switch (pname) {
626 case GL_PERFMON_RESULT_AVAILABLE_AMD:
627 *data = 1;
628 if (bytesWritten != NULL)
629 *bytesWritten = sizeof(GLuint);
630 break;
631 case GL_PERFMON_RESULT_SIZE_AMD:
632 *data = perf_monitor_result_size(ctx, m);
633 if (bytesWritten != NULL)
634 *bytesWritten = sizeof(GLuint);
635 break;
636 case GL_PERFMON_RESULT_AMD:
637 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
638 break;
639 default:
640 _mesa_error(ctx, GL_INVALID_ENUM,
641 "glGetPerfMonitorCounterDataAMD(pname)");
642 }
643 }
644
645 /**
646 * Returns how many bytes a counter's value takes up.
647 */
648 unsigned
649 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
650 {
651 switch (c->Type) {
652 case GL_FLOAT:
653 case GL_PERCENTAGE_AMD:
654 return sizeof(GLfloat);
655 case GL_UNSIGNED_INT:
656 return sizeof(GLuint);
657 case GL_UNSIGNED_INT64_AMD:
658 return sizeof(uint64_t);
659 default:
660 assert(!"Should not get here: invalid counter type");
661 return 0;
662 }
663 }