meson: add support for xlib glx
[mesa.git] / src / mesa / main / performance_monitor.c
1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * \file performance_monitor.c
26 * Core Mesa support for the AMD_performance_monitor extension.
27 *
28 * In order to implement this extension, start by defining two enums:
29 * one for Groups, and one for Counters. These will be used as indexes into
30 * arrays, so they should start at 0 and increment from there.
31 *
32 * Counter IDs need to be globally unique. That is, you can't have counter 7
33 * in group A and counter 7 in group B. A global enum of all available
34 * counters is a convenient way to guarantee this.
35 */
36
37 #include <stdbool.h>
38 #include "glheader.h"
39 #include "context.h"
40 #include "enums.h"
41 #include "hash.h"
42 #include "macros.h"
43 #include "mtypes.h"
44 #include "performance_monitor.h"
45 #include "util/bitset.h"
46 #include "util/ralloc.h"
47
48 void
49 _mesa_init_performance_monitors(struct gl_context *ctx)
50 {
51 ctx->PerfMonitor.Monitors = _mesa_NewHashTable();
52 ctx->PerfMonitor.NumGroups = 0;
53 ctx->PerfMonitor.Groups = NULL;
54 }
55
56 static inline void
57 init_groups(struct gl_context *ctx)
58 {
59 if (unlikely(!ctx->PerfMonitor.Groups))
60 ctx->Driver.InitPerfMonitorGroups(ctx);
61 }
62
63 static struct gl_perf_monitor_object *
64 new_performance_monitor(struct gl_context *ctx, GLuint index)
65 {
66 unsigned i;
67 struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx);
68
69 if (m == NULL)
70 return NULL;
71
72 m->Name = index;
73
74 m->Active = false;
75
76 m->ActiveGroups =
77 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
78
79 m->ActiveCounters =
80 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
81
82 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
83 goto fail;
84
85 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
86 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
87
88 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
89 BITSET_WORDS(g->NumCounters));
90 if (m->ActiveCounters[i] == NULL)
91 goto fail;
92 }
93
94 return m;
95
96 fail:
97 ralloc_free(m->ActiveGroups);
98 ralloc_free(m->ActiveCounters);
99 ctx->Driver.DeletePerfMonitor(ctx, m);
100 return NULL;
101 }
102
103 static void
104 free_performance_monitor(GLuint key, void *data, void *user)
105 {
106 struct gl_perf_monitor_object *m = data;
107 struct gl_context *ctx = user;
108
109 ralloc_free(m->ActiveGroups);
110 ralloc_free(m->ActiveCounters);
111 ctx->Driver.DeletePerfMonitor(ctx, m);
112 }
113
114 void
115 _mesa_free_performance_monitors(struct gl_context *ctx)
116 {
117 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
118 free_performance_monitor, ctx);
119 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
120 }
121
122 static inline struct gl_perf_monitor_object *
123 lookup_monitor(struct gl_context *ctx, GLuint id)
124 {
125 return (struct gl_perf_monitor_object *)
126 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
127 }
128
129 static inline const struct gl_perf_monitor_group *
130 get_group(const struct gl_context *ctx, GLuint id)
131 {
132 if (id >= ctx->PerfMonitor.NumGroups)
133 return NULL;
134
135 return &ctx->PerfMonitor.Groups[id];
136 }
137
138 static inline const struct gl_perf_monitor_counter *
139 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
140 {
141 if (id >= group_obj->NumCounters)
142 return NULL;
143
144 return &group_obj->Counters[id];
145 }
146
147 /*****************************************************************************/
148
149 void 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
169 void 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
202 void 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
231 void 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
273 void 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
338 void 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
379 void 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
421 void 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 ++m->ActiveGroups[group];
484 BITSET_SET(m->ActiveCounters[group], counterList[i]);
485 }
486 } else {
487 /* Disable the counters */
488 for (i = 0; i < numCounters; i++) {
489 --m->ActiveGroups[group];
490 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
491 }
492 }
493 }
494
495 void GLAPIENTRY
496 _mesa_BeginPerfMonitorAMD(GLuint monitor)
497 {
498 GET_CURRENT_CONTEXT(ctx);
499
500 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
501
502 if (m == NULL) {
503 _mesa_error(ctx, GL_INVALID_VALUE,
504 "glBeginPerfMonitorAMD(invalid monitor)");
505 return;
506 }
507
508 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
509 * called when a performance monitor is already active."
510 */
511 if (m->Active) {
512 _mesa_error(ctx, GL_INVALID_OPERATION,
513 "glBeginPerfMonitor(already active)");
514 return;
515 }
516
517 /* The driver is free to return false if it can't begin monitoring for
518 * any reason. This translates into an INVALID_OPERATION error.
519 */
520 if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
521 m->Active = true;
522 m->Ended = false;
523 } else {
524 _mesa_error(ctx, GL_INVALID_OPERATION,
525 "glBeginPerfMonitor(driver unable to begin monitoring)");
526 }
527 }
528
529 void GLAPIENTRY
530 _mesa_EndPerfMonitorAMD(GLuint monitor)
531 {
532 GET_CURRENT_CONTEXT(ctx);
533
534 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
535
536 if (m == NULL) {
537 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
538 return;
539 }
540
541 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
542 * when a performance monitor is not currently started."
543 */
544 if (!m->Active) {
545 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)");
546 return;
547 }
548
549 ctx->Driver.EndPerfMonitor(ctx, m);
550
551 m->Active = false;
552 m->Ended = true;
553 }
554
555 /**
556 * Return the number of bytes needed to store a monitor's result.
557 */
558 static unsigned
559 perf_monitor_result_size(const struct gl_context *ctx,
560 const struct gl_perf_monitor_object *m)
561 {
562 unsigned group, counter;
563 unsigned size = 0;
564
565 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
566 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
567 BITSET_WORD tmp;
568
569 BITSET_FOREACH_SET(counter, tmp, m->ActiveCounters[group], g->NumCounters) {
570 const struct gl_perf_monitor_counter *c = &g->Counters[counter];
571
572 size += sizeof(uint32_t); /* Group ID */
573 size += sizeof(uint32_t); /* Counter ID */
574 size += _mesa_perf_monitor_counter_size(c);
575 }
576 }
577 return size;
578 }
579
580 void GLAPIENTRY
581 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
582 GLsizei dataSize, GLuint *data,
583 GLint *bytesWritten)
584 {
585 GET_CURRENT_CONTEXT(ctx);
586
587 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
588 bool result_available;
589
590 if (m == NULL) {
591 _mesa_error(ctx, GL_INVALID_VALUE,
592 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
593 return;
594 }
595
596 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
597 if (data == NULL) {
598 _mesa_error(ctx, GL_INVALID_OPERATION,
599 "glGetPerfMonitorCounterDataAMD(data == NULL)");
600 return;
601 }
602
603 /* We need at least enough room for a single value. */
604 if (dataSize < sizeof(GLuint)) {
605 if (bytesWritten != NULL)
606 *bytesWritten = 0;
607 return;
608 }
609
610 /* If the monitor has never ended, there is no result. */
611 result_available = m->Ended &&
612 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
613
614 /* AMD appears to return 0 for all queries unless a result is available. */
615 if (!result_available) {
616 *data = 0;
617 if (bytesWritten != NULL)
618 *bytesWritten = sizeof(GLuint);
619 return;
620 }
621
622 switch (pname) {
623 case GL_PERFMON_RESULT_AVAILABLE_AMD:
624 *data = 1;
625 if (bytesWritten != NULL)
626 *bytesWritten = sizeof(GLuint);
627 break;
628 case GL_PERFMON_RESULT_SIZE_AMD:
629 *data = perf_monitor_result_size(ctx, m);
630 if (bytesWritten != NULL)
631 *bytesWritten = sizeof(GLuint);
632 break;
633 case GL_PERFMON_RESULT_AMD:
634 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
635 break;
636 default:
637 _mesa_error(ctx, GL_INVALID_ENUM,
638 "glGetPerfMonitorCounterDataAMD(pname)");
639 }
640 }
641
642 /**
643 * Returns how many bytes a counter's value takes up.
644 */
645 unsigned
646 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
647 {
648 switch (c->Type) {
649 case GL_FLOAT:
650 case GL_PERCENTAGE_AMD:
651 return sizeof(GLfloat);
652 case GL_UNSIGNED_INT:
653 return sizeof(GLuint);
654 case GL_UNSIGNED_INT64_AMD:
655 return sizeof(uint64_t);
656 default:
657 assert(!"Should not get here: invalid counter type");
658 return 0;
659 }
660 }