util: Rename PURE to ATTRIBUTE_PURE.
[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 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->Name = index;
66
67 m->Active = false;
68
69 m->ActiveGroups =
70 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
71
72 m->ActiveCounters =
73 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
74
75 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
76 goto fail;
77
78 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
79 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
80
81 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
82 BITSET_WORDS(g->NumCounters));
83 if (m->ActiveCounters[i] == NULL)
84 goto fail;
85 }
86
87 return m;
88
89 fail:
90 ralloc_free(m->ActiveGroups);
91 ralloc_free(m->ActiveCounters);
92 ctx->Driver.DeletePerfMonitor(ctx, m);
93 return NULL;
94 }
95
96 static void
97 free_performance_monitor(GLuint key, void *data, void *user)
98 {
99 struct gl_perf_monitor_object *m = data;
100 struct gl_context *ctx = user;
101
102 ralloc_free(m->ActiveGroups);
103 ralloc_free(m->ActiveCounters);
104 ctx->Driver.DeletePerfMonitor(ctx, m);
105 }
106
107 void
108 _mesa_free_performance_monitors(struct gl_context *ctx)
109 {
110 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
111 free_performance_monitor, ctx);
112 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
113 }
114
115 static inline struct gl_perf_monitor_object *
116 lookup_monitor(struct gl_context *ctx, GLuint id)
117 {
118 return (struct gl_perf_monitor_object *)
119 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
120 }
121
122 static inline const struct gl_perf_monitor_group *
123 get_group(const struct gl_context *ctx, GLuint id)
124 {
125 if (id >= ctx->PerfMonitor.NumGroups)
126 return NULL;
127
128 return &ctx->PerfMonitor.Groups[id];
129 }
130
131 static inline const struct gl_perf_monitor_counter *
132 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
133 {
134 if (id >= group_obj->NumCounters)
135 return NULL;
136
137 return &group_obj->Counters[id];
138 }
139
140 /* For INTEL_performance_query, query id 0 is reserved to be invalid. We use
141 * index to Groups array + 1 as the query id. Same applies to counter id.
142 */
143 static inline GLuint
144 queryid_to_index(GLuint queryid)
145 {
146 return queryid - 1;
147 }
148
149 static inline GLuint
150 index_to_queryid(GLuint index)
151 {
152 return index + 1;
153 }
154
155 static inline bool
156 queryid_valid(const struct gl_context *ctx, GLuint queryid)
157 {
158 return get_group(ctx, queryid_to_index(queryid)) != NULL;
159 }
160
161 static inline GLuint
162 counterid_to_index(GLuint counterid)
163 {
164 return counterid - 1;
165 }
166
167 /*****************************************************************************/
168
169 void GLAPIENTRY
170 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
171 GLuint *groups)
172 {
173 GET_CURRENT_CONTEXT(ctx);
174
175 if (numGroups != NULL)
176 *numGroups = ctx->PerfMonitor.NumGroups;
177
178 if (groupsSize > 0 && groups != NULL) {
179 unsigned i;
180 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
181
182 /* We just use the index in the Groups array as the ID. */
183 for (i = 0; i < n; i++)
184 groups[i] = i;
185 }
186 }
187
188 void GLAPIENTRY
189 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
190 GLint *maxActiveCounters,
191 GLsizei countersSize, GLuint *counters)
192 {
193 GET_CURRENT_CONTEXT(ctx);
194 const struct gl_perf_monitor_group *group_obj = get_group(ctx, group);
195 if (group_obj == NULL) {
196 _mesa_error(ctx, GL_INVALID_VALUE,
197 "glGetPerfMonitorCountersAMD(invalid group)");
198 return;
199 }
200
201 if (maxActiveCounters != NULL)
202 *maxActiveCounters = group_obj->MaxActiveCounters;
203
204 if (numCounters != NULL)
205 *numCounters = group_obj->NumCounters;
206
207 if (counters != NULL) {
208 unsigned i;
209 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
210 for (i = 0; i < n; i++) {
211 /* We just use the index in the Counters array as the ID. */
212 counters[i] = i;
213 }
214 }
215 }
216
217 void GLAPIENTRY
218 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
219 GLsizei *length, GLchar *groupString)
220 {
221 GET_CURRENT_CONTEXT(ctx);
222
223 const struct gl_perf_monitor_group *group_obj = get_group(ctx, group);
224
225 if (group_obj == NULL) {
226 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
227 return;
228 }
229
230 if (bufSize == 0) {
231 /* Return the number of characters that would be required to hold the
232 * group string, excluding the null terminator.
233 */
234 if (length != NULL)
235 *length = strlen(group_obj->Name);
236 } else {
237 if (length != NULL)
238 *length = MIN2(strlen(group_obj->Name), bufSize);
239 if (groupString != NULL)
240 strncpy(groupString, group_obj->Name, bufSize);
241 }
242 }
243
244 void GLAPIENTRY
245 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
246 GLsizei bufSize, GLsizei *length,
247 GLchar *counterString)
248 {
249 GET_CURRENT_CONTEXT(ctx);
250
251 const struct gl_perf_monitor_group *group_obj;
252 const struct gl_perf_monitor_counter *counter_obj;
253
254 group_obj = get_group(ctx, group);
255
256 if (group_obj == NULL) {
257 _mesa_error(ctx, GL_INVALID_VALUE,
258 "glGetPerfMonitorCounterStringAMD(invalid group)");
259 return;
260 }
261
262 counter_obj = get_counter(group_obj, counter);
263
264 if (counter_obj == NULL) {
265 _mesa_error(ctx, GL_INVALID_VALUE,
266 "glGetPerfMonitorCounterStringAMD(invalid counter)");
267 return;
268 }
269
270 if (bufSize == 0) {
271 /* Return the number of characters that would be required to hold the
272 * counter string, excluding the null terminator.
273 */
274 if (length != NULL)
275 *length = strlen(counter_obj->Name);
276 } else {
277 if (length != NULL)
278 *length = MIN2(strlen(counter_obj->Name), bufSize);
279 if (counterString != NULL)
280 strncpy(counterString, counter_obj->Name, bufSize);
281 }
282 }
283
284 void GLAPIENTRY
285 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
286 GLvoid *data)
287 {
288 GET_CURRENT_CONTEXT(ctx);
289
290 const struct gl_perf_monitor_group *group_obj;
291 const struct gl_perf_monitor_counter *counter_obj;
292
293 group_obj = get_group(ctx, group);
294
295 if (group_obj == NULL) {
296 _mesa_error(ctx, GL_INVALID_VALUE,
297 "glGetPerfMonitorCounterInfoAMD(invalid group)");
298 return;
299 }
300
301 counter_obj = get_counter(group_obj, counter);
302
303 if (counter_obj == NULL) {
304 _mesa_error(ctx, GL_INVALID_VALUE,
305 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
306 return;
307 }
308
309 switch (pname) {
310 case GL_COUNTER_TYPE_AMD:
311 *((GLenum *) data) = counter_obj->Type;
312 break;
313
314 case GL_COUNTER_RANGE_AMD:
315 switch (counter_obj->Type) {
316 case GL_FLOAT:
317 case GL_PERCENTAGE_AMD: {
318 float *f_data = data;
319 f_data[0] = counter_obj->Minimum.f;
320 f_data[1] = counter_obj->Maximum.f;
321 break;
322 }
323 case GL_UNSIGNED_INT: {
324 uint32_t *u32_data = data;
325 u32_data[0] = counter_obj->Minimum.u32;
326 u32_data[1] = counter_obj->Maximum.u32;
327 break;
328 }
329 case GL_UNSIGNED_INT64_AMD: {
330 uint64_t *u64_data = data;
331 u64_data[0] = counter_obj->Minimum.u64;
332 u64_data[1] = counter_obj->Maximum.u64;
333 break;
334 }
335 default:
336 assert(!"Should not get here: invalid counter type");
337 }
338 break;
339
340 default:
341 _mesa_error(ctx, GL_INVALID_ENUM,
342 "glGetPerfMonitorCounterInfoAMD(pname)");
343 return;
344 }
345 }
346
347 void GLAPIENTRY
348 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
349 {
350 GLuint first;
351 GET_CURRENT_CONTEXT(ctx);
352
353 if (MESA_VERBOSE & VERBOSE_API)
354 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
355
356 if (n < 0) {
357 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
358 return;
359 }
360
361 if (monitors == NULL)
362 return;
363
364 /* We don't actually need them to be contiguous, but this is what
365 * the rest of Mesa does, so we may as well.
366 */
367 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
368 if (first) {
369 GLsizei i;
370 for (i = 0; i < n; i++) {
371 struct gl_perf_monitor_object *m =
372 new_performance_monitor(ctx, first + i);
373 if (!m) {
374 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
375 return;
376 }
377 monitors[i] = first + i;
378 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
379 }
380 } else {
381 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
382 return;
383 }
384 }
385
386 void GLAPIENTRY
387 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
388 {
389 GLint i;
390 GET_CURRENT_CONTEXT(ctx);
391
392 if (MESA_VERBOSE & VERBOSE_API)
393 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
394
395 if (n < 0) {
396 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
397 return;
398 }
399
400 if (monitors == NULL)
401 return;
402
403 for (i = 0; i < n; i++) {
404 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
405
406 if (m) {
407 /* Give the driver a chance to stop the monitor if it's active. */
408 if (m->Active) {
409 ctx->Driver.ResetPerfMonitor(ctx, m);
410 m->Ended = false;
411 }
412
413 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
414 ralloc_free(m->ActiveGroups);
415 ralloc_free(m->ActiveCounters);
416 ctx->Driver.DeletePerfMonitor(ctx, m);
417 } else {
418 /* "INVALID_VALUE error will be generated if any of the monitor IDs
419 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
420 * reference a valid generated monitor ID."
421 */
422 _mesa_error(ctx, GL_INVALID_VALUE,
423 "glDeletePerfMonitorsAMD(invalid monitor)");
424 }
425 }
426 }
427
428 void GLAPIENTRY
429 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
430 GLuint group, GLint numCounters,
431 GLuint *counterList)
432 {
433 GET_CURRENT_CONTEXT(ctx);
434 int i;
435 struct gl_perf_monitor_object *m;
436 const struct gl_perf_monitor_group *group_obj;
437
438 m = lookup_monitor(ctx, monitor);
439
440 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
441 * SelectPerfMonitorCountersAMD does not reference a monitor created by
442 * GenPerfMonitorsAMD."
443 */
444 if (m == NULL) {
445 _mesa_error(ctx, GL_INVALID_VALUE,
446 "glSelectPerfMonitorCountersAMD(invalid monitor)");
447 return;
448 }
449
450 group_obj = get_group(ctx, group);
451
452 /* "INVALID_VALUE error will be generated if the <group> parameter to
453 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
454 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
455 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
456 */
457 if (group_obj == NULL) {
458 _mesa_error(ctx, GL_INVALID_VALUE,
459 "glSelectPerfMonitorCountersAMD(invalid group)");
460 return;
461 }
462
463 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
464 * SelectPerfMonitorCountersAMD is less than 0."
465 */
466 if (numCounters < 0) {
467 _mesa_error(ctx, GL_INVALID_VALUE,
468 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
469 return;
470 }
471
472 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
473 * results for that monitor become invalidated and the result queries
474 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
475 */
476 ctx->Driver.ResetPerfMonitor(ctx, m);
477
478 /* Sanity check the counter ID list. */
479 for (i = 0; i < numCounters; i++) {
480 if (counterList[i] >= group_obj->NumCounters) {
481 _mesa_error(ctx, GL_INVALID_VALUE,
482 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
483 return;
484 }
485 }
486
487 if (enable) {
488 /* Enable the counters */
489 for (i = 0; i < numCounters; i++) {
490 ++m->ActiveGroups[group];
491 BITSET_SET(m->ActiveCounters[group], counterList[i]);
492 }
493 } else {
494 /* Disable the counters */
495 for (i = 0; i < numCounters; i++) {
496 --m->ActiveGroups[group];
497 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
498 }
499 }
500 }
501
502 void GLAPIENTRY
503 _mesa_BeginPerfMonitorAMD(GLuint monitor)
504 {
505 GET_CURRENT_CONTEXT(ctx);
506
507 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
508
509 if (m == NULL) {
510 _mesa_error(ctx, GL_INVALID_VALUE,
511 "glBeginPerfMonitorAMD(invalid monitor)");
512 return;
513 }
514
515 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
516 * called when a performance monitor is already active."
517 */
518 if (m->Active) {
519 _mesa_error(ctx, GL_INVALID_OPERATION,
520 "glBeginPerfMonitor(already active)");
521 return;
522 }
523
524 /* The driver is free to return false if it can't begin monitoring for
525 * any reason. This translates into an INVALID_OPERATION error.
526 */
527 if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
528 m->Active = true;
529 m->Ended = false;
530 } else {
531 _mesa_error(ctx, GL_INVALID_OPERATION,
532 "glBeginPerfMonitor(driver unable to begin monitoring)");
533 }
534 }
535
536 void GLAPIENTRY
537 _mesa_EndPerfMonitorAMD(GLuint monitor)
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, "glEndPerfMonitorAMD(invalid monitor)");
545 return;
546 }
547
548 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
549 * when a performance monitor is not currently started."
550 */
551 if (!m->Active) {
552 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)");
553 return;
554 }
555
556 ctx->Driver.EndPerfMonitor(ctx, m);
557
558 m->Active = false;
559 m->Ended = true;
560 }
561
562 /**
563 * Return the number of bytes needed to store a monitor's result.
564 */
565 static unsigned
566 perf_monitor_result_size(const struct gl_context *ctx,
567 const struct gl_perf_monitor_object *m)
568 {
569 unsigned group, counter;
570 unsigned size = 0;
571
572 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
573 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
574 for (counter = 0; counter < g->NumCounters; counter++) {
575 const struct gl_perf_monitor_counter *c = &g->Counters[counter];
576
577 if (!BITSET_TEST(m->ActiveCounters[group], counter))
578 continue;
579
580 size += sizeof(uint32_t); /* Group ID */
581 size += sizeof(uint32_t); /* Counter ID */
582 size += _mesa_perf_monitor_counter_size(c);
583 }
584 }
585 return size;
586 }
587
588 void GLAPIENTRY
589 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
590 GLsizei dataSize, GLuint *data,
591 GLint *bytesWritten)
592 {
593 GET_CURRENT_CONTEXT(ctx);
594
595 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
596 bool result_available;
597
598 if (m == NULL) {
599 _mesa_error(ctx, GL_INVALID_VALUE,
600 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
601 return;
602 }
603
604 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
605 if (data == NULL) {
606 _mesa_error(ctx, GL_INVALID_OPERATION,
607 "glGetPerfMonitorCounterDataAMD(data == NULL)");
608 return;
609 }
610
611 /* We need at least enough room for a single value. */
612 if (dataSize < sizeof(GLuint)) {
613 if (bytesWritten != NULL)
614 *bytesWritten = 0;
615 return;
616 }
617
618 /* If the monitor has never ended, there is no result. */
619 result_available = m->Ended &&
620 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
621
622 /* AMD appears to return 0 for all queries unless a result is available. */
623 if (!result_available) {
624 *data = 0;
625 if (bytesWritten != NULL)
626 *bytesWritten = sizeof(GLuint);
627 return;
628 }
629
630 switch (pname) {
631 case GL_PERFMON_RESULT_AVAILABLE_AMD:
632 *data = 1;
633 if (bytesWritten != NULL)
634 *bytesWritten = sizeof(GLuint);
635 break;
636 case GL_PERFMON_RESULT_SIZE_AMD:
637 *data = perf_monitor_result_size(ctx, m);
638 if (bytesWritten != NULL)
639 *bytesWritten = sizeof(GLuint);
640 break;
641 case GL_PERFMON_RESULT_AMD:
642 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
643 break;
644 default:
645 _mesa_error(ctx, GL_INVALID_ENUM,
646 "glGetPerfMonitorCounterDataAMD(pname)");
647 }
648 }
649
650 /**
651 * Returns how many bytes a counter's value takes up.
652 */
653 unsigned
654 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
655 {
656 switch (c->Type) {
657 case GL_FLOAT:
658 case GL_PERCENTAGE_AMD:
659 return sizeof(GLfloat);
660 case GL_UNSIGNED_INT:
661 return sizeof(GLuint);
662 case GL_UNSIGNED_INT64_AMD:
663 return sizeof(uint64_t);
664 default:
665 assert(!"Should not get here: invalid counter type");
666 return 0;
667 }
668 }
669
670 extern void GLAPIENTRY
671 _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
672 {
673 GET_CURRENT_CONTEXT(ctx);
674 unsigned numGroups;
675
676 /* The GL_INTEL_performance_query spec says:
677 *
678 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
679 */
680 if (!queryId) {
681 _mesa_error(ctx, GL_INVALID_VALUE,
682 "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
683 return;
684 }
685
686 numGroups = ctx->PerfMonitor.NumGroups;
687
688 /* The GL_INTEL_performance_query spec says:
689 *
690 * "If the given hardware platform doesn't support any performance
691 * queries, then the value of 0 is returned and INVALID_OPERATION error
692 * is raised."
693 */
694 if (numGroups == 0) {
695 *queryId = 0;
696 _mesa_error(ctx, GL_INVALID_OPERATION,
697 "glGetFirstPerfQueryIdINTEL(no queries supported)");
698 return;
699 }
700
701 *queryId = index_to_queryid(0);
702 }
703
704 extern void GLAPIENTRY
705 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId)
706 {
707 GET_CURRENT_CONTEXT(ctx);
708
709 /* The GL_INTEL_performance_query spec says:
710 *
711 * "The result is passed in location pointed by nextQueryId. If query
712 * identified by queryId is the last query available the value of 0 is
713 * returned. If the specified performance query identifier is invalid
714 * then INVALID_VALUE error is generated. If nextQueryId pointer is
715 * equal to 0, an INVALID_VALUE error is generated. Whenever error is
716 * generated, the value of 0 is returned."
717 */
718
719 if (!nextQueryId) {
720 _mesa_error(ctx, GL_INVALID_VALUE,
721 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
722 return;
723 }
724
725 if (!queryid_valid(ctx, queryId)) {
726 *nextQueryId = 0;
727 _mesa_error(ctx, GL_INVALID_VALUE,
728 "glGetNextPerfQueryIdINTEL(invalid query)");
729 return;
730 }
731
732 ++queryId;
733
734 if (!queryid_valid(ctx, queryId)) {
735 *nextQueryId = 0;
736 } else {
737 *nextQueryId = queryId;
738 }
739 }
740
741 extern void GLAPIENTRY
742 _mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId)
743 {
744 GET_CURRENT_CONTEXT(ctx);
745 unsigned i;
746
747 /* The GL_INTEL_performance_query spec says:
748 *
749 * "If queryName does not reference a valid query name, an INVALID_VALUE
750 * error is generated."
751 */
752 if (!queryName) {
753 _mesa_error(ctx, GL_INVALID_VALUE,
754 "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
755 return;
756 }
757
758 /* The specification does not state that this produces an error. */
759 if (!queryId) {
760 _mesa_warning(ctx, "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
761 return;
762 }
763
764 for (i = 0; i < ctx->PerfMonitor.NumGroups; ++i) {
765 const struct gl_perf_monitor_group *group_obj = get_group(ctx, i);
766 if (strcmp(group_obj->Name, queryName) == 0) {
767 *queryId = index_to_queryid(i);
768 return;
769 }
770 }
771
772 _mesa_error(ctx, GL_INVALID_VALUE,
773 "glGetPerfQueryIdByNameINTEL(invalid query name)");
774 }
775
776 extern void GLAPIENTRY
777 _mesa_GetPerfQueryInfoINTEL(GLuint queryId,
778 GLuint queryNameLength, char *queryName,
779 GLuint *dataSize, GLuint *noCounters,
780 GLuint *noActiveInstances,
781 GLuint *capsMask)
782 {
783 GET_CURRENT_CONTEXT(ctx);
784 unsigned i;
785
786 const struct gl_perf_monitor_group *group_obj =
787 get_group(ctx, queryid_to_index(queryId));
788
789 if (group_obj == NULL) {
790 /* The GL_INTEL_performance_query spec says:
791 *
792 * "If queryId does not reference a valid query type, an
793 * INVALID_VALUE error is generated."
794 */
795 _mesa_error(ctx, GL_INVALID_VALUE,
796 "glGetPerfQueryInfoINTEL(invalid query)");
797 return;
798 }
799
800 if (queryName) {
801 strncpy(queryName, group_obj->Name, queryNameLength);
802
803 /* No specification given about whether the string needs to be
804 * zero-terminated. Zero-terminate the string always as we don't
805 * otherwise communicate the length of the returned string.
806 */
807 if (queryNameLength > 0) {
808 queryName[queryNameLength - 1] = '\0';
809 }
810 }
811
812 if (dataSize) {
813 unsigned size = 0;
814 for (i = 0; i < group_obj->NumCounters; ++i) {
815 /* What we get from the driver is group id (uint32_t) + counter id
816 * (uint32_t) + value.
817 */
818 size += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]);
819 }
820 *dataSize = size;
821 }
822
823 if (noCounters) {
824 *noCounters = group_obj->NumCounters;
825 }
826
827 /* The GL_INTEL_performance_query spec says:
828 *
829 * "-- the actual number of already created query instances in
830 * maxInstances location"
831 *
832 * 1) Typo in the specification, should be noActiveInstances.
833 * 2) Another typo in the specification, maxInstances parameter is not listed
834 * in the declaration of this function in the list of new functions.
835 */
836 if (noActiveInstances) {
837 *noActiveInstances = _mesa_HashNumEntries(ctx->PerfMonitor.Monitors);
838 }
839
840 if (capsMask) {
841 /* TODO: This information not yet available in the monitor structs. For
842 * now, we hardcode SINGLE_CONTEXT, since that's what the implementation
843 * currently tries very hard to do.
844 */
845 *capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL;
846 }
847 }
848
849 extern void GLAPIENTRY
850 _mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId,
851 GLuint counterNameLength, char *counterName,
852 GLuint counterDescLength, char *counterDesc,
853 GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum,
854 GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue)
855 {
856 GET_CURRENT_CONTEXT(ctx);
857
858 const struct gl_perf_monitor_group *group_obj;
859 const struct gl_perf_monitor_counter *counter_obj;
860 unsigned counterIndex;
861 unsigned i;
862
863 group_obj = get_group(ctx, queryid_to_index(queryId));
864
865 /* The GL_INTEL_performance_query spec says:
866 *
867 * "If the pair of queryId and counterId does not reference a valid
868 * counter, an INVALID_VALUE error is generated."
869 */
870 if (group_obj == NULL) {
871 _mesa_error(ctx, GL_INVALID_VALUE,
872 "glGetPerfCounterInfoINTEL(invalid queryId)");
873 return;
874 }
875
876 counterIndex = counterid_to_index(counterId);
877 counter_obj = get_counter(group_obj, counterIndex);
878
879 if (counter_obj == NULL) {
880 _mesa_error(ctx, GL_INVALID_VALUE,
881 "glGetPerfCounterInfoINTEL(invalid counterId)");
882 return;
883 }
884
885 if (counterName) {
886 strncpy(counterName, counter_obj->Name, counterNameLength);
887
888 /* No specification given about whether the string needs to be
889 * zero-terminated. Zero-terminate the string always as we don't
890 * otherwise communicate the length of the returned string.
891 */
892 if (counterNameLength > 0) {
893 counterName[counterNameLength - 1] = '\0';
894 }
895 }
896
897 if (counterDesc) {
898 /* TODO: No separate description text at the moment. We pass the name
899 * again for the moment.
900 */
901 strncpy(counterDesc, counter_obj->Name, counterDescLength);
902
903 /* No specification given about whether the string needs to be
904 * zero-terminated. Zero-terminate the string always as we don't
905 * otherwise communicate the length of the returned string.
906 */
907 if (counterDescLength > 0) {
908 counterDesc[counterDescLength - 1] = '\0';
909 }
910 }
911
912 if (counterOffset) {
913 unsigned offset = 0;
914 for (i = 0; i < counterIndex; ++i) {
915 /* What we get from the driver is group id (uint32_t) + counter id
916 * (uint32_t) + value.
917 */
918 offset += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]);
919 }
920 *counterOffset = 2 * sizeof(uint32_t) + offset;
921 }
922
923 if (counterDataSize) {
924 *counterDataSize = _mesa_perf_monitor_counter_size(counter_obj);
925 }
926
927 if (counterTypeEnum) {
928 /* TODO: Different counter types (semantic type, not data type) not
929 * supported as of yet.
930 */
931 *counterTypeEnum = GL_PERFQUERY_COUNTER_RAW_INTEL;
932 }
933
934 if (counterDataTypeEnum) {
935 switch (counter_obj->Type) {
936 case GL_FLOAT:
937 case GL_PERCENTAGE_AMD:
938 *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL;
939 break;
940 case GL_UNSIGNED_INT:
941 *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL;
942 break;
943 case GL_UNSIGNED_INT64_AMD:
944 *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL;
945 break;
946 default:
947 assert(!"Should not get here: invalid counter type");
948 return;
949 }
950 }
951
952 if (rawCounterMaxValue) {
953 /* This value is (implicitly) specified to be used only with
954 * GL_PERFQUERY_COUNTER_RAW_INTEL counters. When semantic types for
955 * counters are added, that needs to be checked.
956 */
957
958 /* The GL_INTEL_performance_query spec says:
959 *
960 * "for some raw counters for which the maximal value is
961 * deterministic, the maximal value of the counter in 1 second is
962 * returned in the location pointed by rawCounterMaxValue, otherwise,
963 * the location is written with the value of 0."
964 *
965 * The maximum value reported by the driver at the moment is not with
966 * these semantics, so write 0 always to be safe.
967 */
968 *rawCounterMaxValue = 0;
969 }
970 }
971
972 extern void GLAPIENTRY
973 _mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle)
974 {
975 GET_CURRENT_CONTEXT(ctx);
976 GLuint first;
977 GLuint group;
978 const struct gl_perf_monitor_group *group_obj;
979 struct gl_perf_monitor_object *m;
980 unsigned i;
981
982 /* This is not specified in the extension, but is the only sane thing to
983 * do.
984 */
985 if (queryHandle == NULL) {
986 _mesa_error(ctx, GL_INVALID_VALUE,
987 "glCreatePerfQueryINTEL(queryHandle == NULL)");
988 return;
989 }
990
991 group = queryid_to_index(queryId);
992 group_obj = get_group(ctx, group);
993
994 /* The GL_INTEL_performance_query spec says:
995 *
996 * "If queryId does not reference a valid query type, an INVALID_VALUE
997 * error is generated."
998 */
999 if (group_obj == NULL) {
1000 _mesa_error(ctx, GL_INVALID_VALUE,
1001 "glCreatePerfQueryINTEL(invalid queryId)");
1002 return;
1003 }
1004
1005 /* The query object created here is the counterpart of a `monitor' in
1006 * AMD_performance_monitor. This call is equivalent to calling
1007 * GenPerfMonitorsAMD and SelectPerfMonitorCountersAMD with a list of all
1008 * counters in a group.
1009 */
1010
1011 /* We keep the monitor ids contiguous */
1012 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, 1);
1013 if (!first) {
1014 /* The GL_INTEL_performance_query spec says:
1015 *
1016 * "If the query instance cannot be created due to exceeding the
1017 * number of allowed instances or driver fails query creation due to
1018 * an insufficient memory reason, an OUT_OF_MEMORY error is
1019 * generated, and the location pointed by queryHandle returns NULL."
1020 */
1021 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCreatePerfQueryINTEL");
1022 return;
1023 }
1024
1025 m = new_performance_monitor(ctx, first);
1026 if (m == NULL) {
1027 _mesa_error_no_memory(__func__);
1028 return;
1029 }
1030
1031 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first, m);
1032 *queryHandle = first;
1033
1034 ctx->Driver.ResetPerfMonitor(ctx, m);
1035
1036 for (i = 0; i < group_obj->NumCounters; ++i) {
1037 ++m->ActiveGroups[group];
1038 /* Counters are a continuous range of integers, 0 to NumCounters (excl),
1039 * so i is the counter value to use here.
1040 */
1041 BITSET_SET(m->ActiveCounters[group], i);
1042 }
1043 }
1044
1045 extern void GLAPIENTRY
1046 _mesa_DeletePerfQueryINTEL(GLuint queryHandle)
1047 {
1048 GET_CURRENT_CONTEXT(ctx);
1049 struct gl_perf_monitor_object *m;
1050
1051 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1052 * id.
1053 */
1054 m = lookup_monitor(ctx, queryHandle);
1055
1056 /* The GL_INTEL_performance_query spec says:
1057 *
1058 * "If a query handle doesn't reference a previously created performance
1059 * query instance, an INVALID_VALUE error is generated."
1060 */
1061 if (m == NULL) {
1062 _mesa_error(ctx, GL_INVALID_VALUE,
1063 "glDeletePerfQueryINTEL(invalid queryHandle)");
1064 return;
1065 }
1066
1067 /* Let the driver stop the monitor if it's active. */
1068 if (m->Active) {
1069 ctx->Driver.ResetPerfMonitor(ctx, m);
1070 m->Ended = false;
1071 }
1072
1073 _mesa_HashRemove(ctx->PerfMonitor.Monitors, queryHandle);
1074 ralloc_free(m->ActiveGroups);
1075 ralloc_free(m->ActiveCounters);
1076 ctx->Driver.DeletePerfMonitor(ctx, m);
1077 }
1078
1079 extern void GLAPIENTRY
1080 _mesa_BeginPerfQueryINTEL(GLuint queryHandle)
1081 {
1082 GET_CURRENT_CONTEXT(ctx);
1083 struct gl_perf_monitor_object *m;
1084
1085 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1086 * id.
1087 */
1088
1089 m = lookup_monitor(ctx, queryHandle);
1090
1091 /* The GL_INTEL_performance_query spec says:
1092 *
1093 * "If a query handle doesn't reference a previously created performance
1094 * query instance, an INVALID_VALUE error is generated."
1095 */
1096 if (m == NULL) {
1097 _mesa_error(ctx, GL_INVALID_VALUE,
1098 "glBeginPerfQueryINTEL(invalid queryHandle)");
1099 return;
1100 }
1101
1102 /* The GL_INTEL_performance_query spec says:
1103 *
1104 * "Note that some query types, they cannot be collected in the same
1105 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
1106 * they refer to queries of such different types. In such case
1107 * INVALID_OPERATION error is generated."
1108 *
1109 * We also generate an INVALID_OPERATION error if the driver can't begin
1110 * monitoring for its own reasons, and for nesting the same query.
1111 */
1112 if (m->Active) {
1113 _mesa_error(ctx, GL_INVALID_OPERATION,
1114 "glBeginPerfQueryINTEL(already active)");
1115 return;
1116 }
1117
1118 if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
1119 m->Active = true;
1120 m->Ended = false;
1121 } else {
1122 _mesa_error(ctx, GL_INVALID_OPERATION,
1123 "glBeginPerfQueryINTEL(driver unable to begin monitoring)");
1124 }
1125 }
1126
1127 extern void GLAPIENTRY
1128 _mesa_EndPerfQueryINTEL(GLuint queryHandle)
1129 {
1130 GET_CURRENT_CONTEXT(ctx);
1131 struct gl_perf_monitor_object *m;
1132
1133 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1134 * id.
1135 */
1136
1137 m = lookup_monitor(ctx, queryHandle);
1138
1139 /* The GL_INTEL_performance_query spec says:
1140 *
1141 * "If a performance query is not currently started, an
1142 * INVALID_OPERATION error will be generated."
1143 *
1144 * The specification doesn't state that an invalid handle would be an
1145 * INVALID_VALUE error. Regardless, query for such a handle will not be
1146 * started, so we generate an INVALID_OPERATION in that case too.
1147 */
1148 if (m == NULL) {
1149 _mesa_error(ctx, GL_INVALID_OPERATION,
1150 "glEndPerfQueryINTEL(invalid queryHandle)");
1151 return;
1152 }
1153
1154 if (!m->Active) {
1155 _mesa_error(ctx, GL_INVALID_OPERATION,
1156 "glEndPerfQueryINTEL(not active)");
1157 return;
1158 }
1159
1160 ctx->Driver.EndPerfMonitor(ctx, m);
1161
1162 m->Active = false;
1163 m->Ended = true;
1164 }
1165
1166 extern void GLAPIENTRY
1167 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
1168 GLsizei dataSize, void *data, GLuint *bytesWritten)
1169 {
1170 GET_CURRENT_CONTEXT(ctx);
1171 struct gl_perf_monitor_object *m;
1172 bool result_available;
1173
1174 /* The GL_INTEL_performance_query spec says:
1175 *
1176 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE
1177 * error is generated."
1178 */
1179 if (!bytesWritten || !data) {
1180 _mesa_error(ctx, GL_INVALID_VALUE,
1181 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
1182 return;
1183 }
1184
1185 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1186 * id.
1187 */
1188
1189 m = lookup_monitor(ctx, queryHandle);
1190
1191 /* The specification doesn't state that an invalid handle generates an
1192 * error. We could interpret that to mean the case should be handled as
1193 * "measurement not ready for this query", but what should be done if
1194 * `flags' equals PERFQUERY_WAIT_INTEL?
1195 *
1196 * To resolve this, we just generate an INVALID_VALUE from an invalid query
1197 * handle.
1198 */
1199 if (m == NULL) {
1200 _mesa_error(ctx, GL_INVALID_VALUE,
1201 "glGetPerfQueryDataINTEL(invalid queryHandle)");
1202 return;
1203 }
1204
1205 /* We need at least enough room for a single value. */
1206 if (dataSize < sizeof(GLuint)) {
1207 *bytesWritten = 0;
1208 return;
1209 }
1210
1211 /* The GL_INTEL_performance_query spec says:
1212 *
1213 * "The call may end without returning any data if they are not ready
1214 * for reading as the measurement session is still pending (the
1215 * EndPerfQueryINTEL() command processing is not finished by
1216 * hardware). In this case location pointed by the bytesWritten
1217 * parameter will be set to 0."
1218 *
1219 * If EndPerfQueryINTEL() is not called at all, we follow this.
1220 */
1221 if (!m->Ended) {
1222 *bytesWritten = 0;
1223 return;
1224 }
1225
1226 result_available = ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
1227
1228 if (!result_available) {
1229 if (flags == GL_PERFQUERY_FLUSH_INTEL) {
1230 ctx->Driver.Flush(ctx);
1231 } else if (flags == GL_PERFQUERY_WAIT_INTEL) {
1232 /* Assume Finish() is both enough and not too much to wait for
1233 * results. If results are still not available after Finish(), the
1234 * later code automatically bails out with 0 for bytesWritten.
1235 */
1236 ctx->Driver.Finish(ctx);
1237 result_available =
1238 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
1239 }
1240 }
1241
1242 if (result_available) {
1243 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, (GLint*)bytesWritten);
1244 } else {
1245 *bytesWritten = 0;
1246 }
1247 }