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
48void
49_mesa_init_performance_monitors(struct gl_context *ctx)
50{
51   ctx->PerfMonitor.Monitors = _mesa_NewHashTable();
52   ctx->PerfMonitor.NumGroups = 0;
53   ctx->PerfMonitor.Groups = NULL;
54}
55
56static inline void
57init_groups(struct gl_context *ctx)
58{
59   if (unlikely(!ctx->PerfMonitor.Groups))
60      ctx->Driver.InitPerfMonitorGroups(ctx);
61}
62
63static struct gl_perf_monitor_object *
64new_performance_monitor(struct gl_context *ctx, GLuint index)
65{
66   unsigned i;
67   struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx);
68
69   if (m == NULL)
70      return NULL;
71
72   m->Name = index;
73
74   m->Active = false;
75
76   m->ActiveGroups =
77      rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
78
79   m->ActiveCounters =
80      ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
81
82   if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
83      goto fail;
84
85   for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
86      const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
87
88      m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
89                                           BITSET_WORDS(g->NumCounters));
90      if (m->ActiveCounters[i] == NULL)
91         goto fail;
92   }
93
94   return m;
95
96fail:
97   ralloc_free(m->ActiveGroups);
98   ralloc_free(m->ActiveCounters);
99   ctx->Driver.DeletePerfMonitor(ctx, m);
100   return NULL;
101}
102
103static void
104free_performance_monitor(GLuint key, void *data, void *user)
105{
106   struct gl_perf_monitor_object *m = data;
107   struct gl_context *ctx = user;
108
109   ralloc_free(m->ActiveGroups);
110   ralloc_free(m->ActiveCounters);
111   ctx->Driver.DeletePerfMonitor(ctx, m);
112}
113
114void
115_mesa_free_performance_monitors(struct gl_context *ctx)
116{
117   _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
118                       free_performance_monitor, ctx);
119   _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
120}
121
122static inline struct gl_perf_monitor_object *
123lookup_monitor(struct gl_context *ctx, GLuint id)
124{
125   return (struct gl_perf_monitor_object *)
126      _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
127}
128
129static inline const struct gl_perf_monitor_group *
130get_group(const struct gl_context *ctx, GLuint id)
131{
132   if (id >= ctx->PerfMonitor.NumGroups)
133      return NULL;
134
135   return &ctx->PerfMonitor.Groups[id];
136}
137
138static inline const struct gl_perf_monitor_counter *
139get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
140{
141   if (id >= group_obj->NumCounters)
142      return NULL;
143
144   return &group_obj->Counters[id];
145}
146
147/*****************************************************************************/
148
149void GLAPIENTRY
150_mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
151                              GLuint *groups)
152{
153   GET_CURRENT_CONTEXT(ctx);
154   init_groups(ctx);
155
156   if (numGroups != NULL)
157      *numGroups = ctx->PerfMonitor.NumGroups;
158
159   if (groupsSize > 0 && groups != NULL) {
160      unsigned i;
161      unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
162
163      /* We just use the index in the Groups array as the ID. */
164      for (i = 0; i < n; i++)
165         groups[i] = i;
166   }
167}
168
169void GLAPIENTRY
170_mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
171                                GLint *maxActiveCounters,
172                                GLsizei countersSize, GLuint *counters)
173{
174   GET_CURRENT_CONTEXT(ctx);
175   const struct gl_perf_monitor_group *group_obj;
176
177   init_groups(ctx);
178
179   group_obj = get_group(ctx, group);
180   if (group_obj == NULL) {
181      _mesa_error(ctx, GL_INVALID_VALUE,
182                  "glGetPerfMonitorCountersAMD(invalid group)");
183      return;
184   }
185
186   if (maxActiveCounters != NULL)
187      *maxActiveCounters = group_obj->MaxActiveCounters;
188
189   if (numCounters != NULL)
190      *numCounters = group_obj->NumCounters;
191
192   if (counters != NULL) {
193      unsigned i;
194      unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
195      for (i = 0; i < n; i++) {
196         /* We just use the index in the Counters array as the ID. */
197         counters[i] = i;
198      }
199   }
200}
201
202void GLAPIENTRY
203_mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
204                                   GLsizei *length, GLchar *groupString)
205{
206   GET_CURRENT_CONTEXT(ctx);
207   const struct gl_perf_monitor_group *group_obj;
208
209   init_groups(ctx);
210
211   group_obj = get_group(ctx, group);
212   if (group_obj == NULL) {
213      _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
214      return;
215   }
216
217   if (bufSize == 0) {
218      /* Return the number of characters that would be required to hold the
219       * group string, excluding the null terminator.
220       */
221      if (length != NULL)
222         *length = strlen(group_obj->Name);
223   } else {
224      if (length != NULL)
225         *length = MIN2(strlen(group_obj->Name), bufSize);
226      if (groupString != NULL)
227         strncpy(groupString, group_obj->Name, bufSize);
228   }
229}
230
231void GLAPIENTRY
232_mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
233                                     GLsizei bufSize, GLsizei *length,
234                                     GLchar *counterString)
235{
236   GET_CURRENT_CONTEXT(ctx);
237
238   const struct gl_perf_monitor_group *group_obj;
239   const struct gl_perf_monitor_counter *counter_obj;
240
241   init_groups(ctx);
242
243   group_obj = get_group(ctx, group);
244
245   if (group_obj == NULL) {
246      _mesa_error(ctx, GL_INVALID_VALUE,
247                  "glGetPerfMonitorCounterStringAMD(invalid group)");
248      return;
249   }
250
251   counter_obj = get_counter(group_obj, counter);
252
253   if (counter_obj == NULL) {
254      _mesa_error(ctx, GL_INVALID_VALUE,
255                  "glGetPerfMonitorCounterStringAMD(invalid counter)");
256      return;
257   }
258
259   if (bufSize == 0) {
260      /* Return the number of characters that would be required to hold the
261       * counter string, excluding the null terminator.
262       */
263      if (length != NULL)
264         *length = strlen(counter_obj->Name);
265   } else {
266      if (length != NULL)
267         *length = MIN2(strlen(counter_obj->Name), bufSize);
268      if (counterString != NULL)
269         strncpy(counterString, counter_obj->Name, bufSize);
270   }
271}
272
273void GLAPIENTRY
274_mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
275                                   GLvoid *data)
276{
277   GET_CURRENT_CONTEXT(ctx);
278
279   const struct gl_perf_monitor_group *group_obj;
280   const struct gl_perf_monitor_counter *counter_obj;
281
282   init_groups(ctx);
283
284   group_obj = get_group(ctx, group);
285
286   if (group_obj == NULL) {
287      _mesa_error(ctx, GL_INVALID_VALUE,
288                  "glGetPerfMonitorCounterInfoAMD(invalid group)");
289      return;
290   }
291
292   counter_obj = get_counter(group_obj, counter);
293
294   if (counter_obj == NULL) {
295      _mesa_error(ctx, GL_INVALID_VALUE,
296                  "glGetPerfMonitorCounterInfoAMD(invalid counter)");
297      return;
298   }
299
300   switch (pname) {
301   case GL_COUNTER_TYPE_AMD:
302      *((GLenum *) data) = counter_obj->Type;
303      break;
304
305   case GL_COUNTER_RANGE_AMD:
306      switch (counter_obj->Type) {
307      case GL_FLOAT:
308      case GL_PERCENTAGE_AMD: {
309         float *f_data = data;
310         f_data[0] = counter_obj->Minimum.f;
311         f_data[1] = counter_obj->Maximum.f;
312         break;
313      }
314      case GL_UNSIGNED_INT: {
315         uint32_t *u32_data = data;
316         u32_data[0] = counter_obj->Minimum.u32;
317         u32_data[1] = counter_obj->Maximum.u32;
318         break;
319      }
320      case GL_UNSIGNED_INT64_AMD: {
321         uint64_t *u64_data = data;
322         u64_data[0] = counter_obj->Minimum.u64;
323         u64_data[1] = counter_obj->Maximum.u64;
324         break;
325      }
326      default:
327         assert(!"Should not get here: invalid counter type");
328      }
329      break;
330
331   default:
332      _mesa_error(ctx, GL_INVALID_ENUM,
333                  "glGetPerfMonitorCounterInfoAMD(pname)");
334      return;
335   }
336}
337
338void GLAPIENTRY
339_mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
340{
341   GLuint first;
342   GET_CURRENT_CONTEXT(ctx);
343
344   if (MESA_VERBOSE & VERBOSE_API)
345      _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
346
347   init_groups(ctx);
348
349   if (n < 0) {
350      _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
351      return;
352   }
353
354   if (monitors == NULL)
355      return;
356
357   /* We don't actually need them to be contiguous, but this is what
358    * the rest of Mesa does, so we may as well.
359    */
360   first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
361   if (first) {
362      GLsizei i;
363      for (i = 0; i < n; i++) {
364         struct gl_perf_monitor_object *m =
365            new_performance_monitor(ctx, first + i);
366         if (!m) {
367            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
368            return;
369         }
370         monitors[i] = first + i;
371         _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
372      }
373   } else {
374      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
375      return;
376   }
377}
378
379void GLAPIENTRY
380_mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
381{
382   GLint i;
383   GET_CURRENT_CONTEXT(ctx);
384
385   if (MESA_VERBOSE & VERBOSE_API)
386      _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
387
388   if (n < 0) {
389      _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
390      return;
391   }
392
393   if (monitors == NULL)
394      return;
395
396   for (i = 0; i < n; i++) {
397      struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
398
399      if (m) {
400         /* Give the driver a chance to stop the monitor if it's active. */
401         if (m->Active) {
402            ctx->Driver.ResetPerfMonitor(ctx, m);
403            m->Ended = false;
404         }
405
406         _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
407         ralloc_free(m->ActiveGroups);
408         ralloc_free(m->ActiveCounters);
409         ctx->Driver.DeletePerfMonitor(ctx, m);
410      } else {
411         /* "INVALID_VALUE error will be generated if any of the monitor IDs
412          *  in the <monitors> parameter to DeletePerfMonitorsAMD do not
413          *  reference a valid generated monitor ID."
414          */
415         _mesa_error(ctx, GL_INVALID_VALUE,
416                     "glDeletePerfMonitorsAMD(invalid monitor)");
417      }
418   }
419}
420
421void GLAPIENTRY
422_mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
423                                   GLuint group, GLint numCounters,
424                                   GLuint *counterList)
425{
426   GET_CURRENT_CONTEXT(ctx);
427   int i;
428   struct gl_perf_monitor_object *m;
429   const struct gl_perf_monitor_group *group_obj;
430
431   m = lookup_monitor(ctx, monitor);
432
433   /* "INVALID_VALUE error will be generated if the <monitor> parameter to
434    *  SelectPerfMonitorCountersAMD does not reference a monitor created by
435    *  GenPerfMonitorsAMD."
436    */
437   if (m == NULL) {
438      _mesa_error(ctx, GL_INVALID_VALUE,
439                  "glSelectPerfMonitorCountersAMD(invalid monitor)");
440      return;
441   }
442
443   group_obj = get_group(ctx, group);
444
445   /* "INVALID_VALUE error will be generated if the <group> parameter to
446    *  GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
447    *  GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
448    *  SelectPerfMonitorCountersAMD does not reference a valid group ID."
449    */
450   if (group_obj == NULL) {
451      _mesa_error(ctx, GL_INVALID_VALUE,
452                  "glSelectPerfMonitorCountersAMD(invalid group)");
453      return;
454   }
455
456   /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
457    *  SelectPerfMonitorCountersAMD is less than 0."
458    */
459   if (numCounters < 0) {
460      _mesa_error(ctx, GL_INVALID_VALUE,
461                  "glSelectPerfMonitorCountersAMD(numCounters < 0)");
462      return;
463   }
464
465   /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
466    *  results for that monitor become invalidated and the result queries
467    *  PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
468    */
469   ctx->Driver.ResetPerfMonitor(ctx, m);
470
471   /* Sanity check the counter ID list. */
472   for (i = 0; i < numCounters; i++) {
473      if (counterList[i] >= group_obj->NumCounters) {
474         _mesa_error(ctx, GL_INVALID_VALUE,
475                     "glSelectPerfMonitorCountersAMD(invalid counter ID)");
476         return;
477      }
478   }
479
480   if (enable) {
481      /* Enable the counters */
482      for (i = 0; i < numCounters; i++) {
483         if (!BITSET_TEST(m->ActiveCounters[group], counterList[i])) {
484            ++m->ActiveGroups[group];
485            BITSET_SET(m->ActiveCounters[group], counterList[i]);
486         }
487      }
488   } else {
489      /* Disable the counters */
490      for (i = 0; i < numCounters; i++) {
491         if (BITSET_TEST(m->ActiveCounters[group], counterList[i])) {
492            --m->ActiveGroups[group];
493            BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
494         }
495      }
496   }
497}
498
499void GLAPIENTRY
500_mesa_BeginPerfMonitorAMD(GLuint monitor)
501{
502   GET_CURRENT_CONTEXT(ctx);
503
504   struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
505
506   if (m == NULL) {
507      _mesa_error(ctx, GL_INVALID_VALUE,
508                  "glBeginPerfMonitorAMD(invalid monitor)");
509      return;
510   }
511
512   /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
513    *  called when a performance monitor is already active."
514    */
515   if (m->Active) {
516      _mesa_error(ctx, GL_INVALID_OPERATION,
517                  "glBeginPerfMonitor(already active)");
518      return;
519   }
520
521   /* The driver is free to return false if it can't begin monitoring for
522    * any reason.  This translates into an INVALID_OPERATION error.
523    */
524   if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
525      m->Active = true;
526      m->Ended = false;
527   } else {
528      _mesa_error(ctx, GL_INVALID_OPERATION,
529                  "glBeginPerfMonitor(driver unable to begin monitoring)");
530   }
531}
532
533void GLAPIENTRY
534_mesa_EndPerfMonitorAMD(GLuint monitor)
535{
536   GET_CURRENT_CONTEXT(ctx);
537
538   struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
539
540   if (m == NULL) {
541      _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
542      return;
543   }
544
545   /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
546    *  when a performance monitor is not currently started."
547    */
548   if (!m->Active) {
549      _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfMonitor(not active)");
550      return;
551   }
552
553   ctx->Driver.EndPerfMonitor(ctx, m);
554
555   m->Active = false;
556   m->Ended = true;
557}
558
559/**
560 * Return the number of bytes needed to store a monitor's result.
561 */
562static unsigned
563perf_monitor_result_size(const struct gl_context *ctx,
564                         const struct gl_perf_monitor_object *m)
565{
566   unsigned group, counter;
567   unsigned size = 0;
568
569   for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
570      const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
571      BITSET_WORD tmp;
572
573      BITSET_FOREACH_SET(counter, tmp, m->ActiveCounters[group], g->NumCounters) {
574         const struct gl_perf_monitor_counter *c = &g->Counters[counter];
575
576         size += sizeof(uint32_t); /* Group ID */
577         size += sizeof(uint32_t); /* Counter ID */
578         size += _mesa_perf_monitor_counter_size(c);
579      }
580   }
581   return size;
582}
583
584void GLAPIENTRY
585_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
586                                   GLsizei dataSize, GLuint *data,
587                                   GLint *bytesWritten)
588{
589   GET_CURRENT_CONTEXT(ctx);
590
591   struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
592   bool result_available;
593
594   if (m == NULL) {
595      _mesa_error(ctx, GL_INVALID_VALUE,
596                  "glGetPerfMonitorCounterDataAMD(invalid monitor)");
597      return;
598   }
599
600   /* "It is an INVALID_OPERATION error for <data> to be NULL." */
601   if (data == NULL) {
602      _mesa_error(ctx, GL_INVALID_OPERATION,
603                  "glGetPerfMonitorCounterDataAMD(data == NULL)");
604      return;
605   }
606
607   /* We need at least enough room for a single value. */
608   if (dataSize < sizeof(GLuint)) {
609      if (bytesWritten != NULL)
610         *bytesWritten = 0;
611      return;
612   }
613
614   /* If the monitor has never ended, there is no result. */
615   result_available = m->Ended &&
616      ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
617
618   /* AMD appears to return 0 for all queries unless a result is available. */
619   if (!result_available) {
620      *data = 0;
621      if (bytesWritten != NULL)
622         *bytesWritten = sizeof(GLuint);
623      return;
624   }
625
626   switch (pname) {
627   case GL_PERFMON_RESULT_AVAILABLE_AMD:
628      *data = 1;
629      if (bytesWritten != NULL)
630         *bytesWritten = sizeof(GLuint);
631      break;
632   case GL_PERFMON_RESULT_SIZE_AMD:
633      *data = perf_monitor_result_size(ctx, m);
634      if (bytesWritten != NULL)
635         *bytesWritten = sizeof(GLuint);
636      break;
637   case GL_PERFMON_RESULT_AMD:
638      ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
639      break;
640   default:
641      _mesa_error(ctx, GL_INVALID_ENUM,
642                  "glGetPerfMonitorCounterDataAMD(pname)");
643   }
644}
645
646/**
647 * Returns how many bytes a counter's value takes up.
648 */
649unsigned
650_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
651{
652   switch (c->Type) {
653   case GL_FLOAT:
654   case GL_PERCENTAGE_AMD:
655      return sizeof(GLfloat);
656   case GL_UNSIGNED_INT:
657      return sizeof(GLuint);
658   case GL_UNSIGNED_INT64_AMD:
659      return sizeof(uint64_t);
660   default:
661      assert(!"Should not get here: invalid counter type");
662      return 0;
663   }
664}
665