• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
_mesa_init_performance_monitors(struct gl_context * ctx)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
init_groups(struct gl_context * ctx)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 *
new_performance_monitor(struct gl_context * ctx,GLuint index)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
free_performance_monitor(GLuint key,void * data,void * user)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
_mesa_free_performance_monitors(struct gl_context * ctx)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 *
lookup_monitor(struct gl_context * ctx,GLuint id)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 *
get_group(const struct gl_context * ctx,GLuint id)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 *
get_counter(const struct gl_perf_monitor_group * group_obj,GLuint id)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 /* For INTEL_performance_query, query id 0 is reserved to be invalid. We use
148  * index to Groups array + 1 as the query id. Same applies to counter id.
149  */
150 static inline GLuint
queryid_to_index(GLuint queryid)151 queryid_to_index(GLuint queryid)
152 {
153    return queryid - 1;
154 }
155 
156 static inline GLuint
index_to_queryid(GLuint index)157 index_to_queryid(GLuint index)
158 {
159    return index + 1;
160 }
161 
162 static inline bool
queryid_valid(const struct gl_context * ctx,GLuint queryid)163 queryid_valid(const struct gl_context *ctx, GLuint queryid)
164 {
165    return get_group(ctx, queryid_to_index(queryid)) != NULL;
166 }
167 
168 static inline GLuint
counterid_to_index(GLuint counterid)169 counterid_to_index(GLuint counterid)
170 {
171    return counterid - 1;
172 }
173 
174 /*****************************************************************************/
175 
176 void GLAPIENTRY
_mesa_GetPerfMonitorGroupsAMD(GLint * numGroups,GLsizei groupsSize,GLuint * groups)177 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
178                               GLuint *groups)
179 {
180    GET_CURRENT_CONTEXT(ctx);
181    init_groups(ctx);
182 
183    if (numGroups != NULL)
184       *numGroups = ctx->PerfMonitor.NumGroups;
185 
186    if (groupsSize > 0 && groups != NULL) {
187       unsigned i;
188       unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
189 
190       /* We just use the index in the Groups array as the ID. */
191       for (i = 0; i < n; i++)
192          groups[i] = i;
193    }
194 }
195 
196 void GLAPIENTRY
_mesa_GetPerfMonitorCountersAMD(GLuint group,GLint * numCounters,GLint * maxActiveCounters,GLsizei countersSize,GLuint * counters)197 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
198                                 GLint *maxActiveCounters,
199                                 GLsizei countersSize, GLuint *counters)
200 {
201    GET_CURRENT_CONTEXT(ctx);
202    const struct gl_perf_monitor_group *group_obj;
203 
204    init_groups(ctx);
205 
206    group_obj = get_group(ctx, group);
207    if (group_obj == NULL) {
208       _mesa_error(ctx, GL_INVALID_VALUE,
209                   "glGetPerfMonitorCountersAMD(invalid group)");
210       return;
211    }
212 
213    if (maxActiveCounters != NULL)
214       *maxActiveCounters = group_obj->MaxActiveCounters;
215 
216    if (numCounters != NULL)
217       *numCounters = group_obj->NumCounters;
218 
219    if (counters != NULL) {
220       unsigned i;
221       unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
222       for (i = 0; i < n; i++) {
223          /* We just use the index in the Counters array as the ID. */
224          counters[i] = i;
225       }
226    }
227 }
228 
229 void GLAPIENTRY
_mesa_GetPerfMonitorGroupStringAMD(GLuint group,GLsizei bufSize,GLsizei * length,GLchar * groupString)230 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
231                                    GLsizei *length, GLchar *groupString)
232 {
233    GET_CURRENT_CONTEXT(ctx);
234    const struct gl_perf_monitor_group *group_obj;
235 
236    init_groups(ctx);
237 
238    group_obj = get_group(ctx, group);
239    if (group_obj == NULL) {
240       _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
241       return;
242    }
243 
244    if (bufSize == 0) {
245       /* Return the number of characters that would be required to hold the
246        * group string, excluding the null terminator.
247        */
248       if (length != NULL)
249          *length = strlen(group_obj->Name);
250    } else {
251       if (length != NULL)
252          *length = MIN2(strlen(group_obj->Name), bufSize);
253       if (groupString != NULL)
254          strncpy(groupString, group_obj->Name, bufSize);
255    }
256 }
257 
258 void GLAPIENTRY
_mesa_GetPerfMonitorCounterStringAMD(GLuint group,GLuint counter,GLsizei bufSize,GLsizei * length,GLchar * counterString)259 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
260                                      GLsizei bufSize, GLsizei *length,
261                                      GLchar *counterString)
262 {
263    GET_CURRENT_CONTEXT(ctx);
264 
265    const struct gl_perf_monitor_group *group_obj;
266    const struct gl_perf_monitor_counter *counter_obj;
267 
268    init_groups(ctx);
269 
270    group_obj = get_group(ctx, group);
271 
272    if (group_obj == NULL) {
273       _mesa_error(ctx, GL_INVALID_VALUE,
274                   "glGetPerfMonitorCounterStringAMD(invalid group)");
275       return;
276    }
277 
278    counter_obj = get_counter(group_obj, counter);
279 
280    if (counter_obj == NULL) {
281       _mesa_error(ctx, GL_INVALID_VALUE,
282                   "glGetPerfMonitorCounterStringAMD(invalid counter)");
283       return;
284    }
285 
286    if (bufSize == 0) {
287       /* Return the number of characters that would be required to hold the
288        * counter string, excluding the null terminator.
289        */
290       if (length != NULL)
291          *length = strlen(counter_obj->Name);
292    } else {
293       if (length != NULL)
294          *length = MIN2(strlen(counter_obj->Name), bufSize);
295       if (counterString != NULL)
296          strncpy(counterString, counter_obj->Name, bufSize);
297    }
298 }
299 
300 void GLAPIENTRY
_mesa_GetPerfMonitorCounterInfoAMD(GLuint group,GLuint counter,GLenum pname,GLvoid * data)301 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
302                                    GLvoid *data)
303 {
304    GET_CURRENT_CONTEXT(ctx);
305 
306    const struct gl_perf_monitor_group *group_obj;
307    const struct gl_perf_monitor_counter *counter_obj;
308 
309    init_groups(ctx);
310 
311    group_obj = get_group(ctx, group);
312 
313    if (group_obj == NULL) {
314       _mesa_error(ctx, GL_INVALID_VALUE,
315                   "glGetPerfMonitorCounterInfoAMD(invalid group)");
316       return;
317    }
318 
319    counter_obj = get_counter(group_obj, counter);
320 
321    if (counter_obj == NULL) {
322       _mesa_error(ctx, GL_INVALID_VALUE,
323                   "glGetPerfMonitorCounterInfoAMD(invalid counter)");
324       return;
325    }
326 
327    switch (pname) {
328    case GL_COUNTER_TYPE_AMD:
329       *((GLenum *) data) = counter_obj->Type;
330       break;
331 
332    case GL_COUNTER_RANGE_AMD:
333       switch (counter_obj->Type) {
334       case GL_FLOAT:
335       case GL_PERCENTAGE_AMD: {
336          float *f_data = data;
337          f_data[0] = counter_obj->Minimum.f;
338          f_data[1] = counter_obj->Maximum.f;
339          break;
340       }
341       case GL_UNSIGNED_INT: {
342          uint32_t *u32_data = data;
343          u32_data[0] = counter_obj->Minimum.u32;
344          u32_data[1] = counter_obj->Maximum.u32;
345          break;
346       }
347       case GL_UNSIGNED_INT64_AMD: {
348          uint64_t *u64_data = data;
349          u64_data[0] = counter_obj->Minimum.u64;
350          u64_data[1] = counter_obj->Maximum.u64;
351          break;
352       }
353       default:
354          assert(!"Should not get here: invalid counter type");
355       }
356       break;
357 
358    default:
359       _mesa_error(ctx, GL_INVALID_ENUM,
360                   "glGetPerfMonitorCounterInfoAMD(pname)");
361       return;
362    }
363 }
364 
365 void GLAPIENTRY
_mesa_GenPerfMonitorsAMD(GLsizei n,GLuint * monitors)366 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
367 {
368    GLuint first;
369    GET_CURRENT_CONTEXT(ctx);
370 
371    if (MESA_VERBOSE & VERBOSE_API)
372       _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
373 
374    init_groups(ctx);
375 
376    if (n < 0) {
377       _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
378       return;
379    }
380 
381    if (monitors == NULL)
382       return;
383 
384    /* We don't actually need them to be contiguous, but this is what
385     * the rest of Mesa does, so we may as well.
386     */
387    first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
388    if (first) {
389       GLsizei i;
390       for (i = 0; i < n; i++) {
391          struct gl_perf_monitor_object *m =
392             new_performance_monitor(ctx, first + i);
393          if (!m) {
394             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
395             return;
396          }
397          monitors[i] = first + i;
398          _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
399       }
400    } else {
401       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
402       return;
403    }
404 }
405 
406 void GLAPIENTRY
_mesa_DeletePerfMonitorsAMD(GLsizei n,GLuint * monitors)407 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
408 {
409    GLint i;
410    GET_CURRENT_CONTEXT(ctx);
411 
412    if (MESA_VERBOSE & VERBOSE_API)
413       _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
414 
415    if (n < 0) {
416       _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
417       return;
418    }
419 
420    if (monitors == NULL)
421       return;
422 
423    for (i = 0; i < n; i++) {
424       struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
425 
426       if (m) {
427          /* Give the driver a chance to stop the monitor if it's active. */
428          if (m->Active) {
429             ctx->Driver.ResetPerfMonitor(ctx, m);
430             m->Ended = false;
431          }
432 
433          _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
434          ralloc_free(m->ActiveGroups);
435          ralloc_free(m->ActiveCounters);
436          ctx->Driver.DeletePerfMonitor(ctx, m);
437       } else {
438          /* "INVALID_VALUE error will be generated if any of the monitor IDs
439           *  in the <monitors> parameter to DeletePerfMonitorsAMD do not
440           *  reference a valid generated monitor ID."
441           */
442          _mesa_error(ctx, GL_INVALID_VALUE,
443                      "glDeletePerfMonitorsAMD(invalid monitor)");
444       }
445    }
446 }
447 
448 void GLAPIENTRY
_mesa_SelectPerfMonitorCountersAMD(GLuint monitor,GLboolean enable,GLuint group,GLint numCounters,GLuint * counterList)449 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
450                                    GLuint group, GLint numCounters,
451                                    GLuint *counterList)
452 {
453    GET_CURRENT_CONTEXT(ctx);
454    int i;
455    struct gl_perf_monitor_object *m;
456    const struct gl_perf_monitor_group *group_obj;
457 
458    m = lookup_monitor(ctx, monitor);
459 
460    /* "INVALID_VALUE error will be generated if the <monitor> parameter to
461     *  SelectPerfMonitorCountersAMD does not reference a monitor created by
462     *  GenPerfMonitorsAMD."
463     */
464    if (m == NULL) {
465       _mesa_error(ctx, GL_INVALID_VALUE,
466                   "glSelectPerfMonitorCountersAMD(invalid monitor)");
467       return;
468    }
469 
470    group_obj = get_group(ctx, group);
471 
472    /* "INVALID_VALUE error will be generated if the <group> parameter to
473     *  GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
474     *  GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
475     *  SelectPerfMonitorCountersAMD does not reference a valid group ID."
476     */
477    if (group_obj == NULL) {
478       _mesa_error(ctx, GL_INVALID_VALUE,
479                   "glSelectPerfMonitorCountersAMD(invalid group)");
480       return;
481    }
482 
483    /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
484     *  SelectPerfMonitorCountersAMD is less than 0."
485     */
486    if (numCounters < 0) {
487       _mesa_error(ctx, GL_INVALID_VALUE,
488                   "glSelectPerfMonitorCountersAMD(numCounters < 0)");
489       return;
490    }
491 
492    /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
493     *  results for that monitor become invalidated and the result queries
494     *  PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
495     */
496    ctx->Driver.ResetPerfMonitor(ctx, m);
497 
498    /* Sanity check the counter ID list. */
499    for (i = 0; i < numCounters; i++) {
500       if (counterList[i] >= group_obj->NumCounters) {
501          _mesa_error(ctx, GL_INVALID_VALUE,
502                      "glSelectPerfMonitorCountersAMD(invalid counter ID)");
503          return;
504       }
505    }
506 
507    if (enable) {
508       /* Enable the counters */
509       for (i = 0; i < numCounters; i++) {
510          ++m->ActiveGroups[group];
511          BITSET_SET(m->ActiveCounters[group], counterList[i]);
512       }
513    } else {
514       /* Disable the counters */
515       for (i = 0; i < numCounters; i++) {
516          --m->ActiveGroups[group];
517          BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
518       }
519    }
520 }
521 
522 void GLAPIENTRY
_mesa_BeginPerfMonitorAMD(GLuint monitor)523 _mesa_BeginPerfMonitorAMD(GLuint monitor)
524 {
525    GET_CURRENT_CONTEXT(ctx);
526 
527    struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
528 
529    if (m == NULL) {
530       _mesa_error(ctx, GL_INVALID_VALUE,
531                   "glBeginPerfMonitorAMD(invalid monitor)");
532       return;
533    }
534 
535    /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
536     *  called when a performance monitor is already active."
537     */
538    if (m->Active) {
539       _mesa_error(ctx, GL_INVALID_OPERATION,
540                   "glBeginPerfMonitor(already active)");
541       return;
542    }
543 
544    /* The driver is free to return false if it can't begin monitoring for
545     * any reason.  This translates into an INVALID_OPERATION error.
546     */
547    if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
548       m->Active = true;
549       m->Ended = false;
550    } else {
551       _mesa_error(ctx, GL_INVALID_OPERATION,
552                   "glBeginPerfMonitor(driver unable to begin monitoring)");
553    }
554 }
555 
556 void GLAPIENTRY
_mesa_EndPerfMonitorAMD(GLuint monitor)557 _mesa_EndPerfMonitorAMD(GLuint monitor)
558 {
559    GET_CURRENT_CONTEXT(ctx);
560 
561    struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
562 
563    if (m == NULL) {
564       _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
565       return;
566    }
567 
568    /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
569     *  when a performance monitor is not currently started."
570     */
571    if (!m->Active) {
572       _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)");
573       return;
574    }
575 
576    ctx->Driver.EndPerfMonitor(ctx, m);
577 
578    m->Active = false;
579    m->Ended = true;
580 }
581 
582 /**
583  * Return the number of bytes needed to store a monitor's result.
584  */
585 static unsigned
perf_monitor_result_size(const struct gl_context * ctx,const struct gl_perf_monitor_object * m)586 perf_monitor_result_size(const struct gl_context *ctx,
587                          const struct gl_perf_monitor_object *m)
588 {
589    unsigned group, counter;
590    unsigned size = 0;
591 
592    for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
593       const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
594       BITSET_WORD tmp;
595 
596       BITSET_FOREACH_SET(counter, tmp, m->ActiveCounters[group], g->NumCounters) {
597          const struct gl_perf_monitor_counter *c = &g->Counters[counter];
598 
599          size += sizeof(uint32_t); /* Group ID */
600          size += sizeof(uint32_t); /* Counter ID */
601          size += _mesa_perf_monitor_counter_size(c);
602       }
603    }
604    return size;
605 }
606 
607 void GLAPIENTRY
_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor,GLenum pname,GLsizei dataSize,GLuint * data,GLint * bytesWritten)608 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
609                                    GLsizei dataSize, GLuint *data,
610                                    GLint *bytesWritten)
611 {
612    GET_CURRENT_CONTEXT(ctx);
613 
614    struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
615    bool result_available;
616 
617    if (m == NULL) {
618       _mesa_error(ctx, GL_INVALID_VALUE,
619                   "glGetPerfMonitorCounterDataAMD(invalid monitor)");
620       return;
621    }
622 
623    /* "It is an INVALID_OPERATION error for <data> to be NULL." */
624    if (data == NULL) {
625       _mesa_error(ctx, GL_INVALID_OPERATION,
626                   "glGetPerfMonitorCounterDataAMD(data == NULL)");
627       return;
628    }
629 
630    /* We need at least enough room for a single value. */
631    if (dataSize < sizeof(GLuint)) {
632       if (bytesWritten != NULL)
633          *bytesWritten = 0;
634       return;
635    }
636 
637    /* If the monitor has never ended, there is no result. */
638    result_available = m->Ended &&
639       ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
640 
641    /* AMD appears to return 0 for all queries unless a result is available. */
642    if (!result_available) {
643       *data = 0;
644       if (bytesWritten != NULL)
645          *bytesWritten = sizeof(GLuint);
646       return;
647    }
648 
649    switch (pname) {
650    case GL_PERFMON_RESULT_AVAILABLE_AMD:
651       *data = 1;
652       if (bytesWritten != NULL)
653          *bytesWritten = sizeof(GLuint);
654       break;
655    case GL_PERFMON_RESULT_SIZE_AMD:
656       *data = perf_monitor_result_size(ctx, m);
657       if (bytesWritten != NULL)
658          *bytesWritten = sizeof(GLuint);
659       break;
660    case GL_PERFMON_RESULT_AMD:
661       ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
662       break;
663    default:
664       _mesa_error(ctx, GL_INVALID_ENUM,
665                   "glGetPerfMonitorCounterDataAMD(pname)");
666    }
667 }
668 
669 /**
670  * Returns how many bytes a counter's value takes up.
671  */
672 unsigned
_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter * c)673 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
674 {
675    switch (c->Type) {
676    case GL_FLOAT:
677    case GL_PERCENTAGE_AMD:
678       return sizeof(GLfloat);
679    case GL_UNSIGNED_INT:
680       return sizeof(GLuint);
681    case GL_UNSIGNED_INT64_AMD:
682       return sizeof(uint64_t);
683    default:
684       assert(!"Should not get here: invalid counter type");
685       return 0;
686    }
687 }
688 
689 extern void GLAPIENTRY
_mesa_GetFirstPerfQueryIdINTEL(GLuint * queryId)690 _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
691 {
692    GET_CURRENT_CONTEXT(ctx);
693    unsigned numGroups;
694 
695    init_groups(ctx);
696 
697    /* The GL_INTEL_performance_query spec says:
698     *
699     *    "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
700     */
701    if (!queryId) {
702       _mesa_error(ctx, GL_INVALID_VALUE,
703                   "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
704       return;
705    }
706 
707    numGroups = ctx->PerfMonitor.NumGroups;
708 
709    /* The GL_INTEL_performance_query spec says:
710     *
711     *    "If the given hardware platform doesn't support any performance
712     *    queries, then the value of 0 is returned and INVALID_OPERATION error
713     *    is raised."
714     */
715    if (numGroups == 0) {
716       *queryId = 0;
717       _mesa_error(ctx, GL_INVALID_OPERATION,
718                   "glGetFirstPerfQueryIdINTEL(no queries supported)");
719       return;
720    }
721 
722    *queryId = index_to_queryid(0);
723 }
724 
725 extern void GLAPIENTRY
_mesa_GetNextPerfQueryIdINTEL(GLuint queryId,GLuint * nextQueryId)726 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId)
727 {
728    GET_CURRENT_CONTEXT(ctx);
729    init_groups(ctx);
730 
731    /* The GL_INTEL_performance_query spec says:
732     *
733     *    "The result is passed in location pointed by nextQueryId. If query
734     *    identified by queryId is the last query available the value of 0 is
735     *    returned. If the specified performance query identifier is invalid
736     *    then INVALID_VALUE error is generated. If nextQueryId pointer is
737     *    equal to 0, an INVALID_VALUE error is generated.  Whenever error is
738     *    generated, the value of 0 is returned."
739     */
740 
741    if (!nextQueryId) {
742       _mesa_error(ctx, GL_INVALID_VALUE,
743                   "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
744       return;
745    }
746 
747    if (!queryid_valid(ctx, queryId)) {
748       *nextQueryId = 0;
749       _mesa_error(ctx, GL_INVALID_VALUE,
750                   "glGetNextPerfQueryIdINTEL(invalid query)");
751       return;
752    }
753 
754    ++queryId;
755 
756    if (!queryid_valid(ctx, queryId)) {
757       *nextQueryId = 0;
758    } else {
759       *nextQueryId = queryId;
760    }
761 }
762 
763 extern void GLAPIENTRY
_mesa_GetPerfQueryIdByNameINTEL(char * queryName,GLuint * queryId)764 _mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId)
765 {
766    GET_CURRENT_CONTEXT(ctx);
767    unsigned i;
768 
769    init_groups(ctx);
770 
771    /* The GL_INTEL_performance_query spec says:
772     *
773     *    "If queryName does not reference a valid query name, an INVALID_VALUE
774     *    error is generated."
775     */
776    if (!queryName) {
777       _mesa_error(ctx, GL_INVALID_VALUE,
778                   "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
779       return;
780    }
781 
782    /* The specification does not state that this produces an error. */
783    if (!queryId) {
784       _mesa_warning(ctx, "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
785       return;
786    }
787 
788    for (i = 0; i < ctx->PerfMonitor.NumGroups; ++i) {
789       const struct gl_perf_monitor_group *group_obj = get_group(ctx, i);
790       if (strcmp(group_obj->Name, queryName) == 0) {
791          *queryId = index_to_queryid(i);
792          return;
793       }
794    }
795 
796    _mesa_error(ctx, GL_INVALID_VALUE,
797                "glGetPerfQueryIdByNameINTEL(invalid query name)");
798 }
799 
800 extern void GLAPIENTRY
_mesa_GetPerfQueryInfoINTEL(GLuint queryId,GLuint queryNameLength,char * queryName,GLuint * dataSize,GLuint * noCounters,GLuint * noActiveInstances,GLuint * capsMask)801 _mesa_GetPerfQueryInfoINTEL(GLuint queryId,
802                             GLuint queryNameLength, char *queryName,
803                             GLuint *dataSize, GLuint *noCounters,
804                             GLuint *noActiveInstances,
805                             GLuint *capsMask)
806 {
807    GET_CURRENT_CONTEXT(ctx);
808    unsigned i;
809 
810    const struct gl_perf_monitor_group *group_obj;
811 
812    init_groups(ctx);
813 
814    group_obj = get_group(ctx, queryid_to_index(queryId));
815    if (group_obj == NULL) {
816       /* The GL_INTEL_performance_query spec says:
817        *
818        *    "If queryId does not reference a valid query type, an
819        *    INVALID_VALUE error is generated."
820        */
821       _mesa_error(ctx, GL_INVALID_VALUE,
822                   "glGetPerfQueryInfoINTEL(invalid query)");
823       return;
824    }
825 
826    if (queryName) {
827       strncpy(queryName, group_obj->Name, queryNameLength);
828 
829       /* No specification given about whether the string needs to be
830        * zero-terminated. Zero-terminate the string always as we don't
831        * otherwise communicate the length of the returned string.
832        */
833       if (queryNameLength > 0) {
834          queryName[queryNameLength - 1] = '\0';
835       }
836    }
837 
838    if (dataSize) {
839       unsigned size = 0;
840       for (i = 0; i < group_obj->NumCounters; ++i) {
841          /* What we get from the driver is group id (uint32_t) + counter id
842           * (uint32_t) + value.
843           */
844          size += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]);
845       }
846       *dataSize = size;
847    }
848 
849    if (noCounters) {
850       *noCounters = group_obj->NumCounters;
851    }
852 
853    /* The GL_INTEL_performance_query spec says:
854     *
855     *    "-- the actual number of already created query instances in
856     *    maxInstances location"
857     *
858     * 1) Typo in the specification, should be noActiveInstances.
859     * 2) Another typo in the specification, maxInstances parameter is not listed
860     *    in the declaration of this function in the list of new functions.
861     */
862    if (noActiveInstances) {
863       *noActiveInstances = _mesa_HashNumEntries(ctx->PerfMonitor.Monitors);
864    }
865 
866    if (capsMask) {
867       /* TODO: This information not yet available in the monitor structs. For
868        * now, we hardcode SINGLE_CONTEXT, since that's what the implementation
869        * currently tries very hard to do.
870        */
871       *capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL;
872    }
873 }
874 
875 extern void GLAPIENTRY
_mesa_GetPerfCounterInfoINTEL(GLuint queryId,GLuint counterId,GLuint counterNameLength,char * counterName,GLuint counterDescLength,char * counterDesc,GLuint * counterOffset,GLuint * counterDataSize,GLuint * counterTypeEnum,GLuint * counterDataTypeEnum,GLuint64 * rawCounterMaxValue)876 _mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId,
877                               GLuint counterNameLength, char *counterName,
878                               GLuint counterDescLength, char *counterDesc,
879                               GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum,
880                               GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue)
881 {
882    GET_CURRENT_CONTEXT(ctx);
883 
884    const struct gl_perf_monitor_group *group_obj;
885    const struct gl_perf_monitor_counter *counter_obj;
886    unsigned counterIndex;
887    unsigned i;
888 
889    init_groups(ctx);
890 
891    group_obj = get_group(ctx, queryid_to_index(queryId));
892 
893    /* The GL_INTEL_performance_query spec says:
894     *
895     *    "If the pair of queryId and counterId does not reference a valid
896     *    counter, an INVALID_VALUE error is generated."
897     */
898    if (group_obj == NULL) {
899       _mesa_error(ctx, GL_INVALID_VALUE,
900                   "glGetPerfCounterInfoINTEL(invalid queryId)");
901       return;
902    }
903 
904    counterIndex = counterid_to_index(counterId);
905    counter_obj = get_counter(group_obj, counterIndex);
906 
907    if (counter_obj == NULL) {
908       _mesa_error(ctx, GL_INVALID_VALUE,
909                   "glGetPerfCounterInfoINTEL(invalid counterId)");
910       return;
911    }
912 
913    if (counterName) {
914       strncpy(counterName, counter_obj->Name, counterNameLength);
915 
916       /* No specification given about whether the string needs to be
917        * zero-terminated. Zero-terminate the string always as we don't
918        * otherwise communicate the length of the returned string.
919        */
920       if (counterNameLength > 0) {
921          counterName[counterNameLength - 1] = '\0';
922       }
923    }
924 
925    if (counterDesc) {
926       /* TODO: No separate description text at the moment. We pass the name
927        * again for the moment.
928        */
929       strncpy(counterDesc, counter_obj->Name, counterDescLength);
930 
931       /* No specification given about whether the string needs to be
932        * zero-terminated. Zero-terminate the string always as we don't
933        * otherwise communicate the length of the returned string.
934        */
935       if (counterDescLength > 0) {
936          counterDesc[counterDescLength - 1] = '\0';
937       }
938    }
939 
940    if (counterOffset) {
941       unsigned offset = 0;
942       for (i = 0; i < counterIndex; ++i) {
943          /* What we get from the driver is group id (uint32_t) + counter id
944           * (uint32_t) + value.
945           */
946          offset += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]);
947       }
948       *counterOffset = 2 * sizeof(uint32_t) + offset;
949    }
950 
951    if (counterDataSize) {
952       *counterDataSize = _mesa_perf_monitor_counter_size(counter_obj);
953    }
954 
955    if (counterTypeEnum) {
956       /* TODO: Different counter types (semantic type, not data type) not
957        * supported as of yet.
958        */
959       *counterTypeEnum = GL_PERFQUERY_COUNTER_RAW_INTEL;
960    }
961 
962    if (counterDataTypeEnum) {
963       switch (counter_obj->Type) {
964       case GL_FLOAT:
965       case GL_PERCENTAGE_AMD:
966          *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL;
967          break;
968       case GL_UNSIGNED_INT:
969          *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL;
970          break;
971       case GL_UNSIGNED_INT64_AMD:
972          *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL;
973          break;
974       default:
975          assert(!"Should not get here: invalid counter type");
976          return;
977       }
978    }
979 
980    if (rawCounterMaxValue) {
981       /* This value is (implicitly) specified to be used only with
982        * GL_PERFQUERY_COUNTER_RAW_INTEL counters. When semantic types for
983        * counters are added, that needs to be checked.
984        */
985 
986       /* The GL_INTEL_performance_query spec says:
987        *
988        *    "for some raw counters for which the maximal value is
989        *    deterministic, the maximal value of the counter in 1 second is
990        *    returned in the location pointed by rawCounterMaxValue, otherwise,
991        *    the location is written with the value of 0."
992        *
993        * The maximum value reported by the driver at the moment is not with
994        * these semantics, so write 0 always to be safe.
995        */
996       *rawCounterMaxValue = 0;
997    }
998 }
999 
1000 extern void GLAPIENTRY
_mesa_CreatePerfQueryINTEL(GLuint queryId,GLuint * queryHandle)1001 _mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle)
1002 {
1003    GET_CURRENT_CONTEXT(ctx);
1004    GLuint first;
1005    GLuint group;
1006    const struct gl_perf_monitor_group *group_obj;
1007    struct gl_perf_monitor_object *m;
1008    unsigned i;
1009 
1010    init_groups(ctx);
1011 
1012    /* This is not specified in the extension, but is the only sane thing to
1013     * do.
1014     */
1015    if (queryHandle == NULL) {
1016       _mesa_error(ctx, GL_INVALID_VALUE,
1017                   "glCreatePerfQueryINTEL(queryHandle == NULL)");
1018       return;
1019    }
1020 
1021    group = queryid_to_index(queryId);
1022    group_obj = get_group(ctx, group);
1023 
1024    /* The GL_INTEL_performance_query spec says:
1025     *
1026     *    "If queryId does not reference a valid query type, an INVALID_VALUE
1027     *    error is generated."
1028     */
1029    if (group_obj == NULL) {
1030       _mesa_error(ctx, GL_INVALID_VALUE,
1031                   "glCreatePerfQueryINTEL(invalid queryId)");
1032       return;
1033    }
1034 
1035    /* The query object created here is the counterpart of a `monitor' in
1036     * AMD_performance_monitor. This call is equivalent to calling
1037     * GenPerfMonitorsAMD and SelectPerfMonitorCountersAMD with a list of all
1038     * counters in a group.
1039     */
1040 
1041    /* We keep the monitor ids contiguous */
1042    first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, 1);
1043    if (!first) {
1044       /* The GL_INTEL_performance_query spec says:
1045        *
1046        *    "If the query instance cannot be created due to exceeding the
1047        *    number of allowed instances or driver fails query creation due to
1048        *    an insufficient memory reason, an OUT_OF_MEMORY error is
1049        *    generated, and the location pointed by queryHandle returns NULL."
1050       */
1051       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCreatePerfQueryINTEL");
1052       return;
1053    }
1054 
1055    m = new_performance_monitor(ctx, first);
1056    if (m == NULL) {
1057       _mesa_error_no_memory(__func__);
1058       return;
1059    }
1060 
1061    _mesa_HashInsert(ctx->PerfMonitor.Monitors, first, m);
1062    *queryHandle = first;
1063 
1064    ctx->Driver.ResetPerfMonitor(ctx, m);
1065 
1066    for (i = 0; i < group_obj->NumCounters; ++i) {
1067       ++m->ActiveGroups[group];
1068       /* Counters are a continuous range of integers, 0 to NumCounters (excl),
1069        * so i is the counter value to use here.
1070        */
1071       BITSET_SET(m->ActiveCounters[group], i);
1072    }
1073 }
1074 
1075 extern void GLAPIENTRY
_mesa_DeletePerfQueryINTEL(GLuint queryHandle)1076 _mesa_DeletePerfQueryINTEL(GLuint queryHandle)
1077 {
1078    GET_CURRENT_CONTEXT(ctx);
1079    struct gl_perf_monitor_object *m;
1080 
1081    /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1082     * id.
1083     */
1084    m = lookup_monitor(ctx, queryHandle);
1085 
1086    /* The GL_INTEL_performance_query spec says:
1087     *
1088     *    "If a query handle doesn't reference a previously created performance
1089     *    query instance, an INVALID_VALUE error is generated."
1090     */
1091    if (m == NULL) {
1092       _mesa_error(ctx, GL_INVALID_VALUE,
1093                   "glDeletePerfQueryINTEL(invalid queryHandle)");
1094       return;
1095    }
1096 
1097    /* Let the driver stop the monitor if it's active. */
1098    if (m->Active) {
1099       ctx->Driver.ResetPerfMonitor(ctx, m);
1100       m->Ended = false;
1101    }
1102 
1103    _mesa_HashRemove(ctx->PerfMonitor.Monitors, queryHandle);
1104    ralloc_free(m->ActiveGroups);
1105    ralloc_free(m->ActiveCounters);
1106    ctx->Driver.DeletePerfMonitor(ctx, m);
1107 }
1108 
1109 extern void GLAPIENTRY
_mesa_BeginPerfQueryINTEL(GLuint queryHandle)1110 _mesa_BeginPerfQueryINTEL(GLuint queryHandle)
1111 {
1112    GET_CURRENT_CONTEXT(ctx);
1113    struct gl_perf_monitor_object *m;
1114 
1115    /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1116     * id.
1117     */
1118 
1119    m = lookup_monitor(ctx, queryHandle);
1120 
1121    /* The GL_INTEL_performance_query spec says:
1122     *
1123     *    "If a query handle doesn't reference a previously created performance
1124     *    query instance, an INVALID_VALUE error is generated."
1125     */
1126    if (m == NULL) {
1127       _mesa_error(ctx, GL_INVALID_VALUE,
1128                   "glBeginPerfQueryINTEL(invalid queryHandle)");
1129       return;
1130    }
1131 
1132    /* The GL_INTEL_performance_query spec says:
1133     *
1134     *    "Note that some query types, they cannot be collected in the same
1135     *    time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
1136     *    they refer to queries of such different types. In such case
1137     *    INVALID_OPERATION error is generated."
1138     *
1139     * We also generate an INVALID_OPERATION error if the driver can't begin
1140     * monitoring for its own reasons, and for nesting the same query.
1141     */
1142    if (m->Active) {
1143       _mesa_error(ctx, GL_INVALID_OPERATION,
1144                   "glBeginPerfQueryINTEL(already active)");
1145       return;
1146    }
1147 
1148    if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
1149       m->Active = true;
1150       m->Ended = false;
1151    } else {
1152       _mesa_error(ctx, GL_INVALID_OPERATION,
1153                   "glBeginPerfQueryINTEL(driver unable to begin monitoring)");
1154    }
1155 }
1156 
1157 extern void GLAPIENTRY
_mesa_EndPerfQueryINTEL(GLuint queryHandle)1158 _mesa_EndPerfQueryINTEL(GLuint queryHandle)
1159 {
1160    GET_CURRENT_CONTEXT(ctx);
1161    struct gl_perf_monitor_object *m;
1162 
1163    /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1164     * id.
1165     */
1166 
1167    m = lookup_monitor(ctx, queryHandle);
1168 
1169    /* The GL_INTEL_performance_query spec says:
1170     *
1171     *    "If a performance query is not currently started, an
1172     *    INVALID_OPERATION error will be generated."
1173     *
1174     * The specification doesn't state that an invalid handle would be an
1175     * INVALID_VALUE error. Regardless, query for such a handle will not be
1176     * started, so we generate an INVALID_OPERATION in that case too.
1177     */
1178    if (m == NULL) {
1179       _mesa_error(ctx, GL_INVALID_OPERATION,
1180                   "glEndPerfQueryINTEL(invalid queryHandle)");
1181       return;
1182    }
1183 
1184    if (!m->Active) {
1185       _mesa_error(ctx, GL_INVALID_OPERATION,
1186                   "glEndPerfQueryINTEL(not active)");
1187       return;
1188    }
1189 
1190    ctx->Driver.EndPerfMonitor(ctx, m);
1191 
1192    m->Active = false;
1193    m->Ended = true;
1194 }
1195 
1196 extern void GLAPIENTRY
_mesa_GetPerfQueryDataINTEL(GLuint queryHandle,GLuint flags,GLsizei dataSize,void * data,GLuint * bytesWritten)1197 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
1198                             GLsizei dataSize, void *data, GLuint *bytesWritten)
1199 {
1200    GET_CURRENT_CONTEXT(ctx);
1201    struct gl_perf_monitor_object *m;
1202    bool result_available;
1203 
1204    /* The GL_INTEL_performance_query spec says:
1205     *
1206     *    "If bytesWritten or data pointers are NULL then an INVALID_VALUE
1207     *    error is generated."
1208     */
1209    if (!bytesWritten || !data) {
1210       _mesa_error(ctx, GL_INVALID_VALUE,
1211                   "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
1212       return;
1213    }
1214 
1215    /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1216     * id.
1217     */
1218 
1219    m = lookup_monitor(ctx, queryHandle);
1220 
1221    /* The specification doesn't state that an invalid handle generates an
1222     * error. We could interpret that to mean the case should be handled as
1223     * "measurement not ready for this query", but what should be done if
1224     * `flags' equals PERFQUERY_WAIT_INTEL?
1225     *
1226     * To resolve this, we just generate an INVALID_VALUE from an invalid query
1227     * handle.
1228     */
1229    if (m == NULL) {
1230       _mesa_error(ctx, GL_INVALID_VALUE,
1231                   "glGetPerfQueryDataINTEL(invalid queryHandle)");
1232       return;
1233    }
1234 
1235    /* We need at least enough room for a single value. */
1236    if (dataSize < sizeof(GLuint)) {
1237       *bytesWritten = 0;
1238       return;
1239    }
1240 
1241    /* The GL_INTEL_performance_query spec says:
1242     *
1243     *    "The call may end without returning any data if they are not ready
1244     *    for reading as the measurement session is still pending (the
1245     *    EndPerfQueryINTEL() command processing is not finished by
1246     *    hardware). In this case location pointed by the bytesWritten
1247     *    parameter will be set to 0."
1248     *
1249     * If EndPerfQueryINTEL() is not called at all, we follow this.
1250     */
1251    if (!m->Ended) {
1252       *bytesWritten = 0;
1253       return;
1254    }
1255 
1256    result_available = ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
1257 
1258    if (!result_available) {
1259       if (flags == GL_PERFQUERY_FLUSH_INTEL) {
1260          ctx->Driver.Flush(ctx);
1261       } else if (flags == GL_PERFQUERY_WAIT_INTEL) {
1262          /* Assume Finish() is both enough and not too much to wait for
1263           * results. If results are still not available after Finish(), the
1264           * later code automatically bails out with 0 for bytesWritten.
1265           */
1266          ctx->Driver.Finish(ctx);
1267          result_available =
1268             ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
1269       }
1270    }
1271 
1272    if (result_available) {
1273       ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, (GLint*)bytesWritten);
1274    } else {
1275       *bytesWritten = 0;
1276    }
1277 }
1278