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