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
57void
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
66static bool
67init_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
145fail:
146   FREE(batch);
147   return false;
148}
149
150static void
151do_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
173static void
174delete_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
182static GLboolean
183begin_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
207fail:
208   /* Failed to start the monitoring session. */
209   do_reset_perf_monitor(m, pipe);
210   return false;
211}
212
213static void
214end_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
230static void
231reset_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
244static GLboolean
245is_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
272static void
273get_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
337void
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
349static inline void
350init_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
438fail:
439   for (gid = 0; gid < num_groups; gid++) {
440      FREE((void *)groups[gid].Counters);
441   }
442   FREE(groups);
443}
444
445static struct gl_perf_monitor_object *
446new_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
478fail:
479   ralloc_free(m->ActiveGroups);
480   ralloc_free(m->ActiveCounters);
481   delete_perf_monitor(ctx, m);
482   return NULL;
483}
484
485static void
486free_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
496void
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
504static inline struct gl_perf_monitor_object *
505lookup_monitor(struct gl_context *ctx, GLuint id)
506{
507   return (struct gl_perf_monitor_object *)
508      _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
509}
510
511static inline const struct gl_perf_monitor_group *
512get_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
520static inline const struct gl_perf_monitor_counter *
521get_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
531void GLAPIENTRY
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
551void GLAPIENTRY
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
584void GLAPIENTRY
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
613void GLAPIENTRY
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
655void GLAPIENTRY
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
720void GLAPIENTRY
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
755void GLAPIENTRY
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
797void GLAPIENTRY
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
875void GLAPIENTRY
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
909void GLAPIENTRY
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 */
938static unsigned
939perf_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
959void GLAPIENTRY
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 */
1024unsigned
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