queryobj.c revision af69d88d
17117f1b4Smrg/*
27117f1b4Smrg * Mesa 3-D graphics library
37117f1b4Smrg *
4c1f859d4Smrg * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
57117f1b4Smrg *
67117f1b4Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77117f1b4Smrg * copy of this software and associated documentation files (the "Software"),
87117f1b4Smrg * to deal in the Software without restriction, including without limitation
97117f1b4Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
107117f1b4Smrg * and/or sell copies of the Software, and to permit persons to whom the
117117f1b4Smrg * Software is furnished to do so, subject to the following conditions:
127117f1b4Smrg *
137117f1b4Smrg * The above copyright notice and this permission notice shall be included
147117f1b4Smrg * in all copies or substantial portions of the Software.
157117f1b4Smrg *
167117f1b4Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177117f1b4Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187117f1b4Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE.
237117f1b4Smrg */
247117f1b4Smrg
257117f1b4Smrg
267117f1b4Smrg#include "glheader.h"
277117f1b4Smrg#include "context.h"
283464ebd5Sriastradh#include "enums.h"
297117f1b4Smrg#include "hash.h"
307117f1b4Smrg#include "imports.h"
317117f1b4Smrg#include "queryobj.h"
327117f1b4Smrg#include "mtypes.h"
33cdc920a0Smrg#include "main/dispatch.h"
344a49301eSmrg
354a49301eSmrg
367117f1b4Smrg/**
377117f1b4Smrg * Allocate a new query object.  This is a fallback routine called via
387117f1b4Smrg * ctx->Driver.NewQueryObject().
397117f1b4Smrg * \param ctx - rendering context
407117f1b4Smrg * \param id - the new object's ID
417117f1b4Smrg * \return pointer to new query_object object or NULL if out of memory.
427117f1b4Smrg */
434a49301eSmrgstatic struct gl_query_object *
443464ebd5Sriastradh_mesa_new_query_object(struct gl_context *ctx, GLuint id)
457117f1b4Smrg{
46af69d88dSmrg   struct gl_query_object *q = CALLOC_STRUCT(gl_query_object);
477117f1b4Smrg   (void) ctx;
487117f1b4Smrg   if (q) {
497117f1b4Smrg      q->Id = id;
507117f1b4Smrg      q->Result = 0;
517117f1b4Smrg      q->Active = GL_FALSE;
52af69d88dSmrg
53af69d88dSmrg      /* This is to satisfy the language of the specification: "In the initial
54af69d88dSmrg       * state of a query object, the result is available" (OpenGL 3.1 §
55af69d88dSmrg       * 2.13).
56af69d88dSmrg       */
57af69d88dSmrg      q->Ready = GL_TRUE;
58af69d88dSmrg
59af69d88dSmrg      /* OpenGL 3.1 § 2.13 says about GenQueries, "These names are marked as
60af69d88dSmrg       * used, but no object is associated with them until the first time they
61af69d88dSmrg       * are used by BeginQuery." Since our implementation actually does
62af69d88dSmrg       * allocate an object at this point, use a flag to indicate that this
63af69d88dSmrg       * object has not yet been bound so should not be considered a query.
64af69d88dSmrg       */
65af69d88dSmrg      q->EverBound = GL_FALSE;
667117f1b4Smrg   }
677117f1b4Smrg   return q;
687117f1b4Smrg}
697117f1b4Smrg
707117f1b4Smrg
717117f1b4Smrg/**
72c1f859d4Smrg * Begin a query.  Software driver fallback.
73c1f859d4Smrg * Called via ctx->Driver.BeginQuery().
74c1f859d4Smrg */
754a49301eSmrgstatic void
763464ebd5Sriastradh_mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
77c1f859d4Smrg{
78af69d88dSmrg   ctx->NewState |= _NEW_DEPTH; /* for swrast */
79c1f859d4Smrg}
80c1f859d4Smrg
81c1f859d4Smrg
82c1f859d4Smrg/**
83c1f859d4Smrg * End a query.  Software driver fallback.
84c1f859d4Smrg * Called via ctx->Driver.EndQuery().
85c1f859d4Smrg */
864a49301eSmrgstatic void
873464ebd5Sriastradh_mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
88c1f859d4Smrg{
89af69d88dSmrg   ctx->NewState |= _NEW_DEPTH; /* for swrast */
90c1f859d4Smrg   q->Ready = GL_TRUE;
91c1f859d4Smrg}
92c1f859d4Smrg
93c1f859d4Smrg
94c1f859d4Smrg/**
95c1f859d4Smrg * Wait for query to complete.  Software driver fallback.
96c1f859d4Smrg * Called via ctx->Driver.WaitQuery().
97c1f859d4Smrg */
984a49301eSmrgstatic void
993464ebd5Sriastradh_mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
100c1f859d4Smrg{
101c1f859d4Smrg   /* For software drivers, _mesa_end_query() should have completed the query.
1024a49301eSmrg    * For real hardware, implement a proper WaitQuery() driver function,
1034a49301eSmrg    * which may require issuing a flush.
104c1f859d4Smrg    */
105c1f859d4Smrg   assert(q->Ready);
106c1f859d4Smrg}
107c1f859d4Smrg
108c1f859d4Smrg
1094a49301eSmrg/**
1104a49301eSmrg * Check if a query results are ready.  Software driver fallback.
1114a49301eSmrg * Called via ctx->Driver.CheckQuery().
1124a49301eSmrg */
1134a49301eSmrgstatic void
1143464ebd5Sriastradh_mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
1154a49301eSmrg{
1164a49301eSmrg   /* No-op for sw rendering.
1174a49301eSmrg    * HW drivers may need to flush at this time.
1184a49301eSmrg    */
1194a49301eSmrg}
1204a49301eSmrg
1214a49301eSmrg
122c1f859d4Smrg/**
123c1f859d4Smrg * Delete a query object.  Called via ctx->Driver.DeleteQuery().
1247117f1b4Smrg * Not removed from hash table here.
1257117f1b4Smrg */
1264a49301eSmrgstatic void
1273464ebd5Sriastradh_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
1287117f1b4Smrg{
129af69d88dSmrg   free(q->Label);
130cdc920a0Smrg   free(q);
1317117f1b4Smrg}
1327117f1b4Smrg
1337117f1b4Smrg
1344a49301eSmrgvoid
1354a49301eSmrg_mesa_init_query_object_functions(struct dd_function_table *driver)
1364a49301eSmrg{
1374a49301eSmrg   driver->NewQueryObject = _mesa_new_query_object;
1384a49301eSmrg   driver->DeleteQuery = _mesa_delete_query;
1394a49301eSmrg   driver->BeginQuery = _mesa_begin_query;
1404a49301eSmrg   driver->EndQuery = _mesa_end_query;
1414a49301eSmrg   driver->WaitQuery = _mesa_wait_query;
1424a49301eSmrg   driver->CheckQuery = _mesa_check_query;
1434a49301eSmrg}
1444a49301eSmrg
1454a49301eSmrg
1463464ebd5Sriastradh/**
147af69d88dSmrg * Return pointer to the query object binding point for the given target and
148af69d88dSmrg * index.
1493464ebd5Sriastradh * \return NULL if invalid target, else the address of binding point
1503464ebd5Sriastradh */
1513464ebd5Sriastradhstatic struct gl_query_object **
152af69d88dSmrgget_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
1533464ebd5Sriastradh{
1543464ebd5Sriastradh   switch (target) {
1553464ebd5Sriastradh   case GL_SAMPLES_PASSED_ARB:
1563464ebd5Sriastradh      if (ctx->Extensions.ARB_occlusion_query)
1573464ebd5Sriastradh         return &ctx->Query.CurrentOcclusionObject;
1583464ebd5Sriastradh      else
1593464ebd5Sriastradh         return NULL;
1603464ebd5Sriastradh   case GL_ANY_SAMPLES_PASSED:
1613464ebd5Sriastradh      if (ctx->Extensions.ARB_occlusion_query2)
1623464ebd5Sriastradh         return &ctx->Query.CurrentOcclusionObject;
1633464ebd5Sriastradh      else
1643464ebd5Sriastradh         return NULL;
165af69d88dSmrg   case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
166af69d88dSmrg      if (ctx->Extensions.ARB_ES3_compatibility
167af69d88dSmrg          || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
168af69d88dSmrg         return &ctx->Query.CurrentOcclusionObject;
169af69d88dSmrg      else
170af69d88dSmrg         return NULL;
1713464ebd5Sriastradh   case GL_TIME_ELAPSED_EXT:
1723464ebd5Sriastradh      if (ctx->Extensions.EXT_timer_query)
1733464ebd5Sriastradh         return &ctx->Query.CurrentTimerObject;
1743464ebd5Sriastradh      else
1753464ebd5Sriastradh         return NULL;
1763464ebd5Sriastradh   case GL_PRIMITIVES_GENERATED:
1773464ebd5Sriastradh      if (ctx->Extensions.EXT_transform_feedback)
178af69d88dSmrg         return &ctx->Query.PrimitivesGenerated[index];
1793464ebd5Sriastradh      else
1803464ebd5Sriastradh         return NULL;
1813464ebd5Sriastradh   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
1823464ebd5Sriastradh      if (ctx->Extensions.EXT_transform_feedback)
183af69d88dSmrg         return &ctx->Query.PrimitivesWritten[index];
1843464ebd5Sriastradh      else
1853464ebd5Sriastradh         return NULL;
1863464ebd5Sriastradh   default:
1873464ebd5Sriastradh      return NULL;
1883464ebd5Sriastradh   }
1893464ebd5Sriastradh}
1903464ebd5Sriastradh
1913464ebd5Sriastradh
192af69d88dSmrgvoid GLAPIENTRY
193af69d88dSmrg_mesa_GenQueries(GLsizei n, GLuint *ids)
1947117f1b4Smrg{
1957117f1b4Smrg   GLuint first;
1967117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
1977117f1b4Smrg
1983464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
1993464ebd5Sriastradh      _mesa_debug(ctx, "glGenQueries(%d)\n", n);
2003464ebd5Sriastradh
2017117f1b4Smrg   if (n < 0) {
2027117f1b4Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
2037117f1b4Smrg      return;
2047117f1b4Smrg   }
2057117f1b4Smrg
2067117f1b4Smrg   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
2077117f1b4Smrg   if (first) {
2087117f1b4Smrg      GLsizei i;
2097117f1b4Smrg      for (i = 0; i < n; i++) {
2107117f1b4Smrg         struct gl_query_object *q
2117117f1b4Smrg            = ctx->Driver.NewQueryObject(ctx, first + i);
2127117f1b4Smrg         if (!q) {
2137117f1b4Smrg            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
2147117f1b4Smrg            return;
2157117f1b4Smrg         }
2167117f1b4Smrg         ids[i] = first + i;
2177117f1b4Smrg         _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
2187117f1b4Smrg      }
2197117f1b4Smrg   }
2207117f1b4Smrg}
2217117f1b4Smrg
2227117f1b4Smrg
223af69d88dSmrgvoid GLAPIENTRY
224af69d88dSmrg_mesa_DeleteQueries(GLsizei n, const GLuint *ids)
2257117f1b4Smrg{
2267117f1b4Smrg   GLint i;
2277117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
2283464ebd5Sriastradh   FLUSH_VERTICES(ctx, 0);
2293464ebd5Sriastradh
2303464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
231af69d88dSmrg      _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
2327117f1b4Smrg
2337117f1b4Smrg   if (n < 0) {
2347117f1b4Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
2357117f1b4Smrg      return;
2367117f1b4Smrg   }
2377117f1b4Smrg
2387117f1b4Smrg   for (i = 0; i < n; i++) {
2397117f1b4Smrg      if (ids[i] > 0) {
240cdc920a0Smrg         struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
2417117f1b4Smrg         if (q) {
242af69d88dSmrg            if (q->Active) {
243af69d88dSmrg               struct gl_query_object **bindpt;
244af69d88dSmrg               bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
245af69d88dSmrg               assert(bindpt); /* Should be non-null for active q. */
246af69d88dSmrg               if (bindpt) {
247af69d88dSmrg                  *bindpt = NULL;
248af69d88dSmrg               }
249af69d88dSmrg               q->Active = GL_FALSE;
250af69d88dSmrg               ctx->Driver.EndQuery(ctx, q);
251af69d88dSmrg            }
2527117f1b4Smrg            _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
253c1f859d4Smrg            ctx->Driver.DeleteQuery(ctx, q);
2547117f1b4Smrg         }
2557117f1b4Smrg      }
2567117f1b4Smrg   }
2577117f1b4Smrg}
2587117f1b4Smrg
2597117f1b4Smrg
260af69d88dSmrgGLboolean GLAPIENTRY
261af69d88dSmrg_mesa_IsQuery(GLuint id)
2627117f1b4Smrg{
263af69d88dSmrg   struct gl_query_object *q;
264af69d88dSmrg
2657117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
2667117f1b4Smrg   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
2677117f1b4Smrg
2683464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
2693464ebd5Sriastradh      _mesa_debug(ctx, "glIsQuery(%u)\n", id);
2703464ebd5Sriastradh
271af69d88dSmrg   if (id == 0)
272af69d88dSmrg      return GL_FALSE;
273af69d88dSmrg
274af69d88dSmrg   q = _mesa_lookup_query_object(ctx, id);
275af69d88dSmrg   if (q == NULL)
2767117f1b4Smrg      return GL_FALSE;
277af69d88dSmrg
278af69d88dSmrg   return q->EverBound;
2797117f1b4Smrg}
2807117f1b4Smrg
281af69d88dSmrgstatic GLboolean
282af69d88dSmrgquery_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
283af69d88dSmrg{
284af69d88dSmrg   switch (target) {
285af69d88dSmrg   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
286af69d88dSmrg   case GL_PRIMITIVES_GENERATED:
287af69d88dSmrg      if (index >= ctx->Const.MaxVertexStreams) {
288af69d88dSmrg         _mesa_error(ctx, GL_INVALID_VALUE,
289af69d88dSmrg                     "glBeginQueryIndexed(index>=MaxVertexStreams)");
290af69d88dSmrg         return GL_FALSE;
291af69d88dSmrg      }
292af69d88dSmrg      break;
293af69d88dSmrg   default:
294af69d88dSmrg      if (index > 0) {
295af69d88dSmrg         _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
296af69d88dSmrg         return GL_FALSE;
297af69d88dSmrg      }
298af69d88dSmrg   }
299af69d88dSmrg   return GL_TRUE;
300af69d88dSmrg}
3017117f1b4Smrg
302af69d88dSmrgvoid GLAPIENTRY
303af69d88dSmrg_mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
3047117f1b4Smrg{
3053464ebd5Sriastradh   struct gl_query_object *q, **bindpt;
3067117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
3077117f1b4Smrg
3083464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
309af69d88dSmrg      _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
310af69d88dSmrg                  _mesa_lookup_enum_by_nr(target), index, id);
3113464ebd5Sriastradh
312af69d88dSmrg   if (!query_error_check_index(ctx, target, index))
313af69d88dSmrg      return;
314af69d88dSmrg
315af69d88dSmrg   FLUSH_VERTICES(ctx, 0);
3167117f1b4Smrg
317af69d88dSmrg   bindpt = get_query_binding_point(ctx, target, index);
3183464ebd5Sriastradh   if (!bindpt) {
319af69d88dSmrg      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
320af69d88dSmrg      return;
321af69d88dSmrg   }
322af69d88dSmrg
323af69d88dSmrg   /* From the GL_ARB_occlusion_query spec:
324af69d88dSmrg    *
325af69d88dSmrg    *     "If BeginQueryARB is called while another query is already in
326af69d88dSmrg    *      progress with the same target, an INVALID_OPERATION error is
327af69d88dSmrg    *      generated."
328af69d88dSmrg    */
329af69d88dSmrg   if (*bindpt) {
330af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
331af69d88dSmrg                  "glBeginQuery{Indexed}(target=%s is active)",
332af69d88dSmrg                  _mesa_lookup_enum_by_nr(target));
3333464ebd5Sriastradh      return;
3347117f1b4Smrg   }
3357117f1b4Smrg
3367117f1b4Smrg   if (id == 0) {
337af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
3387117f1b4Smrg      return;
3397117f1b4Smrg   }
3407117f1b4Smrg
341cdc920a0Smrg   q = _mesa_lookup_query_object(ctx, id);
3427117f1b4Smrg   if (!q) {
343af69d88dSmrg      if (ctx->API != API_OPENGL_COMPAT) {
344af69d88dSmrg         _mesa_error(ctx, GL_INVALID_OPERATION,
345af69d88dSmrg                     "glBeginQuery{Indexed}(non-gen name)");
3467117f1b4Smrg         return;
347af69d88dSmrg      } else {
348af69d88dSmrg         /* create new object */
349af69d88dSmrg         q = ctx->Driver.NewQueryObject(ctx, id);
350af69d88dSmrg         if (!q) {
351af69d88dSmrg            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
352af69d88dSmrg            return;
353af69d88dSmrg         }
354af69d88dSmrg         _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
3557117f1b4Smrg      }
3567117f1b4Smrg   }
3577117f1b4Smrg   else {
3587117f1b4Smrg      /* pre-existing object */
3597117f1b4Smrg      if (q->Active) {
3607117f1b4Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
361af69d88dSmrg                     "glBeginQuery{Indexed}(query already active)");
3627117f1b4Smrg         return;
3637117f1b4Smrg      }
3647117f1b4Smrg   }
3657117f1b4Smrg
366c1f859d4Smrg   q->Target = target;
3677117f1b4Smrg   q->Active = GL_TRUE;
3687117f1b4Smrg   q->Result = 0;
3697117f1b4Smrg   q->Ready = GL_FALSE;
370af69d88dSmrg   q->EverBound = GL_TRUE;
371af69d88dSmrg   q->Stream = index;
3727117f1b4Smrg
3733464ebd5Sriastradh   /* XXX should probably refcount query objects */
3743464ebd5Sriastradh   *bindpt = q;
3757117f1b4Smrg
376c1f859d4Smrg   ctx->Driver.BeginQuery(ctx, q);
3777117f1b4Smrg}
3787117f1b4Smrg
3797117f1b4Smrg
380af69d88dSmrgvoid GLAPIENTRY
381af69d88dSmrg_mesa_EndQueryIndexed(GLenum target, GLuint index)
3827117f1b4Smrg{
3833464ebd5Sriastradh   struct gl_query_object *q, **bindpt;
3847117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
3857117f1b4Smrg
3863464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
387af69d88dSmrg      _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
388af69d88dSmrg                  _mesa_lookup_enum_by_nr(target), index);
3893464ebd5Sriastradh
390af69d88dSmrg   if (!query_error_check_index(ctx, target, index))
391af69d88dSmrg      return;
3927117f1b4Smrg
393af69d88dSmrg   FLUSH_VERTICES(ctx, 0);
394af69d88dSmrg
395af69d88dSmrg   bindpt = get_query_binding_point(ctx, target, index);
3963464ebd5Sriastradh   if (!bindpt) {
397af69d88dSmrg      _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
3983464ebd5Sriastradh      return;
3997117f1b4Smrg   }
4007117f1b4Smrg
4013464ebd5Sriastradh   /* XXX should probably refcount query objects */
4023464ebd5Sriastradh   q = *bindpt;
403af69d88dSmrg
404af69d88dSmrg   /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
405af69d88dSmrg   if (q && q->Target != target) {
406af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
407af69d88dSmrg                  "glEndQuery(target=%s with active query of target %s)",
408af69d88dSmrg                  _mesa_lookup_enum_by_nr(target),
409af69d88dSmrg                  _mesa_lookup_enum_by_nr(q->Target));
410af69d88dSmrg      return;
411af69d88dSmrg   }
412af69d88dSmrg
4133464ebd5Sriastradh   *bindpt = NULL;
4143464ebd5Sriastradh
4157117f1b4Smrg   if (!q || !q->Active) {
4167117f1b4Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
417af69d88dSmrg                  "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
4187117f1b4Smrg      return;
4197117f1b4Smrg   }
4207117f1b4Smrg
4217117f1b4Smrg   q->Active = GL_FALSE;
422c1f859d4Smrg   ctx->Driver.EndQuery(ctx, q);
4237117f1b4Smrg}
4247117f1b4Smrg
425af69d88dSmrgvoid GLAPIENTRY
426af69d88dSmrg_mesa_BeginQuery(GLenum target, GLuint id)
427af69d88dSmrg{
428af69d88dSmrg   _mesa_BeginQueryIndexed(target, 0, id);
429af69d88dSmrg}
4307117f1b4Smrg
431af69d88dSmrgvoid GLAPIENTRY
432af69d88dSmrg_mesa_EndQuery(GLenum target)
4337117f1b4Smrg{
434af69d88dSmrg   _mesa_EndQueryIndexed(target, 0);
435af69d88dSmrg}
436af69d88dSmrg
437af69d88dSmrgvoid GLAPIENTRY
438af69d88dSmrg_mesa_QueryCounter(GLuint id, GLenum target)
439af69d88dSmrg{
440af69d88dSmrg   struct gl_query_object *q;
4417117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
4427117f1b4Smrg
4433464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
444af69d88dSmrg      _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
445af69d88dSmrg                  _mesa_lookup_enum_by_nr(target));
446af69d88dSmrg
447af69d88dSmrg   /* error checking */
448af69d88dSmrg   if (target != GL_TIMESTAMP) {
449af69d88dSmrg      _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
450af69d88dSmrg      return;
451af69d88dSmrg   }
452af69d88dSmrg
453af69d88dSmrg   if (id == 0) {
454af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
455af69d88dSmrg      return;
456af69d88dSmrg   }
457af69d88dSmrg
458af69d88dSmrg   q = _mesa_lookup_query_object(ctx, id);
459af69d88dSmrg   if (!q) {
460af69d88dSmrg      /* XXX the Core profile should throw INVALID_OPERATION here */
461af69d88dSmrg
462af69d88dSmrg      /* create new object */
463af69d88dSmrg      q = ctx->Driver.NewQueryObject(ctx, id);
464af69d88dSmrg      if (!q) {
465af69d88dSmrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
466af69d88dSmrg         return;
467af69d88dSmrg      }
468af69d88dSmrg      _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
469af69d88dSmrg   }
470af69d88dSmrg   else {
471af69d88dSmrg      if (q->Target && q->Target != GL_TIMESTAMP) {
472af69d88dSmrg         _mesa_error(ctx, GL_INVALID_OPERATION,
473af69d88dSmrg                     "glQueryCounter(id has an invalid target)");
474af69d88dSmrg         return;
475af69d88dSmrg      }
476af69d88dSmrg   }
477af69d88dSmrg
478af69d88dSmrg   if (q->Active) {
479af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
480af69d88dSmrg      return;
481af69d88dSmrg   }
482af69d88dSmrg
483af69d88dSmrg   q->Target = target;
484af69d88dSmrg   q->Result = 0;
485af69d88dSmrg   q->Ready = GL_FALSE;
486af69d88dSmrg   q->EverBound = GL_TRUE;
487af69d88dSmrg
488af69d88dSmrg   if (ctx->Driver.QueryCounter) {
489af69d88dSmrg      ctx->Driver.QueryCounter(ctx, q);
490af69d88dSmrg   } else {
491af69d88dSmrg      /* QueryCounter is implemented using EndQuery without BeginQuery
492af69d88dSmrg       * in drivers. This is actually Direct3D and Gallium convention.
493af69d88dSmrg       */
494af69d88dSmrg      ctx->Driver.EndQuery(ctx, q);
495af69d88dSmrg   }
496af69d88dSmrg}
497af69d88dSmrg
498af69d88dSmrg
499af69d88dSmrgvoid GLAPIENTRY
500af69d88dSmrg_mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
501af69d88dSmrg                        GLint *params)
502af69d88dSmrg{
503af69d88dSmrg   struct gl_query_object *q = NULL, **bindpt = NULL;
504af69d88dSmrg   GET_CURRENT_CONTEXT(ctx);
505af69d88dSmrg
506af69d88dSmrg   if (MESA_VERBOSE & VERBOSE_API)
507af69d88dSmrg      _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
5083464ebd5Sriastradh                  _mesa_lookup_enum_by_nr(target),
509af69d88dSmrg                  index,
5103464ebd5Sriastradh                  _mesa_lookup_enum_by_nr(pname));
5113464ebd5Sriastradh
512af69d88dSmrg   if (!query_error_check_index(ctx, target, index))
5133464ebd5Sriastradh      return;
514af69d88dSmrg
515af69d88dSmrg   if (target == GL_TIMESTAMP) {
516af69d88dSmrg      if (!ctx->Extensions.ARB_timer_query) {
517af69d88dSmrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
518af69d88dSmrg         return;
519af69d88dSmrg      }
5207117f1b4Smrg   }
521af69d88dSmrg   else {
522af69d88dSmrg      bindpt = get_query_binding_point(ctx, target, index);
523af69d88dSmrg      if (!bindpt) {
524af69d88dSmrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
525af69d88dSmrg         return;
526af69d88dSmrg      }
5277117f1b4Smrg
528af69d88dSmrg      q = *bindpt;
529af69d88dSmrg   }
5303464ebd5Sriastradh
5317117f1b4Smrg   switch (pname) {
5327117f1b4Smrg      case GL_QUERY_COUNTER_BITS_ARB:
533af69d88dSmrg         switch (target) {
534af69d88dSmrg         case GL_SAMPLES_PASSED:
535af69d88dSmrg            *params = ctx->Const.QueryCounterBits.SamplesPassed;
536af69d88dSmrg            break;
537af69d88dSmrg         case GL_ANY_SAMPLES_PASSED:
538af69d88dSmrg            /* The minimum value of this is 1 if it's nonzero, and the value
539af69d88dSmrg             * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
540af69d88dSmrg             * bits.
541af69d88dSmrg             */
542af69d88dSmrg            *params = 1;
543af69d88dSmrg            break;
544af69d88dSmrg         case GL_TIME_ELAPSED:
545af69d88dSmrg            *params = ctx->Const.QueryCounterBits.TimeElapsed;
546af69d88dSmrg            break;
547af69d88dSmrg         case GL_TIMESTAMP:
548af69d88dSmrg            *params = ctx->Const.QueryCounterBits.Timestamp;
549af69d88dSmrg            break;
550af69d88dSmrg         case GL_PRIMITIVES_GENERATED:
551af69d88dSmrg            *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
552af69d88dSmrg            break;
553af69d88dSmrg         case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
554af69d88dSmrg            *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
555af69d88dSmrg            break;
556af69d88dSmrg         default:
557af69d88dSmrg            _mesa_problem(ctx,
558af69d88dSmrg                          "Unknown target in glGetQueryIndexediv(target = %s)",
559af69d88dSmrg                          _mesa_lookup_enum_by_nr(target));
560af69d88dSmrg            *params = 0;
561af69d88dSmrg            break;
562af69d88dSmrg         }
5637117f1b4Smrg         break;
5647117f1b4Smrg      case GL_CURRENT_QUERY_ARB:
565af69d88dSmrg         *params = (q && q->Target == target) ? q->Id : 0;
5667117f1b4Smrg         break;
5677117f1b4Smrg      default:
568af69d88dSmrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
5697117f1b4Smrg         return;
5707117f1b4Smrg   }
5717117f1b4Smrg}
5727117f1b4Smrg
573af69d88dSmrgvoid GLAPIENTRY
574af69d88dSmrg_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
575af69d88dSmrg{
576af69d88dSmrg   _mesa_GetQueryIndexediv(target, 0, pname, params);
577af69d88dSmrg}
5787117f1b4Smrg
579af69d88dSmrgvoid GLAPIENTRY
580af69d88dSmrg_mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
5817117f1b4Smrg{
5827117f1b4Smrg   struct gl_query_object *q = NULL;
5837117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
5847117f1b4Smrg
5853464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
5863464ebd5Sriastradh      _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id,
5873464ebd5Sriastradh                  _mesa_lookup_enum_by_nr(pname));
5883464ebd5Sriastradh
5897117f1b4Smrg   if (id)
590cdc920a0Smrg      q = _mesa_lookup_query_object(ctx, id);
5917117f1b4Smrg
5927117f1b4Smrg   if (!q || q->Active) {
5937117f1b4Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
5947117f1b4Smrg                  "glGetQueryObjectivARB(id=%d is invalid or active)", id);
5957117f1b4Smrg      return;
5967117f1b4Smrg   }
5977117f1b4Smrg
5987117f1b4Smrg   switch (pname) {
5997117f1b4Smrg      case GL_QUERY_RESULT_ARB:
600c1f859d4Smrg         if (!q->Ready)
601c1f859d4Smrg            ctx->Driver.WaitQuery(ctx, q);
6027117f1b4Smrg         /* if result is too large for returned type, clamp to max value */
603af69d88dSmrg         if (q->Target == GL_ANY_SAMPLES_PASSED
604af69d88dSmrg             || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
6053464ebd5Sriastradh            if (q->Result)
6063464ebd5Sriastradh               *params = GL_TRUE;
6073464ebd5Sriastradh            else
6083464ebd5Sriastradh               *params = GL_FALSE;
6093464ebd5Sriastradh         } else {
6103464ebd5Sriastradh            if (q->Result > 0x7fffffff) {
6113464ebd5Sriastradh               *params = 0x7fffffff;
6123464ebd5Sriastradh            }
6133464ebd5Sriastradh            else {
6143464ebd5Sriastradh               *params = (GLint)q->Result;
6153464ebd5Sriastradh            }
6167117f1b4Smrg         }
6177117f1b4Smrg         break;
6187117f1b4Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
619c1f859d4Smrg	 if (!q->Ready)
620c1f859d4Smrg	    ctx->Driver.CheckQuery( ctx, q );
6217117f1b4Smrg         *params = q->Ready;
6227117f1b4Smrg         break;
6237117f1b4Smrg      default:
6247117f1b4Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
6257117f1b4Smrg         return;
6267117f1b4Smrg   }
6277117f1b4Smrg}
6287117f1b4Smrg
6297117f1b4Smrg
630af69d88dSmrgvoid GLAPIENTRY
631af69d88dSmrg_mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
6327117f1b4Smrg{
6337117f1b4Smrg   struct gl_query_object *q = NULL;
6347117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
6357117f1b4Smrg
6363464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
6373464ebd5Sriastradh      _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id,
6383464ebd5Sriastradh                  _mesa_lookup_enum_by_nr(pname));
6393464ebd5Sriastradh
6407117f1b4Smrg   if (id)
641cdc920a0Smrg      q = _mesa_lookup_query_object(ctx, id);
6427117f1b4Smrg
6437117f1b4Smrg   if (!q || q->Active) {
6447117f1b4Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
6457117f1b4Smrg                  "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
6467117f1b4Smrg      return;
6477117f1b4Smrg   }
6487117f1b4Smrg
6497117f1b4Smrg   switch (pname) {
6507117f1b4Smrg      case GL_QUERY_RESULT_ARB:
651c1f859d4Smrg         if (!q->Ready)
652c1f859d4Smrg            ctx->Driver.WaitQuery(ctx, q);
6537117f1b4Smrg         /* if result is too large for returned type, clamp to max value */
654af69d88dSmrg         if (q->Target == GL_ANY_SAMPLES_PASSED
655af69d88dSmrg             || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
6563464ebd5Sriastradh            if (q->Result)
6573464ebd5Sriastradh               *params = GL_TRUE;
6583464ebd5Sriastradh            else
6593464ebd5Sriastradh               *params = GL_FALSE;
6603464ebd5Sriastradh         } else {
6613464ebd5Sriastradh            if (q->Result > 0xffffffff) {
6623464ebd5Sriastradh               *params = 0xffffffff;
6633464ebd5Sriastradh            }
6643464ebd5Sriastradh            else {
6653464ebd5Sriastradh               *params = (GLuint)q->Result;
6663464ebd5Sriastradh            }
6677117f1b4Smrg         }
6687117f1b4Smrg         break;
6697117f1b4Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
670c1f859d4Smrg	 if (!q->Ready)
671c1f859d4Smrg	    ctx->Driver.CheckQuery( ctx, q );
6727117f1b4Smrg         *params = q->Ready;
6737117f1b4Smrg         break;
6747117f1b4Smrg      default:
6757117f1b4Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
6767117f1b4Smrg         return;
6777117f1b4Smrg   }
6787117f1b4Smrg}
6797117f1b4Smrg
6807117f1b4Smrg
6817117f1b4Smrg/**
6827117f1b4Smrg * New with GL_EXT_timer_query
6837117f1b4Smrg */
684af69d88dSmrgvoid GLAPIENTRY
685af69d88dSmrg_mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
6867117f1b4Smrg{
6877117f1b4Smrg   struct gl_query_object *q = NULL;
6887117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
6897117f1b4Smrg
6903464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
6913464ebd5Sriastradh      _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id,
6923464ebd5Sriastradh                  _mesa_lookup_enum_by_nr(pname));
6933464ebd5Sriastradh
6947117f1b4Smrg   if (id)
695cdc920a0Smrg      q = _mesa_lookup_query_object(ctx, id);
6967117f1b4Smrg
6977117f1b4Smrg   if (!q || q->Active) {
6987117f1b4Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
6997117f1b4Smrg                  "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
7007117f1b4Smrg      return;
7017117f1b4Smrg   }
7027117f1b4Smrg
7037117f1b4Smrg   switch (pname) {
7047117f1b4Smrg      case GL_QUERY_RESULT_ARB:
705c1f859d4Smrg         if (!q->Ready)
706c1f859d4Smrg            ctx->Driver.WaitQuery(ctx, q);
7077117f1b4Smrg         *params = q->Result;
7087117f1b4Smrg         break;
7097117f1b4Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
710c1f859d4Smrg	 if (!q->Ready)
711c1f859d4Smrg	    ctx->Driver.CheckQuery( ctx, q );
7127117f1b4Smrg         *params = q->Ready;
7137117f1b4Smrg         break;
7147117f1b4Smrg      default:
7157117f1b4Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
7167117f1b4Smrg         return;
7177117f1b4Smrg   }
7187117f1b4Smrg}
7197117f1b4Smrg
7207117f1b4Smrg
7217117f1b4Smrg/**
7227117f1b4Smrg * New with GL_EXT_timer_query
7237117f1b4Smrg */
724af69d88dSmrgvoid GLAPIENTRY
725af69d88dSmrg_mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
7267117f1b4Smrg{
7277117f1b4Smrg   struct gl_query_object *q = NULL;
7287117f1b4Smrg   GET_CURRENT_CONTEXT(ctx);
7297117f1b4Smrg
7303464ebd5Sriastradh   if (MESA_VERBOSE & VERBOSE_API)
7313464ebd5Sriastradh      _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id,
7323464ebd5Sriastradh                  _mesa_lookup_enum_by_nr(pname));
7333464ebd5Sriastradh
7347117f1b4Smrg   if (id)
735cdc920a0Smrg      q = _mesa_lookup_query_object(ctx, id);
7367117f1b4Smrg
7377117f1b4Smrg   if (!q || q->Active) {
7387117f1b4Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
7397117f1b4Smrg                  "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
7407117f1b4Smrg      return;
7417117f1b4Smrg   }
7427117f1b4Smrg
7437117f1b4Smrg   switch (pname) {
7447117f1b4Smrg      case GL_QUERY_RESULT_ARB:
745c1f859d4Smrg         if (!q->Ready)
746c1f859d4Smrg            ctx->Driver.WaitQuery(ctx, q);
7477117f1b4Smrg         *params = q->Result;
7487117f1b4Smrg         break;
7497117f1b4Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
750c1f859d4Smrg	 if (!q->Ready)
751c1f859d4Smrg	    ctx->Driver.CheckQuery( ctx, q );
7527117f1b4Smrg         *params = q->Ready;
7537117f1b4Smrg         break;
7547117f1b4Smrg      default:
7557117f1b4Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
7567117f1b4Smrg         return;
7577117f1b4Smrg   }
7587117f1b4Smrg}
7597117f1b4Smrg
7607117f1b4Smrg/**
7617117f1b4Smrg * Allocate/init the context state related to query objects.
7627117f1b4Smrg */
7637117f1b4Smrgvoid
7643464ebd5Sriastradh_mesa_init_queryobj(struct gl_context *ctx)
7657117f1b4Smrg{
7667117f1b4Smrg   ctx->Query.QueryObjects = _mesa_NewHashTable();
7677117f1b4Smrg   ctx->Query.CurrentOcclusionObject = NULL;
768af69d88dSmrg
769af69d88dSmrg   ctx->Const.QueryCounterBits.SamplesPassed = 64;
770af69d88dSmrg   ctx->Const.QueryCounterBits.TimeElapsed = 64;
771af69d88dSmrg   ctx->Const.QueryCounterBits.Timestamp = 64;
772af69d88dSmrg   ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
773af69d88dSmrg   ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
7747117f1b4Smrg}
7757117f1b4Smrg
7767117f1b4Smrg
7777117f1b4Smrg/**
7787117f1b4Smrg * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
7797117f1b4Smrg */
7807117f1b4Smrgstatic void
7817117f1b4Smrgdelete_queryobj_cb(GLuint id, void *data, void *userData)
7827117f1b4Smrg{
7837117f1b4Smrg   struct gl_query_object *q= (struct gl_query_object *) data;
7843464ebd5Sriastradh   struct gl_context *ctx = (struct gl_context *)userData;
785c1f859d4Smrg   ctx->Driver.DeleteQuery(ctx, q);
7867117f1b4Smrg}
7877117f1b4Smrg
7887117f1b4Smrg
7897117f1b4Smrg/**
7907117f1b4Smrg * Free the context state related to query objects.
7917117f1b4Smrg */
7927117f1b4Smrgvoid
7933464ebd5Sriastradh_mesa_free_queryobj_data(struct gl_context *ctx)
7947117f1b4Smrg{
795c1f859d4Smrg   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
7967117f1b4Smrg   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
7977117f1b4Smrg}
798