Home | History | Annotate | Download | only in main
      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 /* 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
    151 queryid_to_index(GLuint queryid)
    152 {
    153    return queryid - 1;
    154 }
    155 
    156 static inline GLuint
    157 index_to_queryid(GLuint index)
    158 {
    159    return index + 1;
    160 }
    161 
    162 static inline bool
    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
    169 counterid_to_index(GLuint counterid)
    170 {
    171    return counterid - 1;
    172 }
    173 
    174 /*****************************************************************************/
    175 
    176 void GLAPIENTRY
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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
   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