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 #include "util/u_memory.h"
48 #include "api_exec_decl.h"
49
50 #include "state_tracker/st_cb_bitmap.h"
51 #include "state_tracker/st_context.h"
52 #include "state_tracker/st_debug.h"
53
54 #include "pipe/p_context.h"
55 #include "pipe/p_screen.h"
56
57 void
_mesa_init_performance_monitors(struct gl_context * ctx)58 _mesa_init_performance_monitors(struct gl_context *ctx)
59 {
60 ctx->PerfMonitor.Monitors = _mesa_NewHashTable();
61 ctx->PerfMonitor.NumGroups = 0;
62 ctx->PerfMonitor.Groups = NULL;
63 }
64
65
66 static bool
init_perf_monitor(struct gl_context * ctx,struct gl_perf_monitor_object * m)67 init_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
68 {
69 struct pipe_context *pipe = ctx->pipe;
70 unsigned *batch = NULL;
71 unsigned num_active_counters = 0;
72 unsigned max_batch_counters = 0;
73 unsigned num_batch_counters = 0;
74 int gid, cid;
75
76 st_flush_bitmap_cache(st_context(ctx));
77
78 /* Determine the number of active counters. */
79 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) {
80 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid];
81
82 if (m->ActiveGroups[gid] > g->MaxActiveCounters) {
83 /* Maximum number of counters reached. Cannot start the session. */
84 if (ST_DEBUG & DEBUG_MESA) {
85 debug_printf("Maximum number of counters reached. "
86 "Cannot start the session!\n");
87 }
88 return false;
89 }
90
91 num_active_counters += m->ActiveGroups[gid];
92 if (g->has_batch)
93 max_batch_counters += m->ActiveGroups[gid];
94 }
95
96 if (!num_active_counters)
97 return true;
98
99 m->active_counters = CALLOC(num_active_counters,
100 sizeof(*m->active_counters));
101 if (!m->active_counters)
102 return false;
103
104 if (max_batch_counters) {
105 batch = CALLOC(max_batch_counters, sizeof(*batch));
106 if (!batch)
107 return false;
108 }
109
110 /* Create a query for each active counter. */
111 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) {
112 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid];
113
114 BITSET_FOREACH_SET(cid, m->ActiveCounters[gid], g->NumCounters) {
115 const struct gl_perf_monitor_counter *c = &g->Counters[cid];
116 struct gl_perf_counter_object *cntr =
117 &m->active_counters[m->num_active_counters];
118
119 cntr->id = cid;
120 cntr->group_id = gid;
121 if (c->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) {
122 cntr->batch_index = num_batch_counters;
123 batch[num_batch_counters++] = c->query_type;
124 } else {
125 cntr->query = pipe->create_query(pipe, c->query_type, 0);
126 if (!cntr->query)
127 goto fail;
128 }
129 ++m->num_active_counters;
130 }
131 }
132
133 /* Create the batch query. */
134 if (num_batch_counters) {
135 m->batch_query = pipe->create_batch_query(pipe, num_batch_counters,
136 batch);
137 m->batch_result = CALLOC(num_batch_counters, sizeof(m->batch_result->batch[0]));
138 if (!m->batch_query || !m->batch_result)
139 goto fail;
140 }
141
142 FREE(batch);
143 return true;
144
145 fail:
146 FREE(batch);
147 return false;
148 }
149
150 static void
do_reset_perf_monitor(struct gl_perf_monitor_object * m,struct pipe_context * pipe)151 do_reset_perf_monitor(struct gl_perf_monitor_object *m,
152 struct pipe_context *pipe)
153 {
154 unsigned i;
155
156 for (i = 0; i < m->num_active_counters; ++i) {
157 struct pipe_query *query = m->active_counters[i].query;
158 if (query)
159 pipe->destroy_query(pipe, query);
160 }
161 FREE(m->active_counters);
162 m->active_counters = NULL;
163 m->num_active_counters = 0;
164
165 if (m->batch_query) {
166 pipe->destroy_query(pipe, m->batch_query);
167 m->batch_query = NULL;
168 }
169 FREE(m->batch_result);
170 m->batch_result = NULL;
171 }
172
173 static void
delete_perf_monitor(struct gl_context * ctx,struct gl_perf_monitor_object * m)174 delete_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
175 {
176 struct pipe_context *pipe = st_context(ctx)->pipe;
177
178 do_reset_perf_monitor(m, pipe);
179 FREE(m);
180 }
181
182 static GLboolean
begin_perf_monitor(struct gl_context * ctx,struct gl_perf_monitor_object * m)183 begin_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
184 {
185 struct pipe_context *pipe = st_context(ctx)->pipe;
186 unsigned i;
187
188 if (!m->num_active_counters) {
189 /* Create a query for each active counter before starting
190 * a new monitoring session. */
191 if (!init_perf_monitor(ctx, m))
192 goto fail;
193 }
194
195 /* Start the query for each active counter. */
196 for (i = 0; i < m->num_active_counters; ++i) {
197 struct pipe_query *query = m->active_counters[i].query;
198 if (query && !pipe->begin_query(pipe, query))
199 goto fail;
200 }
201
202 if (m->batch_query && !pipe->begin_query(pipe, m->batch_query))
203 goto fail;
204
205 return true;
206
207 fail:
208 /* Failed to start the monitoring session. */
209 do_reset_perf_monitor(m, pipe);
210 return false;
211 }
212
213 static void
end_perf_monitor(struct gl_context * ctx,struct gl_perf_monitor_object * m)214 end_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
215 {
216 struct pipe_context *pipe = st_context(ctx)->pipe;
217 unsigned i;
218
219 /* Stop the query for each active counter. */
220 for (i = 0; i < m->num_active_counters; ++i) {
221 struct pipe_query *query = m->active_counters[i].query;
222 if (query)
223 pipe->end_query(pipe, query);
224 }
225
226 if (m->batch_query)
227 pipe->end_query(pipe, m->batch_query);
228 }
229
230 static void
reset_perf_monitor(struct gl_context * ctx,struct gl_perf_monitor_object * m)231 reset_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
232 {
233 struct pipe_context *pipe = st_context(ctx)->pipe;
234
235 if (!m->Ended)
236 end_perf_monitor(ctx, m);
237
238 do_reset_perf_monitor(m, pipe);
239
240 if (m->Active)
241 begin_perf_monitor(ctx, m);
242 }
243
244 static GLboolean
is_perf_monitor_result_available(struct gl_context * ctx,struct gl_perf_monitor_object * m)245 is_perf_monitor_result_available(struct gl_context *ctx,
246 struct gl_perf_monitor_object *m)
247 {
248 struct pipe_context *pipe = st_context(ctx)->pipe;
249 unsigned i;
250
251 if (!m->num_active_counters)
252 return false;
253
254 /* The result of a monitoring session is only available if the query of
255 * each active counter is idle. */
256 for (i = 0; i < m->num_active_counters; ++i) {
257 struct pipe_query *query = m->active_counters[i].query;
258 union pipe_query_result result;
259 if (query && !pipe->get_query_result(pipe, query, FALSE, &result)) {
260 /* The query is busy. */
261 return false;
262 }
263 }
264
265 if (m->batch_query &&
266 !pipe->get_query_result(pipe, m->batch_query, FALSE, m->batch_result))
267 return false;
268
269 return true;
270 }
271
272 static void
get_perf_monitor_result(struct gl_context * ctx,struct gl_perf_monitor_object * m,GLsizei dataSize,GLuint * data,GLint * bytesWritten)273 get_perf_monitor_result(struct gl_context *ctx,
274 struct gl_perf_monitor_object *m,
275 GLsizei dataSize,
276 GLuint *data,
277 GLint *bytesWritten)
278 {
279 struct pipe_context *pipe = st_context(ctx)->pipe;
280 unsigned i;
281
282 /* Copy data to the supplied array (data).
283 *
284 * The output data format is: <group ID, counter ID, value> for each
285 * active counter. The API allows counters to appear in any order.
286 */
287 GLsizei offset = 0;
288 bool have_batch_query = false;
289
290 if (m->batch_query)
291 have_batch_query = pipe->get_query_result(pipe, m->batch_query, TRUE,
292 m->batch_result);
293
294 /* Read query results for each active counter. */
295 for (i = 0; i < m->num_active_counters; ++i) {
296 struct gl_perf_counter_object *cntr = &m->active_counters[i];
297 union pipe_query_result result = { 0 };
298 int gid, cid;
299 GLenum type;
300
301 cid = cntr->id;
302 gid = cntr->group_id;
303 type = ctx->PerfMonitor.Groups[gid].Counters[cid].Type;
304
305 if (cntr->query) {
306 if (!pipe->get_query_result(pipe, cntr->query, TRUE, &result))
307 continue;
308 } else {
309 if (!have_batch_query)
310 continue;
311 result.batch[0] = m->batch_result->batch[cntr->batch_index];
312 }
313
314 data[offset++] = gid;
315 data[offset++] = cid;
316 switch (type) {
317 case GL_UNSIGNED_INT64_AMD:
318 memcpy(&data[offset], &result.u64, sizeof(uint64_t));
319 offset += sizeof(uint64_t) / sizeof(GLuint);
320 break;
321 case GL_UNSIGNED_INT:
322 memcpy(&data[offset], &result.u32, sizeof(uint32_t));
323 offset += sizeof(uint32_t) / sizeof(GLuint);
324 break;
325 case GL_FLOAT:
326 case GL_PERCENTAGE_AMD:
327 memcpy(&data[offset], &result.f, sizeof(GLfloat));
328 offset += sizeof(GLfloat) / sizeof(GLuint);
329 break;
330 }
331 }
332
333 if (bytesWritten)
334 *bytesWritten = offset * sizeof(GLuint);
335 }
336
337 void
_mesa_free_perfomance_monitor_groups(struct gl_context * ctx)338 _mesa_free_perfomance_monitor_groups(struct gl_context *ctx)
339 {
340 struct gl_perf_monitor_state *perfmon = &ctx->PerfMonitor;
341 int gid;
342
343 for (gid = 0; gid < perfmon->NumGroups; gid++) {
344 FREE((void *)perfmon->Groups[gid].Counters);
345 }
346 FREE((void *)perfmon->Groups);
347 }
348
349 static inline void
init_groups(struct gl_context * ctx)350 init_groups(struct gl_context *ctx)
351 {
352 if (likely(ctx->PerfMonitor.Groups))
353 return;
354
355 struct gl_perf_monitor_state *perfmon = &ctx->PerfMonitor;
356 struct pipe_screen *screen = ctx->pipe->screen;
357 struct gl_perf_monitor_group *groups = NULL;
358 int num_counters, num_groups;
359 int gid, cid;
360
361 /* Get the number of available queries. */
362 num_counters = screen->get_driver_query_info(screen, 0, NULL);
363
364 /* Get the number of available groups. */
365 num_groups = screen->get_driver_query_group_info(screen, 0, NULL);
366 groups = CALLOC(num_groups, sizeof(*groups));
367 if (!groups)
368 return;
369
370 for (gid = 0; gid < num_groups; gid++) {
371 struct gl_perf_monitor_group *g = &groups[perfmon->NumGroups];
372 struct pipe_driver_query_group_info group_info;
373 struct gl_perf_monitor_counter *counters = NULL;
374
375 if (!screen->get_driver_query_group_info(screen, gid, &group_info))
376 continue;
377
378 g->Name = group_info.name;
379 g->MaxActiveCounters = group_info.max_active_queries;
380
381 if (group_info.num_queries)
382 counters = CALLOC(group_info.num_queries, sizeof(*counters));
383 if (!counters)
384 goto fail;
385 g->Counters = counters;
386
387 for (cid = 0; cid < num_counters; cid++) {
388 struct gl_perf_monitor_counter *c = &counters[g->NumCounters];
389 struct pipe_driver_query_info info;
390
391 if (!screen->get_driver_query_info(screen, cid, &info))
392 continue;
393 if (info.group_id != gid)
394 continue;
395
396 c->Name = info.name;
397 switch (info.type) {
398 case PIPE_DRIVER_QUERY_TYPE_UINT64:
399 case PIPE_DRIVER_QUERY_TYPE_BYTES:
400 case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS:
401 case PIPE_DRIVER_QUERY_TYPE_HZ:
402 c->Minimum.u64 = 0;
403 c->Maximum.u64 = info.max_value.u64 ? info.max_value.u64 : UINT64_MAX;
404 c->Type = GL_UNSIGNED_INT64_AMD;
405 break;
406 case PIPE_DRIVER_QUERY_TYPE_UINT:
407 c->Minimum.u32 = 0;
408 c->Maximum.u32 = info.max_value.u32 ? info.max_value.u32 : UINT32_MAX;
409 c->Type = GL_UNSIGNED_INT;
410 break;
411 case PIPE_DRIVER_QUERY_TYPE_FLOAT:
412 c->Minimum.f = 0.0;
413 c->Maximum.f = info.max_value.f ? info.max_value.f : FLT_MAX;
414 c->Type = GL_FLOAT;
415 break;
416 case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE:
417 c->Minimum.f = 0.0f;
418 c->Maximum.f = 100.0f;
419 c->Type = GL_PERCENTAGE_AMD;
420 break;
421 default:
422 unreachable("Invalid driver query type!");
423 }
424
425 c->query_type = info.query_type;
426 c->flags = info.flags;
427 if (c->flags & PIPE_DRIVER_QUERY_FLAG_BATCH)
428 g->has_batch = true;
429
430 g->NumCounters++;
431 }
432 perfmon->NumGroups++;
433 }
434 perfmon->Groups = groups;
435
436 return;
437
438 fail:
439 for (gid = 0; gid < num_groups; gid++) {
440 FREE((void *)groups[gid].Counters);
441 }
442 FREE(groups);
443 }
444
445 static struct gl_perf_monitor_object *
new_performance_monitor(struct gl_context * ctx,GLuint index)446 new_performance_monitor(struct gl_context *ctx, GLuint index)
447 {
448 unsigned i;
449 struct gl_perf_monitor_object *m = CALLOC_STRUCT(gl_perf_monitor_object);
450
451 if (m == NULL)
452 return NULL;
453
454 m->Name = index;
455
456 m->Active = false;
457
458 m->ActiveGroups =
459 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
460
461 m->ActiveCounters =
462 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
463
464 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
465 goto fail;
466
467 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
468 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
469
470 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
471 BITSET_WORDS(g->NumCounters));
472 if (m->ActiveCounters[i] == NULL)
473 goto fail;
474 }
475
476 return m;
477
478 fail:
479 ralloc_free(m->ActiveGroups);
480 ralloc_free(m->ActiveCounters);
481 delete_perf_monitor(ctx, m);
482 return NULL;
483 }
484
485 static void
free_performance_monitor(void * data,void * user)486 free_performance_monitor(void *data, void *user)
487 {
488 struct gl_perf_monitor_object *m = data;
489 struct gl_context *ctx = user;
490
491 ralloc_free(m->ActiveGroups);
492 ralloc_free(m->ActiveCounters);
493 delete_perf_monitor(ctx, m);
494 }
495
496 void
_mesa_free_performance_monitors(struct gl_context * ctx)497 _mesa_free_performance_monitors(struct gl_context *ctx)
498 {
499 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
500 free_performance_monitor, ctx);
501 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
502 }
503
504 static inline struct gl_perf_monitor_object *
lookup_monitor(struct gl_context * ctx,GLuint id)505 lookup_monitor(struct gl_context *ctx, GLuint id)
506 {
507 return (struct gl_perf_monitor_object *)
508 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
509 }
510
511 static inline const struct gl_perf_monitor_group *
get_group(const struct gl_context * ctx,GLuint id)512 get_group(const struct gl_context *ctx, GLuint id)
513 {
514 if (id >= ctx->PerfMonitor.NumGroups)
515 return NULL;
516
517 return &ctx->PerfMonitor.Groups[id];
518 }
519
520 static inline const struct gl_perf_monitor_counter *
get_counter(const struct gl_perf_monitor_group * group_obj,GLuint id)521 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
522 {
523 if (id >= group_obj->NumCounters)
524 return NULL;
525
526 return &group_obj->Counters[id];
527 }
528
529 /*****************************************************************************/
530
531 void GLAPIENTRY
_mesa_GetPerfMonitorGroupsAMD(GLint * numGroups,GLsizei groupsSize,GLuint * groups)532 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
533 GLuint *groups)
534 {
535 GET_CURRENT_CONTEXT(ctx);
536 init_groups(ctx);
537
538 if (numGroups != NULL)
539 *numGroups = ctx->PerfMonitor.NumGroups;
540
541 if (groupsSize > 0 && groups != NULL) {
542 unsigned i;
543 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
544
545 /* We just use the index in the Groups array as the ID. */
546 for (i = 0; i < n; i++)
547 groups[i] = i;
548 }
549 }
550
551 void GLAPIENTRY
_mesa_GetPerfMonitorCountersAMD(GLuint group,GLint * numCounters,GLint * maxActiveCounters,GLsizei countersSize,GLuint * counters)552 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
553 GLint *maxActiveCounters,
554 GLsizei countersSize, GLuint *counters)
555 {
556 GET_CURRENT_CONTEXT(ctx);
557 const struct gl_perf_monitor_group *group_obj;
558
559 init_groups(ctx);
560
561 group_obj = get_group(ctx, group);
562 if (group_obj == NULL) {
563 _mesa_error(ctx, GL_INVALID_VALUE,
564 "glGetPerfMonitorCountersAMD(invalid group)");
565 return;
566 }
567
568 if (maxActiveCounters != NULL)
569 *maxActiveCounters = group_obj->MaxActiveCounters;
570
571 if (numCounters != NULL)
572 *numCounters = group_obj->NumCounters;
573
574 if (counters != NULL) {
575 unsigned i;
576 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
577 for (i = 0; i < n; i++) {
578 /* We just use the index in the Counters array as the ID. */
579 counters[i] = i;
580 }
581 }
582 }
583
584 void GLAPIENTRY
_mesa_GetPerfMonitorGroupStringAMD(GLuint group,GLsizei bufSize,GLsizei * length,GLchar * groupString)585 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
586 GLsizei *length, GLchar *groupString)
587 {
588 GET_CURRENT_CONTEXT(ctx);
589 const struct gl_perf_monitor_group *group_obj;
590
591 init_groups(ctx);
592
593 group_obj = get_group(ctx, group);
594 if (group_obj == NULL) {
595 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
596 return;
597 }
598
599 if (bufSize == 0) {
600 /* Return the number of characters that would be required to hold the
601 * group string, excluding the null terminator.
602 */
603 if (length != NULL)
604 *length = strlen(group_obj->Name);
605 } else {
606 if (length != NULL)
607 *length = MIN2(strlen(group_obj->Name), bufSize);
608 if (groupString != NULL)
609 strncpy(groupString, group_obj->Name, bufSize);
610 }
611 }
612
613 void GLAPIENTRY
_mesa_GetPerfMonitorCounterStringAMD(GLuint group,GLuint counter,GLsizei bufSize,GLsizei * length,GLchar * counterString)614 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
615 GLsizei bufSize, GLsizei *length,
616 GLchar *counterString)
617 {
618 GET_CURRENT_CONTEXT(ctx);
619
620 const struct gl_perf_monitor_group *group_obj;
621 const struct gl_perf_monitor_counter *counter_obj;
622
623 init_groups(ctx);
624
625 group_obj = get_group(ctx, group);
626
627 if (group_obj == NULL) {
628 _mesa_error(ctx, GL_INVALID_VALUE,
629 "glGetPerfMonitorCounterStringAMD(invalid group)");
630 return;
631 }
632
633 counter_obj = get_counter(group_obj, counter);
634
635 if (counter_obj == NULL) {
636 _mesa_error(ctx, GL_INVALID_VALUE,
637 "glGetPerfMonitorCounterStringAMD(invalid counter)");
638 return;
639 }
640
641 if (bufSize == 0) {
642 /* Return the number of characters that would be required to hold the
643 * counter string, excluding the null terminator.
644 */
645 if (length != NULL)
646 *length = strlen(counter_obj->Name);
647 } else {
648 if (length != NULL)
649 *length = MIN2(strlen(counter_obj->Name), bufSize);
650 if (counterString != NULL)
651 strncpy(counterString, counter_obj->Name, bufSize);
652 }
653 }
654
655 void GLAPIENTRY
_mesa_GetPerfMonitorCounterInfoAMD(GLuint group,GLuint counter,GLenum pname,GLvoid * data)656 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
657 GLvoid *data)
658 {
659 GET_CURRENT_CONTEXT(ctx);
660
661 const struct gl_perf_monitor_group *group_obj;
662 const struct gl_perf_monitor_counter *counter_obj;
663
664 init_groups(ctx);
665
666 group_obj = get_group(ctx, group);
667
668 if (group_obj == NULL) {
669 _mesa_error(ctx, GL_INVALID_VALUE,
670 "glGetPerfMonitorCounterInfoAMD(invalid group)");
671 return;
672 }
673
674 counter_obj = get_counter(group_obj, counter);
675
676 if (counter_obj == NULL) {
677 _mesa_error(ctx, GL_INVALID_VALUE,
678 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
679 return;
680 }
681
682 switch (pname) {
683 case GL_COUNTER_TYPE_AMD:
684 *((GLenum *) data) = counter_obj->Type;
685 break;
686
687 case GL_COUNTER_RANGE_AMD:
688 switch (counter_obj->Type) {
689 case GL_FLOAT:
690 case GL_PERCENTAGE_AMD: {
691 float *f_data = data;
692 f_data[0] = counter_obj->Minimum.f;
693 f_data[1] = counter_obj->Maximum.f;
694 break;
695 }
696 case GL_UNSIGNED_INT: {
697 uint32_t *u32_data = data;
698 u32_data[0] = counter_obj->Minimum.u32;
699 u32_data[1] = counter_obj->Maximum.u32;
700 break;
701 }
702 case GL_UNSIGNED_INT64_AMD: {
703 uint64_t *u64_data = data;
704 u64_data[0] = counter_obj->Minimum.u64;
705 u64_data[1] = counter_obj->Maximum.u64;
706 break;
707 }
708 default:
709 assert(!"Should not get here: invalid counter type");
710 }
711 break;
712
713 default:
714 _mesa_error(ctx, GL_INVALID_ENUM,
715 "glGetPerfMonitorCounterInfoAMD(pname)");
716 return;
717 }
718 }
719
720 void GLAPIENTRY
_mesa_GenPerfMonitorsAMD(GLsizei n,GLuint * monitors)721 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
722 {
723 GET_CURRENT_CONTEXT(ctx);
724
725 if (MESA_VERBOSE & VERBOSE_API)
726 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
727
728 init_groups(ctx);
729
730 if (n < 0) {
731 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
732 return;
733 }
734
735 if (monitors == NULL)
736 return;
737
738 if (_mesa_HashFindFreeKeys(ctx->PerfMonitor.Monitors, monitors, n)) {
739 GLsizei i;
740 for (i = 0; i < n; i++) {
741 struct gl_perf_monitor_object *m =
742 new_performance_monitor(ctx, monitors[i]);
743 if (!m) {
744 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
745 return;
746 }
747 _mesa_HashInsert(ctx->PerfMonitor.Monitors, monitors[i], m, true);
748 }
749 } else {
750 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
751 return;
752 }
753 }
754
755 void GLAPIENTRY
_mesa_DeletePerfMonitorsAMD(GLsizei n,GLuint * monitors)756 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
757 {
758 GLint i;
759 GET_CURRENT_CONTEXT(ctx);
760
761 if (MESA_VERBOSE & VERBOSE_API)
762 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
763
764 if (n < 0) {
765 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
766 return;
767 }
768
769 if (monitors == NULL)
770 return;
771
772 for (i = 0; i < n; i++) {
773 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
774
775 if (m) {
776 /* Give the driver a chance to stop the monitor if it's active. */
777 if (m->Active) {
778 reset_perf_monitor(ctx, m);
779 m->Ended = false;
780 }
781
782 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
783 ralloc_free(m->ActiveGroups);
784 ralloc_free(m->ActiveCounters);
785 delete_perf_monitor(ctx, m);
786 } else {
787 /* "INVALID_VALUE error will be generated if any of the monitor IDs
788 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
789 * reference a valid generated monitor ID."
790 */
791 _mesa_error(ctx, GL_INVALID_VALUE,
792 "glDeletePerfMonitorsAMD(invalid monitor)");
793 }
794 }
795 }
796
797 void GLAPIENTRY
_mesa_SelectPerfMonitorCountersAMD(GLuint monitor,GLboolean enable,GLuint group,GLint numCounters,GLuint * counterList)798 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
799 GLuint group, GLint numCounters,
800 GLuint *counterList)
801 {
802 GET_CURRENT_CONTEXT(ctx);
803 int i;
804 struct gl_perf_monitor_object *m;
805 const struct gl_perf_monitor_group *group_obj;
806
807 m = lookup_monitor(ctx, monitor);
808
809 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
810 * SelectPerfMonitorCountersAMD does not reference a monitor created by
811 * GenPerfMonitorsAMD."
812 */
813 if (m == NULL) {
814 _mesa_error(ctx, GL_INVALID_VALUE,
815 "glSelectPerfMonitorCountersAMD(invalid monitor)");
816 return;
817 }
818
819 group_obj = get_group(ctx, group);
820
821 /* "INVALID_VALUE error will be generated if the <group> parameter to
822 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
823 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
824 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
825 */
826 if (group_obj == NULL) {
827 _mesa_error(ctx, GL_INVALID_VALUE,
828 "glSelectPerfMonitorCountersAMD(invalid group)");
829 return;
830 }
831
832 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
833 * SelectPerfMonitorCountersAMD is less than 0."
834 */
835 if (numCounters < 0) {
836 _mesa_error(ctx, GL_INVALID_VALUE,
837 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
838 return;
839 }
840
841 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
842 * results for that monitor become invalidated and the result queries
843 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
844 */
845 reset_perf_monitor(ctx, m);
846
847 /* Sanity check the counter ID list. */
848 for (i = 0; i < numCounters; i++) {
849 if (counterList[i] >= group_obj->NumCounters) {
850 _mesa_error(ctx, GL_INVALID_VALUE,
851 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
852 return;
853 }
854 }
855
856 if (enable) {
857 /* Enable the counters */
858 for (i = 0; i < numCounters; i++) {
859 if (!BITSET_TEST(m->ActiveCounters[group], counterList[i])) {
860 ++m->ActiveGroups[group];
861 BITSET_SET(m->ActiveCounters[group], counterList[i]);
862 }
863 }
864 } else {
865 /* Disable the counters */
866 for (i = 0; i < numCounters; i++) {
867 if (BITSET_TEST(m->ActiveCounters[group], counterList[i])) {
868 --m->ActiveGroups[group];
869 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
870 }
871 }
872 }
873 }
874
875 void GLAPIENTRY
_mesa_BeginPerfMonitorAMD(GLuint monitor)876 _mesa_BeginPerfMonitorAMD(GLuint monitor)
877 {
878 GET_CURRENT_CONTEXT(ctx);
879
880 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
881
882 if (m == NULL) {
883 _mesa_error(ctx, GL_INVALID_VALUE,
884 "glBeginPerfMonitorAMD(invalid monitor)");
885 return;
886 }
887
888 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
889 * called when a performance monitor is already active."
890 */
891 if (m->Active) {
892 _mesa_error(ctx, GL_INVALID_OPERATION,
893 "glBeginPerfMonitor(already active)");
894 return;
895 }
896
897 /* The driver is free to return false if it can't begin monitoring for
898 * any reason. This translates into an INVALID_OPERATION error.
899 */
900 if (begin_perf_monitor(ctx, m)) {
901 m->Active = true;
902 m->Ended = false;
903 } else {
904 _mesa_error(ctx, GL_INVALID_OPERATION,
905 "glBeginPerfMonitor(driver unable to begin monitoring)");
906 }
907 }
908
909 void GLAPIENTRY
_mesa_EndPerfMonitorAMD(GLuint monitor)910 _mesa_EndPerfMonitorAMD(GLuint monitor)
911 {
912 GET_CURRENT_CONTEXT(ctx);
913
914 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
915
916 if (m == NULL) {
917 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
918 return;
919 }
920
921 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
922 * when a performance monitor is not currently started."
923 */
924 if (!m->Active) {
925 _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfMonitor(not active)");
926 return;
927 }
928
929 end_perf_monitor(ctx, m);
930
931 m->Active = false;
932 m->Ended = true;
933 }
934
935 /**
936 * Return the number of bytes needed to store a monitor's result.
937 */
938 static unsigned
perf_monitor_result_size(const struct gl_context * ctx,const struct gl_perf_monitor_object * m)939 perf_monitor_result_size(const struct gl_context *ctx,
940 const struct gl_perf_monitor_object *m)
941 {
942 unsigned group, counter;
943 unsigned size = 0;
944
945 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
946 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
947
948 BITSET_FOREACH_SET(counter, m->ActiveCounters[group], g->NumCounters) {
949 const struct gl_perf_monitor_counter *c = &g->Counters[counter];
950
951 size += sizeof(uint32_t); /* Group ID */
952 size += sizeof(uint32_t); /* Counter ID */
953 size += _mesa_perf_monitor_counter_size(c);
954 }
955 }
956 return size;
957 }
958
959 void GLAPIENTRY
_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor,GLenum pname,GLsizei dataSize,GLuint * data,GLint * bytesWritten)960 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
961 GLsizei dataSize, GLuint *data,
962 GLint *bytesWritten)
963 {
964 GET_CURRENT_CONTEXT(ctx);
965
966 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
967 bool result_available;
968
969 if (m == NULL) {
970 _mesa_error(ctx, GL_INVALID_VALUE,
971 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
972 return;
973 }
974
975 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
976 if (data == NULL) {
977 _mesa_error(ctx, GL_INVALID_OPERATION,
978 "glGetPerfMonitorCounterDataAMD(data == NULL)");
979 return;
980 }
981
982 /* We need at least enough room for a single value. */
983 if (dataSize < sizeof(GLuint)) {
984 if (bytesWritten != NULL)
985 *bytesWritten = 0;
986 return;
987 }
988
989 /* If the monitor has never ended, there is no result. */
990 result_available = m->Ended &&
991 is_perf_monitor_result_available(ctx, m);
992
993 /* AMD appears to return 0 for all queries unless a result is available. */
994 if (!result_available) {
995 *data = 0;
996 if (bytesWritten != NULL)
997 *bytesWritten = sizeof(GLuint);
998 return;
999 }
1000
1001 switch (pname) {
1002 case GL_PERFMON_RESULT_AVAILABLE_AMD:
1003 *data = 1;
1004 if (bytesWritten != NULL)
1005 *bytesWritten = sizeof(GLuint);
1006 break;
1007 case GL_PERFMON_RESULT_SIZE_AMD:
1008 *data = perf_monitor_result_size(ctx, m);
1009 if (bytesWritten != NULL)
1010 *bytesWritten = sizeof(GLuint);
1011 break;
1012 case GL_PERFMON_RESULT_AMD:
1013 get_perf_monitor_result(ctx, m, dataSize, data, bytesWritten);
1014 break;
1015 default:
1016 _mesa_error(ctx, GL_INVALID_ENUM,
1017 "glGetPerfMonitorCounterDataAMD(pname)");
1018 }
1019 }
1020
1021 /**
1022 * Returns how many bytes a counter's value takes up.
1023 */
1024 unsigned
_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter * c)1025 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
1026 {
1027 switch (c->Type) {
1028 case GL_FLOAT:
1029 case GL_PERCENTAGE_AMD:
1030 return sizeof(GLfloat);
1031 case GL_UNSIGNED_INT:
1032 return sizeof(GLuint);
1033 case GL_UNSIGNED_INT64_AMD:
1034 return sizeof(uint64_t);
1035 default:
1036 assert(!"Should not get here: invalid counter type");
1037 return 0;
1038 }
1039 }
1040