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 
    169    /* From GL_EXT_occlusion_query_boolean spec:
    170     *
    171     *    "Accepted by the <target> parameter of BeginQueryEXT, EndQueryEXT,
    172     *    and GetQueryivEXT:
    173     *
    174     *   ANY_SAMPLES_PASSED_EXT                         0x8C2F
    175     *   ANY_SAMPLES_PASSED_CONSERVATIVE_EXT            0x8D6A"
    176     */
    177    if ((_mesa_is_gles(ctx) && ctx->Version == 20) &&
    178        (target != GL_ANY_SAMPLES_PASSED &&
    179         target != GL_ANY_SAMPLES_PASSED_CONSERVATIVE))
    180       return NULL;
    181 
    182    switch (target) {
    183    case GL_SAMPLES_PASSED_ARB:
    184       if (ctx->Extensions.ARB_occlusion_query)
    185          return &ctx->Query.CurrentOcclusionObject;
    186       else
    187          return NULL;
    188    case GL_ANY_SAMPLES_PASSED:
    189       if (ctx->Extensions.ARB_occlusion_query2)
    190          return &ctx->Query.CurrentOcclusionObject;
    191       else
    192          return NULL;
    193    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
    194       if (ctx->Extensions.ARB_ES3_compatibility
    195           || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
    196          return &ctx->Query.CurrentOcclusionObject;
    197       else
    198          return NULL;
    199    case GL_TIME_ELAPSED_EXT:
    200       if (ctx->Extensions.EXT_timer_query)
    201          return &ctx->Query.CurrentTimerObject;
    202       else
    203          return NULL;
    204    case GL_PRIMITIVES_GENERATED:
    205       if (ctx->Extensions.EXT_transform_feedback)
    206          return &ctx->Query.PrimitivesGenerated[index];
    207       else
    208          return NULL;
    209    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    210       if (ctx->Extensions.EXT_transform_feedback)
    211          return &ctx->Query.PrimitivesWritten[index];
    212       else
    213          return NULL;
    214    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
    215       if (ctx->Extensions.ARB_transform_feedback_overflow_query)
    216          return &ctx->Query.TransformFeedbackOverflow[index];
    217       else
    218          return NULL;
    219    case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
    220       if (ctx->Extensions.ARB_transform_feedback_overflow_query)
    221          return &ctx->Query.TransformFeedbackOverflowAny;
    222       else
    223          return NULL;
    224 
    225    case GL_VERTICES_SUBMITTED_ARB:
    226    case GL_PRIMITIVES_SUBMITTED_ARB:
    227    case GL_VERTEX_SHADER_INVOCATIONS_ARB:
    228    case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
    229    case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
    230    case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
    231          return get_pipe_stats_binding_point(ctx, target);
    232 
    233    case GL_GEOMETRY_SHADER_INVOCATIONS:
    234       /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */
    235       target = GL_VERTICES_SUBMITTED_ARB + MAX_PIPELINE_STATISTICS - 1;
    236       /* fallthrough */
    237    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
    238       if (_mesa_has_geometry_shaders(ctx))
    239          return get_pipe_stats_binding_point(ctx, target);
    240       else
    241          return NULL;
    242 
    243    case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
    244    case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
    245       if (_mesa_has_tessellation(ctx))
    246          return get_pipe_stats_binding_point(ctx, target);
    247       else
    248          return NULL;
    249 
    250    case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
    251       if (_mesa_has_compute_shaders(ctx))
    252          return get_pipe_stats_binding_point(ctx, target);
    253       else
    254          return NULL;
    255 
    256    default:
    257       return NULL;
    258    }
    259 }
    260 
    261 /**
    262  * Create $n query objects and store them in *ids. Make them of type $target
    263  * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries().
    264  */
    265 static void
    266 create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids,
    267                bool dsa)
    268 {
    269    const char *func = dsa ? "glGenQueries" : "glCreateQueries";
    270    GLuint first;
    271 
    272    if (MESA_VERBOSE & VERBOSE_API)
    273       _mesa_debug(ctx, "%s(%d)\n", func, n);
    274 
    275    if (n < 0) {
    276       _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
    277       return;
    278    }
    279 
    280    first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
    281    if (first) {
    282       GLsizei i;
    283       for (i = 0; i < n; i++) {
    284          struct gl_query_object *q
    285             = ctx->Driver.NewQueryObject(ctx, first + i);
    286          if (!q) {
    287             _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
    288             return;
    289          } else if (dsa) {
    290             /* Do the equivalent of binding the buffer with a target */
    291             q->Target = target;
    292             q->EverBound = GL_TRUE;
    293          }
    294          ids[i] = first + i;
    295          _mesa_HashInsertLocked(ctx->Query.QueryObjects, first + i, q);
    296       }
    297    }
    298 }
    299 
    300 void GLAPIENTRY
    301 _mesa_GenQueries(GLsizei n, GLuint *ids)
    302 {
    303    GET_CURRENT_CONTEXT(ctx);
    304    create_queries(ctx, 0, n, ids, false);
    305 }
    306 
    307 void GLAPIENTRY
    308 _mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids)
    309 {
    310    GET_CURRENT_CONTEXT(ctx);
    311 
    312    switch (target) {
    313    case GL_SAMPLES_PASSED:
    314    case GL_ANY_SAMPLES_PASSED:
    315    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
    316    case GL_TIME_ELAPSED:
    317    case GL_TIMESTAMP:
    318    case GL_PRIMITIVES_GENERATED:
    319    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    320    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
    321    case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
    322       break;
    323    default:
    324       _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)",
    325                   _mesa_enum_to_string(target));
    326       return;
    327    }
    328 
    329    create_queries(ctx, target, n, ids, true);
    330 }
    331 
    332 
    333 void GLAPIENTRY
    334 _mesa_DeleteQueries(GLsizei n, const GLuint *ids)
    335 {
    336    GLint i;
    337    GET_CURRENT_CONTEXT(ctx);
    338    FLUSH_VERTICES(ctx, 0);
    339 
    340    if (MESA_VERBOSE & VERBOSE_API)
    341       _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
    342 
    343    if (n < 0) {
    344       _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
    345       return;
    346    }
    347 
    348    for (i = 0; i < n; i++) {
    349       if (ids[i] > 0) {
    350          struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
    351          if (q) {
    352             if (q->Active) {
    353                struct gl_query_object **bindpt;
    354                bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
    355                assert(bindpt); /* Should be non-null for active q. */
    356                if (bindpt) {
    357                   *bindpt = NULL;
    358                }
    359                q->Active = GL_FALSE;
    360                ctx->Driver.EndQuery(ctx, q);
    361             }
    362             _mesa_HashRemoveLocked(ctx->Query.QueryObjects, ids[i]);
    363             ctx->Driver.DeleteQuery(ctx, q);
    364          }
    365       }
    366    }
    367 }
    368 
    369 
    370 GLboolean GLAPIENTRY
    371 _mesa_IsQuery(GLuint id)
    372 {
    373    struct gl_query_object *q;
    374 
    375    GET_CURRENT_CONTEXT(ctx);
    376    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
    377 
    378    if (MESA_VERBOSE & VERBOSE_API)
    379       _mesa_debug(ctx, "glIsQuery(%u)\n", id);
    380 
    381    if (id == 0)
    382       return GL_FALSE;
    383 
    384    q = _mesa_lookup_query_object(ctx, id);
    385    if (q == NULL)
    386       return GL_FALSE;
    387 
    388    return q->EverBound;
    389 }
    390 
    391 static GLboolean
    392 query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
    393 {
    394    switch (target) {
    395    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    396    case GL_PRIMITIVES_GENERATED:
    397    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
    398       if (index >= ctx->Const.MaxVertexStreams) {
    399          _mesa_error(ctx, GL_INVALID_VALUE,
    400                      "glBeginQueryIndexed(index>=MaxVertexStreams)");
    401          return GL_FALSE;
    402       }
    403       break;
    404    default:
    405       if (index > 0) {
    406          _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
    407          return GL_FALSE;
    408       }
    409    }
    410    return GL_TRUE;
    411 }
    412 
    413 void GLAPIENTRY
    414 _mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
    415 {
    416    struct gl_query_object *q, **bindpt;
    417    GET_CURRENT_CONTEXT(ctx);
    418 
    419    if (MESA_VERBOSE & VERBOSE_API)
    420       _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
    421                   _mesa_enum_to_string(target), index, id);
    422 
    423    if (!query_error_check_index(ctx, target, index))
    424       return;
    425 
    426    FLUSH_VERTICES(ctx, 0);
    427 
    428    bindpt = get_query_binding_point(ctx, target, index);
    429    if (!bindpt) {
    430       _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
    431       return;
    432    }
    433 
    434    /* From the GL_ARB_occlusion_query spec:
    435     *
    436     *     "If BeginQueryARB is called while another query is already in
    437     *      progress with the same target, an INVALID_OPERATION error is
    438     *      generated."
    439     */
    440    if (*bindpt) {
    441       _mesa_error(ctx, GL_INVALID_OPERATION,
    442                   "glBeginQuery{Indexed}(target=%s is active)",
    443                   _mesa_enum_to_string(target));
    444       return;
    445    }
    446 
    447    if (id == 0) {
    448       _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
    449       return;
    450    }
    451 
    452    q = _mesa_lookup_query_object(ctx, id);
    453    if (!q) {
    454       if (ctx->API != API_OPENGL_COMPAT) {
    455          _mesa_error(ctx, GL_INVALID_OPERATION,
    456                      "glBeginQuery{Indexed}(non-gen name)");
    457          return;
    458       } else {
    459          /* create new object */
    460          q = ctx->Driver.NewQueryObject(ctx, id);
    461          if (!q) {
    462             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
    463             return;
    464          }
    465          _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q);
    466       }
    467    }
    468    else {
    469       /* pre-existing object */
    470       if (q->Active) {
    471          _mesa_error(ctx, GL_INVALID_OPERATION,
    472                      "glBeginQuery{Indexed}(query already active)");
    473          return;
    474       }
    475 
    476       /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4
    477        * spec states:
    478        *
    479        *     "BeginQuery generates an INVALID_OPERATION error if any of the
    480        *      following conditions hold: [...] id is the name of an
    481        *      existing query object whose type does not match target; [...]
    482        *
    483        * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY
    484        * OBJECTS AND ASYNCHRONOUS QUERIES, page 43.
    485        */
    486       if (q->EverBound && q->Target != target) {
    487          _mesa_error(ctx, GL_INVALID_OPERATION,
    488                      "glBeginQuery{Indexed}(target mismatch)");
    489          return;
    490       }
    491    }
    492 
    493    /* This possibly changes the target of a buffer allocated by
    494     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
    495     * the following:
    496     *
    497     * "CreateQueries adds a <target>, so strictly speaking the <target>
    498     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
    499     * isn't a selector, so we decided not to change it."
    500     *
    501     * Updating the target of the query object should be acceptable, so let's
    502     * do that.
    503     */
    504 
    505    q->Target = target;
    506    q->Active = GL_TRUE;
    507    q->Result = 0;
    508    q->Ready = GL_FALSE;
    509    q->EverBound = GL_TRUE;
    510    q->Stream = index;
    511 
    512    /* XXX should probably refcount query objects */
    513    *bindpt = q;
    514 
    515    ctx->Driver.BeginQuery(ctx, q);
    516 }
    517 
    518 
    519 void GLAPIENTRY
    520 _mesa_EndQueryIndexed(GLenum target, GLuint index)
    521 {
    522    struct gl_query_object *q, **bindpt;
    523    GET_CURRENT_CONTEXT(ctx);
    524 
    525    if (MESA_VERBOSE & VERBOSE_API)
    526       _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
    527                   _mesa_enum_to_string(target), index);
    528 
    529    if (!query_error_check_index(ctx, target, index))
    530       return;
    531 
    532    FLUSH_VERTICES(ctx, 0);
    533 
    534    bindpt = get_query_binding_point(ctx, target, index);
    535    if (!bindpt) {
    536       _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
    537       return;
    538    }
    539 
    540    /* XXX should probably refcount query objects */
    541    q = *bindpt;
    542 
    543    /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
    544    if (q && q->Target != target) {
    545       _mesa_error(ctx, GL_INVALID_OPERATION,
    546                   "glEndQuery(target=%s with active query of target %s)",
    547                   _mesa_enum_to_string(target),
    548                   _mesa_enum_to_string(q->Target));
    549       return;
    550    }
    551 
    552    *bindpt = NULL;
    553 
    554    if (!q || !q->Active) {
    555       _mesa_error(ctx, GL_INVALID_OPERATION,
    556                   "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
    557       return;
    558    }
    559 
    560    q->Active = GL_FALSE;
    561    ctx->Driver.EndQuery(ctx, q);
    562 }
    563 
    564 void GLAPIENTRY
    565 _mesa_BeginQuery(GLenum target, GLuint id)
    566 {
    567    _mesa_BeginQueryIndexed(target, 0, id);
    568 }
    569 
    570 void GLAPIENTRY
    571 _mesa_EndQuery(GLenum target)
    572 {
    573    _mesa_EndQueryIndexed(target, 0);
    574 }
    575 
    576 void GLAPIENTRY
    577 _mesa_QueryCounter(GLuint id, GLenum target)
    578 {
    579    struct gl_query_object *q;
    580    GET_CURRENT_CONTEXT(ctx);
    581 
    582    if (MESA_VERBOSE & VERBOSE_API)
    583       _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
    584                   _mesa_enum_to_string(target));
    585 
    586    /* error checking */
    587    if (target != GL_TIMESTAMP) {
    588       _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
    589       return;
    590    }
    591 
    592    if (id == 0) {
    593       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
    594       return;
    595    }
    596 
    597    q = _mesa_lookup_query_object(ctx, id);
    598    if (!q) {
    599       /* XXX the Core profile should throw INVALID_OPERATION here */
    600 
    601       /* create new object */
    602       q = ctx->Driver.NewQueryObject(ctx, id);
    603       if (!q) {
    604          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
    605          return;
    606       }
    607       _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q);
    608    }
    609    else {
    610       if (q->Target && q->Target != GL_TIMESTAMP) {
    611          _mesa_error(ctx, GL_INVALID_OPERATION,
    612                      "glQueryCounter(id has an invalid target)");
    613          return;
    614       }
    615    }
    616 
    617    if (q->Active) {
    618       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
    619       return;
    620    }
    621 
    622    /* This possibly changes the target of a buffer allocated by
    623     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
    624     * the following:
    625     *
    626     * "CreateQueries adds a <target>, so strictly speaking the <target>
    627     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
    628     * isn't a selector, so we decided not to change it."
    629     *
    630     * Updating the target of the query object should be acceptable, so let's
    631     * do that.
    632     */
    633 
    634    q->Target = target;
    635    q->Result = 0;
    636    q->Ready = GL_FALSE;
    637    q->EverBound = GL_TRUE;
    638 
    639    if (ctx->Driver.QueryCounter) {
    640       ctx->Driver.QueryCounter(ctx, q);
    641    } else {
    642       /* QueryCounter is implemented using EndQuery without BeginQuery
    643        * in drivers. This is actually Direct3D and Gallium convention.
    644        */
    645       ctx->Driver.EndQuery(ctx, q);
    646    }
    647 }
    648 
    649 
    650 void GLAPIENTRY
    651 _mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
    652                         GLint *params)
    653 {
    654    struct gl_query_object *q = NULL, **bindpt = NULL;
    655    GET_CURRENT_CONTEXT(ctx);
    656 
    657    if (MESA_VERBOSE & VERBOSE_API)
    658       _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
    659                   _mesa_enum_to_string(target),
    660                   index,
    661                   _mesa_enum_to_string(pname));
    662 
    663    if (!query_error_check_index(ctx, target, index))
    664       return;
    665 
    666    /* From the GL_EXT_occlusion_query_boolean spec:
    667     *
    668     * "The error INVALID_ENUM is generated if GetQueryivEXT is called where
    669     * <pname> is not CURRENT_QUERY_EXT."
    670     *
    671     * Same rule is present also in ES 3.2 spec.
    672     */
    673    if (_mesa_is_gles(ctx) && pname != GL_CURRENT_QUERY) {
    674       _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivEXT(%s)",
    675                   _mesa_enum_to_string(pname));
    676       return;
    677    }
    678 
    679    if (target == GL_TIMESTAMP) {
    680       if (!ctx->Extensions.ARB_timer_query) {
    681          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
    682          return;
    683       }
    684    }
    685    else {
    686       bindpt = get_query_binding_point(ctx, target, index);
    687       if (!bindpt) {
    688          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
    689          return;
    690       }
    691 
    692       q = *bindpt;
    693    }
    694 
    695    switch (pname) {
    696       case GL_QUERY_COUNTER_BITS_ARB:
    697          switch (target) {
    698          case GL_SAMPLES_PASSED:
    699             *params = ctx->Const.QueryCounterBits.SamplesPassed;
    700             break;
    701          case GL_ANY_SAMPLES_PASSED:
    702             /* The minimum value of this is 1 if it's nonzero, and the value
    703              * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
    704              * bits.
    705              */
    706             *params = 1;
    707             break;
    708          case GL_TIME_ELAPSED:
    709             *params = ctx->Const.QueryCounterBits.TimeElapsed;
    710             break;
    711          case GL_TIMESTAMP:
    712             *params = ctx->Const.QueryCounterBits.Timestamp;
    713             break;
    714          case GL_PRIMITIVES_GENERATED:
    715             *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
    716             break;
    717          case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
    718             *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
    719             break;
    720          case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
    721          case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
    722             /* The minimum value of this is 1 if it's nonzero, and the value
    723              * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
    724              * bits.
    725              */
    726             *params = 1;
    727             break;
    728          case GL_VERTICES_SUBMITTED_ARB:
    729             *params = ctx->Const.QueryCounterBits.VerticesSubmitted;
    730             break;
    731          case GL_PRIMITIVES_SUBMITTED_ARB:
    732             *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted;
    733             break;
    734          case GL_VERTEX_SHADER_INVOCATIONS_ARB:
    735             *params = ctx->Const.QueryCounterBits.VsInvocations;
    736             break;
    737          case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
    738             *params = ctx->Const.QueryCounterBits.TessPatches;
    739             break;
    740          case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
    741             *params = ctx->Const.QueryCounterBits.TessInvocations;
    742             break;
    743          case GL_GEOMETRY_SHADER_INVOCATIONS:
    744             *params = ctx->Const.QueryCounterBits.GsInvocations;
    745             break;
    746          case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
    747             *params = ctx->Const.QueryCounterBits.GsPrimitives;
    748             break;
    749          case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
    750             *params = ctx->Const.QueryCounterBits.FsInvocations;
    751             break;
    752          case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
    753             *params = ctx->Const.QueryCounterBits.ComputeInvocations;
    754             break;
    755          case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
    756             *params = ctx->Const.QueryCounterBits.ClInPrimitives;
    757             break;
    758          case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
    759             *params = ctx->Const.QueryCounterBits.ClOutPrimitives;
    760             break;
    761          default:
    762             _mesa_problem(ctx,
    763                           "Unknown target in glGetQueryIndexediv(target = %s)",
    764                           _mesa_enum_to_string(target));
    765             *params = 0;
    766             break;
    767          }
    768          break;
    769       case GL_CURRENT_QUERY_ARB:
    770          *params = (q && q->Target == target) ? q->Id : 0;
    771          break;
    772       default:
    773          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
    774          return;
    775    }
    776 }
    777 
    778 void GLAPIENTRY
    779 _mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
    780 {
    781    _mesa_GetQueryIndexediv(target, 0, pname, params);
    782 }
    783 
    784 static void
    785 get_query_object(struct gl_context *ctx, const char *func,
    786                  GLuint id, GLenum pname, GLenum ptype,
    787                  struct gl_buffer_object *buf, intptr_t offset)
    788 {
    789    struct gl_query_object *q = NULL;
    790    uint64_t value;
    791 
    792    if (MESA_VERBOSE & VERBOSE_API)
    793       _mesa_debug(ctx, "%s(%u, %s)\n", func, id,
    794                   _mesa_enum_to_string(pname));
    795 
    796    if (id)
    797       q = _mesa_lookup_query_object(ctx, id);
    798 
    799    if (!q || q->Active || !q->EverBound) {
    800       _mesa_error(ctx, GL_INVALID_OPERATION,
    801                   "%s(id=%d is invalid or active)", func, id);
    802       return;
    803    }
    804 
    805    /* From GL_EXT_occlusion_query_boolean spec:
    806     *
    807     *    "Accepted by the <pname> parameter of GetQueryObjectivEXT and
    808     *    GetQueryObjectuivEXT:
    809     *
    810     *    QUERY_RESULT_EXT                               0x8866
    811     *    QUERY_RESULT_AVAILABLE_EXT                     0x8867"
    812     *
    813     * Same rule is present also in ES 3.2 spec.
    814     */
    815    if (_mesa_is_gles(ctx) &&
    816        (pname != GL_QUERY_RESULT && pname != GL_QUERY_RESULT_AVAILABLE)) {
    817       _mesa_error(ctx, GL_INVALID_ENUM, "%s(%s)", func,
    818                   _mesa_enum_to_string(pname));
    819       return;
    820    }
    821 
    822    if (buf && buf != ctx->Shared->NullBufferObj) {
    823       bool is_64bit = ptype == GL_INT64_ARB ||
    824          ptype == GL_UNSIGNED_INT64_ARB;
    825       if (!ctx->Extensions.ARB_query_buffer_object &&
    826           !ctx->Extensions.EXT_disjoint_timer_query) {
    827          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func);
    828          return;
    829       }
    830       if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) {
    831          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func);
    832          return;
    833       }
    834 
    835       if (offset < 0) {
    836          _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func);
    837          return;
    838       }
    839 
    840       switch (pname) {
    841       case GL_QUERY_RESULT:
    842       case GL_QUERY_RESULT_NO_WAIT:
    843       case GL_QUERY_RESULT_AVAILABLE:
    844       case GL_QUERY_TARGET:
    845          ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype);
    846          return;
    847       }
    848 
    849       /* fall through to get error below */
    850    }
    851 
    852    switch (pname) {
    853    case GL_QUERY_RESULT:
    854       if (!q->Ready)
    855          ctx->Driver.WaitQuery(ctx, q);
    856       value = q->Result;
    857       break;
    858    case GL_QUERY_RESULT_NO_WAIT:
    859       if (!ctx->Extensions.ARB_query_buffer_object)
    860          goto invalid_enum;
    861       ctx->Driver.CheckQuery(ctx, q);
    862       if (!q->Ready)
    863          return;
    864       value = q->Result;
    865       break;
    866    case GL_QUERY_RESULT_AVAILABLE:
    867       if (!q->Ready)
    868          ctx->Driver.CheckQuery(ctx, q);
    869       value = q->Ready;
    870       break;
    871    case GL_QUERY_TARGET:
    872       value = q->Target;
    873       break;
    874    default:
    875 invalid_enum:
    876       _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)",
    877                   func, _mesa_enum_to_string(pname));
    878       return;
    879    }
    880 
    881    switch (ptype) {
    882    case GL_INT: {
    883       GLint *param = (GLint *)offset;
    884       if (value > 0x7fffffff)
    885          *param = 0x7fffffff;
    886       else
    887          *param = value;
    888       break;
    889    }
    890    case GL_UNSIGNED_INT: {
    891       GLuint *param = (GLuint *)offset;
    892       if (value > 0xffffffff)
    893          *param = 0xffffffff;
    894       else
    895          *param = value;
    896       break;
    897    }
    898    case GL_INT64_ARB:
    899    case GL_UNSIGNED_INT64_ARB: {
    900       GLuint64EXT *param = (GLuint64EXT *)offset;
    901       *param = value;
    902       break;
    903    }
    904    default:
    905       unreachable("unexpected ptype");
    906    }
    907 }
    908 
    909 void GLAPIENTRY
    910 _mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
    911 {
    912    GET_CURRENT_CONTEXT(ctx);
    913 
    914    get_query_object(ctx, "glGetQueryObjectiv",
    915                     id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params);
    916 }
    917 
    918 
    919 void GLAPIENTRY
    920 _mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
    921 {
    922    GET_CURRENT_CONTEXT(ctx);
    923 
    924    get_query_object(ctx, "glGetQueryObjectuiv",
    925                     id, pname, GL_UNSIGNED_INT,
    926                     ctx->QueryBuffer, (intptr_t)params);
    927 }
    928 
    929 
    930 /**
    931  * New with GL_EXT_timer_query
    932  */
    933 void GLAPIENTRY
    934 _mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
    935 {
    936    GET_CURRENT_CONTEXT(ctx);
    937 
    938    get_query_object(ctx, "glGetQueryObjecti64v",
    939                     id, pname, GL_INT64_ARB,
    940                     ctx->QueryBuffer, (intptr_t)params);
    941 }
    942 
    943 
    944 /**
    945  * New with GL_EXT_timer_query
    946  */
    947 void GLAPIENTRY
    948 _mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
    949 {
    950    GET_CURRENT_CONTEXT(ctx);
    951 
    952    get_query_object(ctx, "glGetQueryObjectui64v",
    953                     id, pname, GL_UNSIGNED_INT64_ARB,
    954                     ctx->QueryBuffer, (intptr_t)params);
    955 }
    956 
    957 /**
    958  * New with GL_ARB_query_buffer_object
    959  */
    960 void GLAPIENTRY
    961 _mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname,
    962                              GLintptr offset)
    963 {
    964    struct gl_buffer_object *buf;
    965    GET_CURRENT_CONTEXT(ctx);
    966 
    967    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv");
    968    if (!buf)
    969       return;
    970 
    971    get_query_object(ctx, "glGetQueryBufferObjectiv",
    972                     id, pname, GL_INT, buf, offset);
    973 }
    974 
    975 
    976 void GLAPIENTRY
    977 _mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname,
    978                               GLintptr offset)
    979 {
    980    struct gl_buffer_object *buf;
    981    GET_CURRENT_CONTEXT(ctx);
    982 
    983    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv");
    984    if (!buf)
    985       return;
    986 
    987    get_query_object(ctx, "glGetQueryBufferObjectuiv",
    988                     id, pname, GL_UNSIGNED_INT, buf, offset);
    989 }
    990 
    991 
    992 void GLAPIENTRY
    993 _mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname,
    994                                GLintptr offset)
    995 {
    996    struct gl_buffer_object *buf;
    997    GET_CURRENT_CONTEXT(ctx);
    998 
    999    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v");
   1000    if (!buf)
   1001       return;
   1002 
   1003    get_query_object(ctx, "glGetQueryBufferObjecti64v",
   1004                     id, pname, GL_INT64_ARB, buf, offset);
   1005 }
   1006 
   1007 
   1008 void GLAPIENTRY
   1009 _mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname,
   1010                                 GLintptr offset)
   1011 {
   1012    struct gl_buffer_object *buf;
   1013    GET_CURRENT_CONTEXT(ctx);
   1014 
   1015    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v");
   1016    if (!buf)
   1017       return;
   1018 
   1019    get_query_object(ctx, "glGetQueryBufferObjectui64v",
   1020                     id, pname, GL_UNSIGNED_INT64_ARB, buf, offset);
   1021 }
   1022 
   1023 
   1024 /**
   1025  * Allocate/init the context state related to query objects.
   1026  */
   1027 void
   1028 _mesa_init_queryobj(struct gl_context *ctx)
   1029 {
   1030    ctx->Query.QueryObjects = _mesa_NewHashTable();
   1031    ctx->Query.CurrentOcclusionObject = NULL;
   1032 
   1033    ctx->Const.QueryCounterBits.SamplesPassed = 64;
   1034    ctx->Const.QueryCounterBits.TimeElapsed = 64;
   1035    ctx->Const.QueryCounterBits.Timestamp = 64;
   1036    ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
   1037    ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
   1038 
   1039    ctx->Const.QueryCounterBits.VerticesSubmitted = 64;
   1040    ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64;
   1041    ctx->Const.QueryCounterBits.VsInvocations = 64;
   1042    ctx->Const.QueryCounterBits.TessPatches = 64;
   1043    ctx->Const.QueryCounterBits.TessInvocations = 64;
   1044    ctx->Const.QueryCounterBits.GsInvocations = 64;
   1045    ctx->Const.QueryCounterBits.GsPrimitives = 64;
   1046    ctx->Const.QueryCounterBits.FsInvocations = 64;
   1047    ctx->Const.QueryCounterBits.ComputeInvocations = 64;
   1048    ctx->Const.QueryCounterBits.ClInPrimitives = 64;
   1049    ctx->Const.QueryCounterBits.ClOutPrimitives = 64;
   1050 }
   1051 
   1052 
   1053 /**
   1054  * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
   1055  */
   1056 static void
   1057 delete_queryobj_cb(GLuint id, void *data, void *userData)
   1058 {
   1059    struct gl_query_object *q= (struct gl_query_object *) data;
   1060    struct gl_context *ctx = (struct gl_context *)userData;
   1061    ctx->Driver.DeleteQuery(ctx, q);
   1062 }
   1063 
   1064 
   1065 /**
   1066  * Free the context state related to query objects.
   1067  */
   1068 void
   1069 _mesa_free_queryobj_data(struct gl_context *ctx)
   1070 {
   1071    _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
   1072    _mesa_DeleteHashTable(ctx->Query.QueryObjects);
   1073 }
   1074