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