Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included
     14  * in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 
     26 #include "bufferobj.h"
     27 #include "glheader.h"
     28 #include "context.h"
     29 #include "enums.h"
     30 #include "hash.h"
     31 #include "imports.h"
     32 #include "queryobj.h"
     33 #include "mtypes.h"
     34 #include "main/dispatch.h"
     35 
     36 
     37 /**
     38  * Allocate a new query object.  This is a fallback routine called via
     39  * ctx->Driver.NewQueryObject().
     40  * \param ctx - rendering context
     41  * \param id - the new object's ID
     42  * \return pointer to new query_object object or NULL if out of memory.
     43  */
     44 static struct gl_query_object *
     45 _mesa_new_query_object(struct gl_context *ctx, GLuint id)
     46 {
     47    struct gl_query_object *q = CALLOC_STRUCT(gl_query_object);
     48    (void) ctx;
     49    if (q) {
     50       q->Id = id;
     51       q->Result = 0;
     52       q->Active = GL_FALSE;
     53 
     54       /* This is to satisfy the language of the specification: "In the initial
     55        * state of a query object, the result is available" (OpenGL 3.1 
     56        * 2.13).
     57        */
     58       q->Ready = GL_TRUE;
     59 
     60       /* OpenGL 3.1  2.13 says about GenQueries, "These names are marked as
     61        * used, but no object is associated with them until the first time they
     62        * are used by BeginQuery." Since our implementation actually does
     63        * allocate an object at this point, use a flag to indicate that this
     64        * object has not yet been bound so should not be considered a query.
     65        */
     66       q->EverBound = GL_FALSE;
     67    }
     68    return q;
     69 }
     70 
     71 
     72 /**
     73  * Begin a query.  Software driver fallback.
     74  * Called via ctx->Driver.BeginQuery().
     75  */
     76 static void
     77 _mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
     78 {
     79    ctx->NewState |= _NEW_DEPTH; /* for swrast */
     80 }
     81 
     82 
     83 /**
     84  * End a query.  Software driver fallback.
     85  * Called via ctx->Driver.EndQuery().
     86  */
     87 static void
     88 _mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
     89 {
     90    ctx->NewState |= _NEW_DEPTH; /* for swrast */
     91    q->Ready = GL_TRUE;
     92 }
     93 
     94 
     95 /**
     96  * Wait for query to complete.  Software driver fallback.
     97  * Called via ctx->Driver.WaitQuery().
     98  */
     99 static void
    100 _mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
    101 {
    102    /* For software drivers, _mesa_end_query() should have completed the query.
    103     * For real hardware, implement a proper WaitQuery() driver function,
    104     * which may require issuing a flush.
    105     */
    106    assert(q->Ready);
    107 }
    108 
    109 
    110 /**
    111  * Check if a query results are ready.  Software driver fallback.
    112  * Called via ctx->Driver.CheckQuery().
    113  */
    114 static void
    115 _mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
    116 {
    117    /* No-op for sw rendering.
    118     * HW drivers may need to flush at this time.
    119     */
    120 }
    121 
    122 
    123 /**
    124  * Delete a query object.  Called via ctx->Driver.DeleteQuery().
    125  * Not removed from hash table here.
    126  */
    127 static void
    128 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
    129 {
    130    free(q->Label);
    131    free(q);
    132 }
    133 
    134 
    135 void
    136 _mesa_init_query_object_functions(struct dd_function_table *driver)
    137 {
    138    driver->NewQueryObject = _mesa_new_query_object;
    139    driver->DeleteQuery = _mesa_delete_query;
    140    driver->BeginQuery = _mesa_begin_query;
    141    driver->EndQuery = _mesa_end_query;
    142    driver->WaitQuery = _mesa_wait_query;
    143    driver->CheckQuery = _mesa_check_query;
    144 }
    145 
    146 static struct gl_query_object **
    147 get_pipe_stats_binding_point(struct gl_context *ctx,
    148                              GLenum target)
    149 {
    150    const int which = target - GL_VERTICES_SUBMITTED_ARB;
    151    assert(which < MAX_PIPELINE_STATISTICS);
    152 
    153    if (!_mesa_is_desktop_gl(ctx) ||
    154        !ctx->Extensions.ARB_pipeline_statistics_query)
    155       return NULL;
    156 
    157    return &ctx->Query.pipeline_stats[which];
    158 }
    159 
    160 /**
    161  * Return pointer to the query object binding point for the given target and
    162  * index.
    163  * \return NULL if invalid target, else the address of binding point
    164  */
    165 static struct gl_query_object **
    166 get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
    167 {
    168    switch (target) {
    169    case GL_SAMPLES_PASSED_ARB:
    170       if (ctx->Extensions.ARB_occlusion_query)
    171          return &ctx->Query.CurrentOcclusionObject;
    172       else
    173          return NULL;
    174    case GL_ANY_SAMPLES_PASSED:
    175       if (ctx->Extensions.ARB_occlusion_query2)
    176          return &ctx->Query.CurrentOcclusionObject;
    177       else
    178          return NULL;
    179    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
    180       if (ctx->Extensions.ARB_ES3_compatibility
    181           || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
    182          return &ctx->Query.CurrentOcclusionObject;
    183       else
    184          return NULL;
    185    case GL_TIME_ELAPSED_EXT:
    186       if (ctx->Extensions.EXT_timer_query)
    187          return &ctx->Query.CurrentTimerObject;
    188       else
    189          return NULL;
    190    case GL_PRIMITIVES_GENERATED:
    191       if (ctx->Extensions.EXT_transform_feedback)
    192          return &ctx->Query.PrimitivesGenerated[index];
    193       else
    194          return NULL;
    195    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    196       if (ctx->Extensions.EXT_transform_feedback)
    197          return &ctx->Query.PrimitivesWritten[index];
    198       else
    199          return NULL;
    200 
    201    case GL_VERTICES_SUBMITTED_ARB:
    202    case GL_PRIMITIVES_SUBMITTED_ARB:
    203    case GL_VERTEX_SHADER_INVOCATIONS_ARB:
    204    case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
    205    case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
    206    case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
    207          return get_pipe_stats_binding_point(ctx, target);
    208 
    209    case GL_GEOMETRY_SHADER_INVOCATIONS:
    210       /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */
    211       target = GL_VERTICES_SUBMITTED_ARB + MAX_PIPELINE_STATISTICS - 1;
    212       /* fallthrough */
    213    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
    214       if (_mesa_has_geometry_shaders(ctx))
    215          return get_pipe_stats_binding_point(ctx, target);
    216       else
    217          return NULL;
    218 
    219    case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
    220    case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
    221       if (_mesa_has_tessellation(ctx))
    222          return get_pipe_stats_binding_point(ctx, target);
    223       else
    224          return NULL;
    225 
    226    case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
    227       if (_mesa_has_compute_shaders(ctx))
    228          return get_pipe_stats_binding_point(ctx, target);
    229       else
    230          return NULL;
    231 
    232    default:
    233       return NULL;
    234    }
    235 }
    236 
    237 /**
    238  * Create $n query objects and store them in *ids. Make them of type $target
    239  * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries().
    240  */
    241 static void
    242 create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids,
    243                bool dsa)
    244 {
    245    const char *func = dsa ? "glGenQueries" : "glCreateQueries";
    246    GLuint first;
    247 
    248    if (MESA_VERBOSE & VERBOSE_API)
    249       _mesa_debug(ctx, "%s(%d)\n", func, n);
    250 
    251    if (n < 0) {
    252       _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
    253       return;
    254    }
    255 
    256    first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
    257    if (first) {
    258       GLsizei i;
    259       for (i = 0; i < n; i++) {
    260          struct gl_query_object *q
    261             = ctx->Driver.NewQueryObject(ctx, first + i);
    262          if (!q) {
    263             _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
    264             return;
    265          } else if (dsa) {
    266             /* Do the equivalent of binding the buffer with a target */
    267             q->Target = target;
    268             q->EverBound = GL_TRUE;
    269          }
    270          ids[i] = first + i;
    271          _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
    272       }
    273    }
    274 }
    275 
    276 void GLAPIENTRY
    277 _mesa_GenQueries(GLsizei n, GLuint *ids)
    278 {
    279    GET_CURRENT_CONTEXT(ctx);
    280    create_queries(ctx, 0, n, ids, false);
    281 }
    282 
    283 void GLAPIENTRY
    284 _mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids)
    285 {
    286    GET_CURRENT_CONTEXT(ctx);
    287 
    288    switch (target) {
    289    case GL_SAMPLES_PASSED:
    290    case GL_ANY_SAMPLES_PASSED:
    291    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
    292    case GL_TIME_ELAPSED:
    293    case GL_TIMESTAMP:
    294    case GL_PRIMITIVES_GENERATED:
    295    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    296       break;
    297    default:
    298       _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)",
    299                   _mesa_enum_to_string(target));
    300       return;
    301    }
    302 
    303    create_queries(ctx, target, n, ids, true);
    304 }
    305 
    306 
    307 void GLAPIENTRY
    308 _mesa_DeleteQueries(GLsizei n, const GLuint *ids)
    309 {
    310    GLint i;
    311    GET_CURRENT_CONTEXT(ctx);
    312    FLUSH_VERTICES(ctx, 0);
    313 
    314    if (MESA_VERBOSE & VERBOSE_API)
    315       _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
    316 
    317    if (n < 0) {
    318       _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
    319       return;
    320    }
    321 
    322    for (i = 0; i < n; i++) {
    323       if (ids[i] > 0) {
    324          struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
    325          if (q) {
    326             if (q->Active) {
    327                struct gl_query_object **bindpt;
    328                bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
    329                assert(bindpt); /* Should be non-null for active q. */
    330                if (bindpt) {
    331                   *bindpt = NULL;
    332                }
    333                q->Active = GL_FALSE;
    334                ctx->Driver.EndQuery(ctx, q);
    335             }
    336             _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
    337             ctx->Driver.DeleteQuery(ctx, q);
    338          }
    339       }
    340    }
    341 }
    342 
    343 
    344 GLboolean GLAPIENTRY
    345 _mesa_IsQuery(GLuint id)
    346 {
    347    struct gl_query_object *q;
    348 
    349    GET_CURRENT_CONTEXT(ctx);
    350    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
    351 
    352    if (MESA_VERBOSE & VERBOSE_API)
    353       _mesa_debug(ctx, "glIsQuery(%u)\n", id);
    354 
    355    if (id == 0)
    356       return GL_FALSE;
    357 
    358    q = _mesa_lookup_query_object(ctx, id);
    359    if (q == NULL)
    360       return GL_FALSE;
    361 
    362    return q->EverBound;
    363 }
    364 
    365 static GLboolean
    366 query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
    367 {
    368    switch (target) {
    369    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    370    case GL_PRIMITIVES_GENERATED:
    371       if (index >= ctx->Const.MaxVertexStreams) {
    372          _mesa_error(ctx, GL_INVALID_VALUE,
    373                      "glBeginQueryIndexed(index>=MaxVertexStreams)");
    374          return GL_FALSE;
    375       }
    376       break;
    377    default:
    378       if (index > 0) {
    379          _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
    380          return GL_FALSE;
    381       }
    382    }
    383    return GL_TRUE;
    384 }
    385 
    386 void GLAPIENTRY
    387 _mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
    388 {
    389    struct gl_query_object *q, **bindpt;
    390    GET_CURRENT_CONTEXT(ctx);
    391 
    392    if (MESA_VERBOSE & VERBOSE_API)
    393       _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
    394                   _mesa_enum_to_string(target), index, id);
    395 
    396    if (!query_error_check_index(ctx, target, index))
    397       return;
    398 
    399    FLUSH_VERTICES(ctx, 0);
    400 
    401    bindpt = get_query_binding_point(ctx, target, index);
    402    if (!bindpt) {
    403       _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
    404       return;
    405    }
    406 
    407    /* From the GL_ARB_occlusion_query spec:
    408     *
    409     *     "If BeginQueryARB is called while another query is already in
    410     *      progress with the same target, an INVALID_OPERATION error is
    411     *      generated."
    412     */
    413    if (*bindpt) {
    414       _mesa_error(ctx, GL_INVALID_OPERATION,
    415                   "glBeginQuery{Indexed}(target=%s is active)",
    416                   _mesa_enum_to_string(target));
    417       return;
    418    }
    419 
    420    if (id == 0) {
    421       _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
    422       return;
    423    }
    424 
    425    q = _mesa_lookup_query_object(ctx, id);
    426    if (!q) {
    427       if (ctx->API != API_OPENGL_COMPAT) {
    428          _mesa_error(ctx, GL_INVALID_OPERATION,
    429                      "glBeginQuery{Indexed}(non-gen name)");
    430          return;
    431       } else {
    432          /* create new object */
    433          q = ctx->Driver.NewQueryObject(ctx, id);
    434          if (!q) {
    435             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
    436             return;
    437          }
    438          _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
    439       }
    440    }
    441    else {
    442       /* pre-existing object */
    443       if (q->Active) {
    444          _mesa_error(ctx, GL_INVALID_OPERATION,
    445                      "glBeginQuery{Indexed}(query already active)");
    446          return;
    447       }
    448 
    449       /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4
    450        * spec states:
    451        *
    452        *     "BeginQuery generates an INVALID_OPERATION error if any of the
    453        *      following conditions hold: [...] id is the name of an
    454        *      existing query object whose type does not match target; [...]
    455        *
    456        * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY
    457        * OBJECTS AND ASYNCHRONOUS QUERIES, page 43.
    458        */
    459       if (q->EverBound && q->Target != target) {
    460          _mesa_error(ctx, GL_INVALID_OPERATION,
    461                      "glBeginQuery{Indexed}(target mismatch)");
    462          return;
    463       }
    464    }
    465 
    466    /* This possibly changes the target of a buffer allocated by
    467     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
    468     * the following:
    469     *
    470     * "CreateQueries adds a <target>, so strictly speaking the <target>
    471     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
    472     * isn't a selector, so we decided not to change it."
    473     *
    474     * Updating the target of the query object should be acceptable, so let's
    475     * do that.
    476     */
    477 
    478    q->Target = target;
    479    q->Active = GL_TRUE;
    480    q->Result = 0;
    481    q->Ready = GL_FALSE;
    482    q->EverBound = GL_TRUE;
    483    q->Stream = index;
    484 
    485    /* XXX should probably refcount query objects */
    486    *bindpt = q;
    487 
    488    ctx->Driver.BeginQuery(ctx, q);
    489 }
    490 
    491 
    492 void GLAPIENTRY
    493 _mesa_EndQueryIndexed(GLenum target, GLuint index)
    494 {
    495    struct gl_query_object *q, **bindpt;
    496    GET_CURRENT_CONTEXT(ctx);
    497 
    498    if (MESA_VERBOSE & VERBOSE_API)
    499       _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
    500                   _mesa_enum_to_string(target), index);
    501 
    502    if (!query_error_check_index(ctx, target, index))
    503       return;
    504 
    505    FLUSH_VERTICES(ctx, 0);
    506 
    507    bindpt = get_query_binding_point(ctx, target, index);
    508    if (!bindpt) {
    509       _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
    510       return;
    511    }
    512 
    513    /* XXX should probably refcount query objects */
    514    q = *bindpt;
    515 
    516    /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
    517    if (q && q->Target != target) {
    518       _mesa_error(ctx, GL_INVALID_OPERATION,
    519                   "glEndQuery(target=%s with active query of target %s)",
    520                   _mesa_enum_to_string(target),
    521                   _mesa_enum_to_string(q->Target));
    522       return;
    523    }
    524 
    525    *bindpt = NULL;
    526 
    527    if (!q || !q->Active) {
    528       _mesa_error(ctx, GL_INVALID_OPERATION,
    529                   "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
    530       return;
    531    }
    532 
    533    q->Active = GL_FALSE;
    534    ctx->Driver.EndQuery(ctx, q);
    535 }
    536 
    537 void GLAPIENTRY
    538 _mesa_BeginQuery(GLenum target, GLuint id)
    539 {
    540    _mesa_BeginQueryIndexed(target, 0, id);
    541 }
    542 
    543 void GLAPIENTRY
    544 _mesa_EndQuery(GLenum target)
    545 {
    546    _mesa_EndQueryIndexed(target, 0);
    547 }
    548 
    549 void GLAPIENTRY
    550 _mesa_QueryCounter(GLuint id, GLenum target)
    551 {
    552    struct gl_query_object *q;
    553    GET_CURRENT_CONTEXT(ctx);
    554 
    555    if (MESA_VERBOSE & VERBOSE_API)
    556       _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
    557                   _mesa_enum_to_string(target));
    558 
    559    /* error checking */
    560    if (target != GL_TIMESTAMP) {
    561       _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
    562       return;
    563    }
    564 
    565    if (id == 0) {
    566       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
    567       return;
    568    }
    569 
    570    q = _mesa_lookup_query_object(ctx, id);
    571    if (!q) {
    572       /* XXX the Core profile should throw INVALID_OPERATION here */
    573 
    574       /* create new object */
    575       q = ctx->Driver.NewQueryObject(ctx, id);
    576       if (!q) {
    577          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
    578          return;
    579       }
    580       _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
    581    }
    582    else {
    583       if (q->Target && q->Target != GL_TIMESTAMP) {
    584          _mesa_error(ctx, GL_INVALID_OPERATION,
    585                      "glQueryCounter(id has an invalid target)");
    586          return;
    587       }
    588    }
    589 
    590    if (q->Active) {
    591       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
    592       return;
    593    }
    594 
    595    /* This possibly changes the target of a buffer allocated by
    596     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
    597     * the following:
    598     *
    599     * "CreateQueries adds a <target>, so strictly speaking the <target>
    600     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
    601     * isn't a selector, so we decided not to change it."
    602     *
    603     * Updating the target of the query object should be acceptable, so let's
    604     * do that.
    605     */
    606 
    607    q->Target = target;
    608    q->Result = 0;
    609    q->Ready = GL_FALSE;
    610    q->EverBound = GL_TRUE;
    611 
    612    if (ctx->Driver.QueryCounter) {
    613       ctx->Driver.QueryCounter(ctx, q);
    614    } else {
    615       /* QueryCounter is implemented using EndQuery without BeginQuery
    616        * in drivers. This is actually Direct3D and Gallium convention.
    617        */
    618       ctx->Driver.EndQuery(ctx, q);
    619    }
    620 }
    621 
    622 
    623 void GLAPIENTRY
    624 _mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
    625                         GLint *params)
    626 {
    627    struct gl_query_object *q = NULL, **bindpt = NULL;
    628    GET_CURRENT_CONTEXT(ctx);
    629 
    630    if (MESA_VERBOSE & VERBOSE_API)
    631       _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
    632                   _mesa_enum_to_string(target),
    633                   index,
    634                   _mesa_enum_to_string(pname));
    635 
    636    if (!query_error_check_index(ctx, target, index))
    637       return;
    638 
    639    if (target == GL_TIMESTAMP) {
    640       if (!ctx->Extensions.ARB_timer_query) {
    641          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
    642          return;
    643       }
    644    }
    645    else {
    646       bindpt = get_query_binding_point(ctx, target, index);
    647       if (!bindpt) {
    648          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
    649          return;
    650       }
    651 
    652       q = *bindpt;
    653    }
    654 
    655    switch (pname) {
    656       case GL_QUERY_COUNTER_BITS_ARB:
    657          switch (target) {
    658          case GL_SAMPLES_PASSED:
    659             *params = ctx->Const.QueryCounterBits.SamplesPassed;
    660             break;
    661          case GL_ANY_SAMPLES_PASSED:
    662             /* The minimum value of this is 1 if it's nonzero, and the value
    663              * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
    664              * bits.
    665              */
    666             *params = 1;
    667             break;
    668          case GL_TIME_ELAPSED:
    669             *params = ctx->Const.QueryCounterBits.TimeElapsed;
    670             break;
    671          case GL_TIMESTAMP:
    672             *params = ctx->Const.QueryCounterBits.Timestamp;
    673             break;
    674          case GL_PRIMITIVES_GENERATED:
    675             *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
    676             break;
    677          case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    678             *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
    679             break;
    680          case GL_VERTICES_SUBMITTED_ARB:
    681             *params = ctx->Const.QueryCounterBits.VerticesSubmitted;
    682             break;
    683          case GL_PRIMITIVES_SUBMITTED_ARB:
    684             *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted;
    685             break;
    686          case GL_VERTEX_SHADER_INVOCATIONS_ARB:
    687             *params = ctx->Const.QueryCounterBits.VsInvocations;
    688             break;
    689          case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
    690             *params = ctx->Const.QueryCounterBits.TessPatches;
    691             break;
    692          case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
    693             *params = ctx->Const.QueryCounterBits.TessInvocations;
    694             break;
    695          case GL_GEOMETRY_SHADER_INVOCATIONS:
    696             *params = ctx->Const.QueryCounterBits.GsInvocations;
    697             break;
    698          case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
    699             *params = ctx->Const.QueryCounterBits.GsPrimitives;
    700             break;
    701          case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
    702             *params = ctx->Const.QueryCounterBits.FsInvocations;
    703             break;
    704          case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
    705             *params = ctx->Const.QueryCounterBits.ComputeInvocations;
    706             break;
    707          case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
    708             *params = ctx->Const.QueryCounterBits.ClInPrimitives;
    709             break;
    710          case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
    711             *params = ctx->Const.QueryCounterBits.ClOutPrimitives;
    712             break;
    713          default:
    714             _mesa_problem(ctx,
    715                           "Unknown target in glGetQueryIndexediv(target = %s)",
    716                           _mesa_enum_to_string(target));
    717             *params = 0;
    718             break;
    719          }
    720          break;
    721       case GL_CURRENT_QUERY_ARB:
    722          *params = (q && q->Target == target) ? q->Id : 0;
    723          break;
    724       default:
    725          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
    726          return;
    727    }
    728 }
    729 
    730 void GLAPIENTRY
    731 _mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
    732 {
    733    _mesa_GetQueryIndexediv(target, 0, pname, params);
    734 }
    735 
    736 static void
    737 get_query_object(struct gl_context *ctx, const char *func,
    738                  GLuint id, GLenum pname, GLenum ptype,
    739                  struct gl_buffer_object *buf, intptr_t offset)
    740 {
    741    struct gl_query_object *q = NULL;
    742    uint64_t value;
    743 
    744    if (MESA_VERBOSE & VERBOSE_API)
    745       _mesa_debug(ctx, "%s(%u, %s)\n", func, id,
    746                   _mesa_enum_to_string(pname));
    747 
    748    if (id)
    749       q = _mesa_lookup_query_object(ctx, id);
    750 
    751    if (!q || q->Active || !q->EverBound) {
    752       _mesa_error(ctx, GL_INVALID_OPERATION,
    753                   "%s(id=%d is invalid or active)", func, id);
    754       return;
    755    }
    756 
    757    if (buf && buf != ctx->Shared->NullBufferObj) {
    758       bool is_64bit = ptype == GL_INT64_ARB ||
    759          ptype == GL_UNSIGNED_INT64_ARB;
    760       if (!ctx->Extensions.ARB_query_buffer_object) {
    761          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func);
    762          return;
    763       }
    764       if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) {
    765          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func);
    766          return;
    767       }
    768 
    769       if (offset < 0) {
    770          _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func);
    771          return;
    772       }
    773 
    774       switch (pname) {
    775       case GL_QUERY_RESULT:
    776       case GL_QUERY_RESULT_NO_WAIT:
    777       case GL_QUERY_RESULT_AVAILABLE:
    778       case GL_QUERY_TARGET:
    779          ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype);
    780          return;
    781       }
    782 
    783       /* fall through to get error below */
    784    }
    785 
    786    switch (pname) {
    787    case GL_QUERY_RESULT:
    788       if (!q->Ready)
    789          ctx->Driver.WaitQuery(ctx, q);
    790       value = q->Result;
    791       break;
    792    case GL_QUERY_RESULT_NO_WAIT:
    793       if (!ctx->Extensions.ARB_query_buffer_object)
    794          goto invalid_enum;
    795       ctx->Driver.CheckQuery(ctx, q);
    796       if (!q->Ready)
    797          return;
    798       value = q->Result;
    799       break;
    800    case GL_QUERY_RESULT_AVAILABLE:
    801       if (!q->Ready)
    802          ctx->Driver.CheckQuery(ctx, q);
    803       value = q->Ready;
    804       break;
    805    case GL_QUERY_TARGET:
    806       value = q->Target;
    807       break;
    808    default:
    809 invalid_enum:
    810       _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)",
    811                   func, _mesa_enum_to_string(pname));
    812       return;
    813    }
    814 
    815    switch (ptype) {
    816    case GL_INT: {
    817       GLint *param = (GLint *)offset;
    818       if (value > 0x7fffffff)
    819          *param = 0x7fffffff;
    820       else
    821          *param = value;
    822       break;
    823    }
    824    case GL_UNSIGNED_INT: {
    825       GLuint *param = (GLuint *)offset;
    826       if (value > 0xffffffff)
    827          *param = 0xffffffff;
    828       else
    829          *param = value;
    830       break;
    831    }
    832    case GL_INT64_ARB:
    833    case GL_UNSIGNED_INT64_ARB: {
    834       GLuint64EXT *param = (GLuint64EXT *)offset;
    835       *param = value;
    836       break;
    837    }
    838    default:
    839       unreachable("unexpected ptype");
    840    }
    841 }
    842 
    843 void GLAPIENTRY
    844 _mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
    845 {
    846    GET_CURRENT_CONTEXT(ctx);
    847 
    848    get_query_object(ctx, "glGetQueryObjectiv",
    849                     id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params);
    850 }
    851 
    852 
    853 void GLAPIENTRY
    854 _mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
    855 {
    856    GET_CURRENT_CONTEXT(ctx);
    857 
    858    get_query_object(ctx, "glGetQueryObjectuiv",
    859                     id, pname, GL_UNSIGNED_INT,
    860                     ctx->QueryBuffer, (intptr_t)params);
    861 }
    862 
    863 
    864 /**
    865  * New with GL_EXT_timer_query
    866  */
    867 void GLAPIENTRY
    868 _mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
    869 {
    870    GET_CURRENT_CONTEXT(ctx);
    871 
    872    get_query_object(ctx, "glGetQueryObjecti64v",
    873                     id, pname, GL_INT64_ARB,
    874                     ctx->QueryBuffer, (intptr_t)params);
    875 }
    876 
    877 
    878 /**
    879  * New with GL_EXT_timer_query
    880  */
    881 void GLAPIENTRY
    882 _mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
    883 {
    884    GET_CURRENT_CONTEXT(ctx);
    885 
    886    get_query_object(ctx, "glGetQueryObjectui64v",
    887                     id, pname, GL_UNSIGNED_INT64_ARB,
    888                     ctx->QueryBuffer, (intptr_t)params);
    889 }
    890 
    891 /**
    892  * New with GL_ARB_query_buffer_object
    893  */
    894 void GLAPIENTRY
    895 _mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname,
    896                              GLintptr offset)
    897 {
    898    struct gl_buffer_object *buf;
    899    GET_CURRENT_CONTEXT(ctx);
    900 
    901    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv");
    902    if (!buf)
    903       return;
    904 
    905    get_query_object(ctx, "glGetQueryBufferObjectiv",
    906                     id, pname, GL_INT, buf, offset);
    907 }
    908 
    909 
    910 void GLAPIENTRY
    911 _mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname,
    912                               GLintptr offset)
    913 {
    914    struct gl_buffer_object *buf;
    915    GET_CURRENT_CONTEXT(ctx);
    916 
    917    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv");
    918    if (!buf)
    919       return;
    920 
    921    get_query_object(ctx, "glGetQueryBufferObjectuiv",
    922                     id, pname, GL_UNSIGNED_INT, buf, offset);
    923 }
    924 
    925 
    926 void GLAPIENTRY
    927 _mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname,
    928                                GLintptr offset)
    929 {
    930    struct gl_buffer_object *buf;
    931    GET_CURRENT_CONTEXT(ctx);
    932 
    933    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v");
    934    if (!buf)
    935       return;
    936 
    937    get_query_object(ctx, "glGetQueryBufferObjecti64v",
    938                     id, pname, GL_INT64_ARB, buf, offset);
    939 }
    940 
    941 
    942 void GLAPIENTRY
    943 _mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname,
    944                                 GLintptr offset)
    945 {
    946    struct gl_buffer_object *buf;
    947    GET_CURRENT_CONTEXT(ctx);
    948 
    949    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v");
    950    if (!buf)
    951       return;
    952 
    953    get_query_object(ctx, "glGetQueryBufferObjectui64v",
    954                     id, pname, GL_UNSIGNED_INT64_ARB, buf, offset);
    955 }
    956 
    957 
    958 /**
    959  * Allocate/init the context state related to query objects.
    960  */
    961 void
    962 _mesa_init_queryobj(struct gl_context *ctx)
    963 {
    964    ctx->Query.QueryObjects = _mesa_NewHashTable();
    965    ctx->Query.CurrentOcclusionObject = NULL;
    966 
    967    ctx->Const.QueryCounterBits.SamplesPassed = 64;
    968    ctx->Const.QueryCounterBits.TimeElapsed = 64;
    969    ctx->Const.QueryCounterBits.Timestamp = 64;
    970    ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
    971    ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
    972 
    973    ctx->Const.QueryCounterBits.VerticesSubmitted = 64;
    974    ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64;
    975    ctx->Const.QueryCounterBits.VsInvocations = 64;
    976    ctx->Const.QueryCounterBits.TessPatches = 64;
    977    ctx->Const.QueryCounterBits.TessInvocations = 64;
    978    ctx->Const.QueryCounterBits.GsInvocations = 64;
    979    ctx->Const.QueryCounterBits.GsPrimitives = 64;
    980    ctx->Const.QueryCounterBits.FsInvocations = 64;
    981    ctx->Const.QueryCounterBits.ComputeInvocations = 64;
    982    ctx->Const.QueryCounterBits.ClInPrimitives = 64;
    983    ctx->Const.QueryCounterBits.ClOutPrimitives = 64;
    984 }
    985 
    986 
    987 /**
    988  * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
    989  */
    990 static void
    991 delete_queryobj_cb(GLuint id, void *data, void *userData)
    992 {
    993    struct gl_query_object *q= (struct gl_query_object *) data;
    994    struct gl_context *ctx = (struct gl_context *)userData;
    995    ctx->Driver.DeleteQuery(ctx, q);
    996 }
    997 
    998 
    999 /**
   1000  * Free the context state related to query objects.
   1001  */
   1002 void
   1003 _mesa_free_queryobj_data(struct gl_context *ctx)
   1004 {
   1005    _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
   1006    _mesa_DeleteHashTable(ctx->Query.QueryObjects);
   1007 }
   1008