queryobj.c revision 848b8605
1848b8605Smrg/*
2848b8605Smrg * Mesa 3-D graphics library
3848b8605Smrg *
4848b8605Smrg * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
5848b8605Smrg *
6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7848b8605Smrg * copy of this software and associated documentation files (the "Software"),
8848b8605Smrg * to deal in the Software without restriction, including without limitation
9848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the
11848b8605Smrg * Software is furnished to do so, subject to the following conditions:
12848b8605Smrg *
13848b8605Smrg * The above copyright notice and this permission notice shall be included
14848b8605Smrg * in all copies or substantial portions of the Software.
15848b8605Smrg *
16848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20848b8605Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22848b8605Smrg * OTHER DEALINGS IN THE SOFTWARE.
23848b8605Smrg */
24848b8605Smrg
25848b8605Smrg
26848b8605Smrg#include "glheader.h"
27848b8605Smrg#include "context.h"
28848b8605Smrg#include "enums.h"
29848b8605Smrg#include "hash.h"
30848b8605Smrg#include "imports.h"
31848b8605Smrg#include "queryobj.h"
32848b8605Smrg#include "mtypes.h"
33848b8605Smrg#include "main/dispatch.h"
34848b8605Smrg
35848b8605Smrg
36848b8605Smrg/**
37848b8605Smrg * Allocate a new query object.  This is a fallback routine called via
38848b8605Smrg * ctx->Driver.NewQueryObject().
39848b8605Smrg * \param ctx - rendering context
40848b8605Smrg * \param id - the new object's ID
41848b8605Smrg * \return pointer to new query_object object or NULL if out of memory.
42848b8605Smrg */
43848b8605Smrgstatic struct gl_query_object *
44848b8605Smrg_mesa_new_query_object(struct gl_context *ctx, GLuint id)
45848b8605Smrg{
46848b8605Smrg   struct gl_query_object *q = CALLOC_STRUCT(gl_query_object);
47848b8605Smrg   (void) ctx;
48848b8605Smrg   if (q) {
49848b8605Smrg      q->Id = id;
50848b8605Smrg      q->Result = 0;
51848b8605Smrg      q->Active = GL_FALSE;
52848b8605Smrg
53848b8605Smrg      /* This is to satisfy the language of the specification: "In the initial
54848b8605Smrg       * state of a query object, the result is available" (OpenGL 3.1 §
55848b8605Smrg       * 2.13).
56848b8605Smrg       */
57848b8605Smrg      q->Ready = GL_TRUE;
58848b8605Smrg
59848b8605Smrg      /* OpenGL 3.1 § 2.13 says about GenQueries, "These names are marked as
60848b8605Smrg       * used, but no object is associated with them until the first time they
61848b8605Smrg       * are used by BeginQuery." Since our implementation actually does
62848b8605Smrg       * allocate an object at this point, use a flag to indicate that this
63848b8605Smrg       * object has not yet been bound so should not be considered a query.
64848b8605Smrg       */
65848b8605Smrg      q->EverBound = GL_FALSE;
66848b8605Smrg   }
67848b8605Smrg   return q;
68848b8605Smrg}
69848b8605Smrg
70848b8605Smrg
71848b8605Smrg/**
72848b8605Smrg * Begin a query.  Software driver fallback.
73848b8605Smrg * Called via ctx->Driver.BeginQuery().
74848b8605Smrg */
75848b8605Smrgstatic void
76848b8605Smrg_mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
77848b8605Smrg{
78848b8605Smrg   ctx->NewState |= _NEW_DEPTH; /* for swrast */
79848b8605Smrg}
80848b8605Smrg
81848b8605Smrg
82848b8605Smrg/**
83848b8605Smrg * End a query.  Software driver fallback.
84848b8605Smrg * Called via ctx->Driver.EndQuery().
85848b8605Smrg */
86848b8605Smrgstatic void
87848b8605Smrg_mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
88848b8605Smrg{
89848b8605Smrg   ctx->NewState |= _NEW_DEPTH; /* for swrast */
90848b8605Smrg   q->Ready = GL_TRUE;
91848b8605Smrg}
92848b8605Smrg
93848b8605Smrg
94848b8605Smrg/**
95848b8605Smrg * Wait for query to complete.  Software driver fallback.
96848b8605Smrg * Called via ctx->Driver.WaitQuery().
97848b8605Smrg */
98848b8605Smrgstatic void
99848b8605Smrg_mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
100848b8605Smrg{
101848b8605Smrg   /* For software drivers, _mesa_end_query() should have completed the query.
102848b8605Smrg    * For real hardware, implement a proper WaitQuery() driver function,
103848b8605Smrg    * which may require issuing a flush.
104848b8605Smrg    */
105848b8605Smrg   assert(q->Ready);
106848b8605Smrg}
107848b8605Smrg
108848b8605Smrg
109848b8605Smrg/**
110848b8605Smrg * Check if a query results are ready.  Software driver fallback.
111848b8605Smrg * Called via ctx->Driver.CheckQuery().
112848b8605Smrg */
113848b8605Smrgstatic void
114848b8605Smrg_mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
115848b8605Smrg{
116848b8605Smrg   /* No-op for sw rendering.
117848b8605Smrg    * HW drivers may need to flush at this time.
118848b8605Smrg    */
119848b8605Smrg}
120848b8605Smrg
121848b8605Smrg
122848b8605Smrg/**
123848b8605Smrg * Delete a query object.  Called via ctx->Driver.DeleteQuery().
124848b8605Smrg * Not removed from hash table here.
125848b8605Smrg */
126848b8605Smrgstatic void
127848b8605Smrg_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
128848b8605Smrg{
129848b8605Smrg   free(q->Label);
130848b8605Smrg   free(q);
131848b8605Smrg}
132848b8605Smrg
133848b8605Smrg
134848b8605Smrgvoid
135848b8605Smrg_mesa_init_query_object_functions(struct dd_function_table *driver)
136848b8605Smrg{
137848b8605Smrg   driver->NewQueryObject = _mesa_new_query_object;
138848b8605Smrg   driver->DeleteQuery = _mesa_delete_query;
139848b8605Smrg   driver->BeginQuery = _mesa_begin_query;
140848b8605Smrg   driver->EndQuery = _mesa_end_query;
141848b8605Smrg   driver->WaitQuery = _mesa_wait_query;
142848b8605Smrg   driver->CheckQuery = _mesa_check_query;
143848b8605Smrg}
144848b8605Smrg
145848b8605Smrg
146848b8605Smrg/**
147848b8605Smrg * Return pointer to the query object binding point for the given target and
148848b8605Smrg * index.
149848b8605Smrg * \return NULL if invalid target, else the address of binding point
150848b8605Smrg */
151848b8605Smrgstatic struct gl_query_object **
152848b8605Smrgget_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
153848b8605Smrg{
154848b8605Smrg   switch (target) {
155848b8605Smrg   case GL_SAMPLES_PASSED_ARB:
156848b8605Smrg      if (ctx->Extensions.ARB_occlusion_query)
157848b8605Smrg         return &ctx->Query.CurrentOcclusionObject;
158848b8605Smrg      else
159848b8605Smrg         return NULL;
160848b8605Smrg   case GL_ANY_SAMPLES_PASSED:
161848b8605Smrg      if (ctx->Extensions.ARB_occlusion_query2)
162848b8605Smrg         return &ctx->Query.CurrentOcclusionObject;
163848b8605Smrg      else
164848b8605Smrg         return NULL;
165848b8605Smrg   case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
166848b8605Smrg      if (ctx->Extensions.ARB_ES3_compatibility
167848b8605Smrg          || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
168848b8605Smrg         return &ctx->Query.CurrentOcclusionObject;
169848b8605Smrg      else
170848b8605Smrg         return NULL;
171848b8605Smrg   case GL_TIME_ELAPSED_EXT:
172848b8605Smrg      if (ctx->Extensions.EXT_timer_query)
173848b8605Smrg         return &ctx->Query.CurrentTimerObject;
174848b8605Smrg      else
175848b8605Smrg         return NULL;
176848b8605Smrg   case GL_PRIMITIVES_GENERATED:
177848b8605Smrg      if (ctx->Extensions.EXT_transform_feedback)
178848b8605Smrg         return &ctx->Query.PrimitivesGenerated[index];
179848b8605Smrg      else
180848b8605Smrg         return NULL;
181848b8605Smrg   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
182848b8605Smrg      if (ctx->Extensions.EXT_transform_feedback)
183848b8605Smrg         return &ctx->Query.PrimitivesWritten[index];
184848b8605Smrg      else
185848b8605Smrg         return NULL;
186848b8605Smrg   default:
187848b8605Smrg      return NULL;
188848b8605Smrg   }
189848b8605Smrg}
190848b8605Smrg
191848b8605Smrg
192848b8605Smrgvoid GLAPIENTRY
193848b8605Smrg_mesa_GenQueries(GLsizei n, GLuint *ids)
194848b8605Smrg{
195848b8605Smrg   GLuint first;
196848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
197848b8605Smrg
198848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
199848b8605Smrg      _mesa_debug(ctx, "glGenQueries(%d)\n", n);
200848b8605Smrg
201848b8605Smrg   if (n < 0) {
202848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
203848b8605Smrg      return;
204848b8605Smrg   }
205848b8605Smrg
206848b8605Smrg   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
207848b8605Smrg   if (first) {
208848b8605Smrg      GLsizei i;
209848b8605Smrg      for (i = 0; i < n; i++) {
210848b8605Smrg         struct gl_query_object *q
211848b8605Smrg            = ctx->Driver.NewQueryObject(ctx, first + i);
212848b8605Smrg         if (!q) {
213848b8605Smrg            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
214848b8605Smrg            return;
215848b8605Smrg         }
216848b8605Smrg         ids[i] = first + i;
217848b8605Smrg         _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
218848b8605Smrg      }
219848b8605Smrg   }
220848b8605Smrg}
221848b8605Smrg
222848b8605Smrg
223848b8605Smrgvoid GLAPIENTRY
224848b8605Smrg_mesa_DeleteQueries(GLsizei n, const GLuint *ids)
225848b8605Smrg{
226848b8605Smrg   GLint i;
227848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
228848b8605Smrg   FLUSH_VERTICES(ctx, 0);
229848b8605Smrg
230848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
231848b8605Smrg      _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
232848b8605Smrg
233848b8605Smrg   if (n < 0) {
234848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
235848b8605Smrg      return;
236848b8605Smrg   }
237848b8605Smrg
238848b8605Smrg   for (i = 0; i < n; i++) {
239848b8605Smrg      if (ids[i] > 0) {
240848b8605Smrg         struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
241848b8605Smrg         if (q) {
242848b8605Smrg            if (q->Active) {
243848b8605Smrg               struct gl_query_object **bindpt;
244848b8605Smrg               bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
245848b8605Smrg               assert(bindpt); /* Should be non-null for active q. */
246848b8605Smrg               if (bindpt) {
247848b8605Smrg                  *bindpt = NULL;
248848b8605Smrg               }
249848b8605Smrg               q->Active = GL_FALSE;
250848b8605Smrg               ctx->Driver.EndQuery(ctx, q);
251848b8605Smrg            }
252848b8605Smrg            _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
253848b8605Smrg            ctx->Driver.DeleteQuery(ctx, q);
254848b8605Smrg         }
255848b8605Smrg      }
256848b8605Smrg   }
257848b8605Smrg}
258848b8605Smrg
259848b8605Smrg
260848b8605SmrgGLboolean GLAPIENTRY
261848b8605Smrg_mesa_IsQuery(GLuint id)
262848b8605Smrg{
263848b8605Smrg   struct gl_query_object *q;
264848b8605Smrg
265848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
266848b8605Smrg   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
267848b8605Smrg
268848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
269848b8605Smrg      _mesa_debug(ctx, "glIsQuery(%u)\n", id);
270848b8605Smrg
271848b8605Smrg   if (id == 0)
272848b8605Smrg      return GL_FALSE;
273848b8605Smrg
274848b8605Smrg   q = _mesa_lookup_query_object(ctx, id);
275848b8605Smrg   if (q == NULL)
276848b8605Smrg      return GL_FALSE;
277848b8605Smrg
278848b8605Smrg   return q->EverBound;
279848b8605Smrg}
280848b8605Smrg
281848b8605Smrgstatic GLboolean
282848b8605Smrgquery_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
283848b8605Smrg{
284848b8605Smrg   switch (target) {
285848b8605Smrg   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
286848b8605Smrg   case GL_PRIMITIVES_GENERATED:
287848b8605Smrg      if (index >= ctx->Const.MaxVertexStreams) {
288848b8605Smrg         _mesa_error(ctx, GL_INVALID_VALUE,
289848b8605Smrg                     "glBeginQueryIndexed(index>=MaxVertexStreams)");
290848b8605Smrg         return GL_FALSE;
291848b8605Smrg      }
292848b8605Smrg      break;
293848b8605Smrg   default:
294848b8605Smrg      if (index > 0) {
295848b8605Smrg         _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
296848b8605Smrg         return GL_FALSE;
297848b8605Smrg      }
298848b8605Smrg   }
299848b8605Smrg   return GL_TRUE;
300848b8605Smrg}
301848b8605Smrg
302848b8605Smrgvoid GLAPIENTRY
303848b8605Smrg_mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
304848b8605Smrg{
305848b8605Smrg   struct gl_query_object *q, **bindpt;
306848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
307848b8605Smrg
308848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
309848b8605Smrg      _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
310848b8605Smrg                  _mesa_lookup_enum_by_nr(target), index, id);
311848b8605Smrg
312848b8605Smrg   if (!query_error_check_index(ctx, target, index))
313848b8605Smrg      return;
314848b8605Smrg
315848b8605Smrg   FLUSH_VERTICES(ctx, 0);
316848b8605Smrg
317848b8605Smrg   bindpt = get_query_binding_point(ctx, target, index);
318848b8605Smrg   if (!bindpt) {
319848b8605Smrg      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
320848b8605Smrg      return;
321848b8605Smrg   }
322848b8605Smrg
323848b8605Smrg   /* From the GL_ARB_occlusion_query spec:
324848b8605Smrg    *
325848b8605Smrg    *     "If BeginQueryARB is called while another query is already in
326848b8605Smrg    *      progress with the same target, an INVALID_OPERATION error is
327848b8605Smrg    *      generated."
328848b8605Smrg    */
329848b8605Smrg   if (*bindpt) {
330848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
331848b8605Smrg                  "glBeginQuery{Indexed}(target=%s is active)",
332848b8605Smrg                  _mesa_lookup_enum_by_nr(target));
333848b8605Smrg      return;
334848b8605Smrg   }
335848b8605Smrg
336848b8605Smrg   if (id == 0) {
337848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
338848b8605Smrg      return;
339848b8605Smrg   }
340848b8605Smrg
341848b8605Smrg   q = _mesa_lookup_query_object(ctx, id);
342848b8605Smrg   if (!q) {
343848b8605Smrg      if (ctx->API != API_OPENGL_COMPAT) {
344848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
345848b8605Smrg                     "glBeginQuery{Indexed}(non-gen name)");
346848b8605Smrg         return;
347848b8605Smrg      } else {
348848b8605Smrg         /* create new object */
349848b8605Smrg         q = ctx->Driver.NewQueryObject(ctx, id);
350848b8605Smrg         if (!q) {
351848b8605Smrg            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
352848b8605Smrg            return;
353848b8605Smrg         }
354848b8605Smrg         _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
355848b8605Smrg      }
356848b8605Smrg   }
357848b8605Smrg   else {
358848b8605Smrg      /* pre-existing object */
359848b8605Smrg      if (q->Active) {
360848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
361848b8605Smrg                     "glBeginQuery{Indexed}(query already active)");
362848b8605Smrg         return;
363848b8605Smrg      }
364848b8605Smrg   }
365848b8605Smrg
366848b8605Smrg   q->Target = target;
367848b8605Smrg   q->Active = GL_TRUE;
368848b8605Smrg   q->Result = 0;
369848b8605Smrg   q->Ready = GL_FALSE;
370848b8605Smrg   q->EverBound = GL_TRUE;
371848b8605Smrg   q->Stream = index;
372848b8605Smrg
373848b8605Smrg   /* XXX should probably refcount query objects */
374848b8605Smrg   *bindpt = q;
375848b8605Smrg
376848b8605Smrg   ctx->Driver.BeginQuery(ctx, q);
377848b8605Smrg}
378848b8605Smrg
379848b8605Smrg
380848b8605Smrgvoid GLAPIENTRY
381848b8605Smrg_mesa_EndQueryIndexed(GLenum target, GLuint index)
382848b8605Smrg{
383848b8605Smrg   struct gl_query_object *q, **bindpt;
384848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
385848b8605Smrg
386848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
387848b8605Smrg      _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
388848b8605Smrg                  _mesa_lookup_enum_by_nr(target), index);
389848b8605Smrg
390848b8605Smrg   if (!query_error_check_index(ctx, target, index))
391848b8605Smrg      return;
392848b8605Smrg
393848b8605Smrg   FLUSH_VERTICES(ctx, 0);
394848b8605Smrg
395848b8605Smrg   bindpt = get_query_binding_point(ctx, target, index);
396848b8605Smrg   if (!bindpt) {
397848b8605Smrg      _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
398848b8605Smrg      return;
399848b8605Smrg   }
400848b8605Smrg
401848b8605Smrg   /* XXX should probably refcount query objects */
402848b8605Smrg   q = *bindpt;
403848b8605Smrg
404848b8605Smrg   /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
405848b8605Smrg   if (q && q->Target != target) {
406848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
407848b8605Smrg                  "glEndQuery(target=%s with active query of target %s)",
408848b8605Smrg                  _mesa_lookup_enum_by_nr(target),
409848b8605Smrg                  _mesa_lookup_enum_by_nr(q->Target));
410848b8605Smrg      return;
411848b8605Smrg   }
412848b8605Smrg
413848b8605Smrg   *bindpt = NULL;
414848b8605Smrg
415848b8605Smrg   if (!q || !q->Active) {
416848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
417848b8605Smrg                  "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
418848b8605Smrg      return;
419848b8605Smrg   }
420848b8605Smrg
421848b8605Smrg   q->Active = GL_FALSE;
422848b8605Smrg   ctx->Driver.EndQuery(ctx, q);
423848b8605Smrg}
424848b8605Smrg
425848b8605Smrgvoid GLAPIENTRY
426848b8605Smrg_mesa_BeginQuery(GLenum target, GLuint id)
427848b8605Smrg{
428848b8605Smrg   _mesa_BeginQueryIndexed(target, 0, id);
429848b8605Smrg}
430848b8605Smrg
431848b8605Smrgvoid GLAPIENTRY
432848b8605Smrg_mesa_EndQuery(GLenum target)
433848b8605Smrg{
434848b8605Smrg   _mesa_EndQueryIndexed(target, 0);
435848b8605Smrg}
436848b8605Smrg
437848b8605Smrgvoid GLAPIENTRY
438848b8605Smrg_mesa_QueryCounter(GLuint id, GLenum target)
439848b8605Smrg{
440848b8605Smrg   struct gl_query_object *q;
441848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
442848b8605Smrg
443848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
444848b8605Smrg      _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
445848b8605Smrg                  _mesa_lookup_enum_by_nr(target));
446848b8605Smrg
447848b8605Smrg   /* error checking */
448848b8605Smrg   if (target != GL_TIMESTAMP) {
449848b8605Smrg      _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
450848b8605Smrg      return;
451848b8605Smrg   }
452848b8605Smrg
453848b8605Smrg   if (id == 0) {
454848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
455848b8605Smrg      return;
456848b8605Smrg   }
457848b8605Smrg
458848b8605Smrg   q = _mesa_lookup_query_object(ctx, id);
459848b8605Smrg   if (!q) {
460848b8605Smrg      /* XXX the Core profile should throw INVALID_OPERATION here */
461848b8605Smrg
462848b8605Smrg      /* create new object */
463848b8605Smrg      q = ctx->Driver.NewQueryObject(ctx, id);
464848b8605Smrg      if (!q) {
465848b8605Smrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
466848b8605Smrg         return;
467848b8605Smrg      }
468848b8605Smrg      _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
469848b8605Smrg   }
470848b8605Smrg   else {
471848b8605Smrg      if (q->Target && q->Target != GL_TIMESTAMP) {
472848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
473848b8605Smrg                     "glQueryCounter(id has an invalid target)");
474848b8605Smrg         return;
475848b8605Smrg      }
476848b8605Smrg   }
477848b8605Smrg
478848b8605Smrg   if (q->Active) {
479848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
480848b8605Smrg      return;
481848b8605Smrg   }
482848b8605Smrg
483848b8605Smrg   q->Target = target;
484848b8605Smrg   q->Result = 0;
485848b8605Smrg   q->Ready = GL_FALSE;
486848b8605Smrg   q->EverBound = GL_TRUE;
487848b8605Smrg
488848b8605Smrg   if (ctx->Driver.QueryCounter) {
489848b8605Smrg      ctx->Driver.QueryCounter(ctx, q);
490848b8605Smrg   } else {
491848b8605Smrg      /* QueryCounter is implemented using EndQuery without BeginQuery
492848b8605Smrg       * in drivers. This is actually Direct3D and Gallium convention.
493848b8605Smrg       */
494848b8605Smrg      ctx->Driver.EndQuery(ctx, q);
495848b8605Smrg   }
496848b8605Smrg}
497848b8605Smrg
498848b8605Smrg
499848b8605Smrgvoid GLAPIENTRY
500848b8605Smrg_mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
501848b8605Smrg                        GLint *params)
502848b8605Smrg{
503848b8605Smrg   struct gl_query_object *q = NULL, **bindpt = NULL;
504848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
505848b8605Smrg
506848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
507848b8605Smrg      _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
508848b8605Smrg                  _mesa_lookup_enum_by_nr(target),
509848b8605Smrg                  index,
510848b8605Smrg                  _mesa_lookup_enum_by_nr(pname));
511848b8605Smrg
512848b8605Smrg   if (!query_error_check_index(ctx, target, index))
513848b8605Smrg      return;
514848b8605Smrg
515848b8605Smrg   if (target == GL_TIMESTAMP) {
516848b8605Smrg      if (!ctx->Extensions.ARB_timer_query) {
517848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
518848b8605Smrg         return;
519848b8605Smrg      }
520848b8605Smrg   }
521848b8605Smrg   else {
522848b8605Smrg      bindpt = get_query_binding_point(ctx, target, index);
523848b8605Smrg      if (!bindpt) {
524848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
525848b8605Smrg         return;
526848b8605Smrg      }
527848b8605Smrg
528848b8605Smrg      q = *bindpt;
529848b8605Smrg   }
530848b8605Smrg
531848b8605Smrg   switch (pname) {
532848b8605Smrg      case GL_QUERY_COUNTER_BITS_ARB:
533848b8605Smrg         switch (target) {
534848b8605Smrg         case GL_SAMPLES_PASSED:
535848b8605Smrg            *params = ctx->Const.QueryCounterBits.SamplesPassed;
536848b8605Smrg            break;
537848b8605Smrg         case GL_ANY_SAMPLES_PASSED:
538848b8605Smrg            /* The minimum value of this is 1 if it's nonzero, and the value
539848b8605Smrg             * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
540848b8605Smrg             * bits.
541848b8605Smrg             */
542848b8605Smrg            *params = 1;
543848b8605Smrg            break;
544848b8605Smrg         case GL_TIME_ELAPSED:
545848b8605Smrg            *params = ctx->Const.QueryCounterBits.TimeElapsed;
546848b8605Smrg            break;
547848b8605Smrg         case GL_TIMESTAMP:
548848b8605Smrg            *params = ctx->Const.QueryCounterBits.Timestamp;
549848b8605Smrg            break;
550848b8605Smrg         case GL_PRIMITIVES_GENERATED:
551848b8605Smrg            *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
552848b8605Smrg            break;
553848b8605Smrg         case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
554848b8605Smrg            *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
555848b8605Smrg            break;
556848b8605Smrg         default:
557848b8605Smrg            _mesa_problem(ctx,
558848b8605Smrg                          "Unknown target in glGetQueryIndexediv(target = %s)",
559848b8605Smrg                          _mesa_lookup_enum_by_nr(target));
560848b8605Smrg            *params = 0;
561848b8605Smrg            break;
562848b8605Smrg         }
563848b8605Smrg         break;
564848b8605Smrg      case GL_CURRENT_QUERY_ARB:
565848b8605Smrg         *params = (q && q->Target == target) ? q->Id : 0;
566848b8605Smrg         break;
567848b8605Smrg      default:
568848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
569848b8605Smrg         return;
570848b8605Smrg   }
571848b8605Smrg}
572848b8605Smrg
573848b8605Smrgvoid GLAPIENTRY
574848b8605Smrg_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
575848b8605Smrg{
576848b8605Smrg   _mesa_GetQueryIndexediv(target, 0, pname, params);
577848b8605Smrg}
578848b8605Smrg
579848b8605Smrgvoid GLAPIENTRY
580848b8605Smrg_mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
581848b8605Smrg{
582848b8605Smrg   struct gl_query_object *q = NULL;
583848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
584848b8605Smrg
585848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
586848b8605Smrg      _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id,
587848b8605Smrg                  _mesa_lookup_enum_by_nr(pname));
588848b8605Smrg
589848b8605Smrg   if (id)
590848b8605Smrg      q = _mesa_lookup_query_object(ctx, id);
591848b8605Smrg
592848b8605Smrg   if (!q || q->Active) {
593848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
594848b8605Smrg                  "glGetQueryObjectivARB(id=%d is invalid or active)", id);
595848b8605Smrg      return;
596848b8605Smrg   }
597848b8605Smrg
598848b8605Smrg   switch (pname) {
599848b8605Smrg      case GL_QUERY_RESULT_ARB:
600848b8605Smrg         if (!q->Ready)
601848b8605Smrg            ctx->Driver.WaitQuery(ctx, q);
602848b8605Smrg         /* if result is too large for returned type, clamp to max value */
603848b8605Smrg         if (q->Target == GL_ANY_SAMPLES_PASSED
604848b8605Smrg             || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
605848b8605Smrg            if (q->Result)
606848b8605Smrg               *params = GL_TRUE;
607848b8605Smrg            else
608848b8605Smrg               *params = GL_FALSE;
609848b8605Smrg         } else {
610848b8605Smrg            if (q->Result > 0x7fffffff) {
611848b8605Smrg               *params = 0x7fffffff;
612848b8605Smrg            }
613848b8605Smrg            else {
614848b8605Smrg               *params = (GLint)q->Result;
615848b8605Smrg            }
616848b8605Smrg         }
617848b8605Smrg         break;
618848b8605Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
619848b8605Smrg	 if (!q->Ready)
620848b8605Smrg	    ctx->Driver.CheckQuery( ctx, q );
621848b8605Smrg         *params = q->Ready;
622848b8605Smrg         break;
623848b8605Smrg      default:
624848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
625848b8605Smrg         return;
626848b8605Smrg   }
627848b8605Smrg}
628848b8605Smrg
629848b8605Smrg
630848b8605Smrgvoid GLAPIENTRY
631848b8605Smrg_mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
632848b8605Smrg{
633848b8605Smrg   struct gl_query_object *q = NULL;
634848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
635848b8605Smrg
636848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
637848b8605Smrg      _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id,
638848b8605Smrg                  _mesa_lookup_enum_by_nr(pname));
639848b8605Smrg
640848b8605Smrg   if (id)
641848b8605Smrg      q = _mesa_lookup_query_object(ctx, id);
642848b8605Smrg
643848b8605Smrg   if (!q || q->Active) {
644848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
645848b8605Smrg                  "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
646848b8605Smrg      return;
647848b8605Smrg   }
648848b8605Smrg
649848b8605Smrg   switch (pname) {
650848b8605Smrg      case GL_QUERY_RESULT_ARB:
651848b8605Smrg         if (!q->Ready)
652848b8605Smrg            ctx->Driver.WaitQuery(ctx, q);
653848b8605Smrg         /* if result is too large for returned type, clamp to max value */
654848b8605Smrg         if (q->Target == GL_ANY_SAMPLES_PASSED
655848b8605Smrg             || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
656848b8605Smrg            if (q->Result)
657848b8605Smrg               *params = GL_TRUE;
658848b8605Smrg            else
659848b8605Smrg               *params = GL_FALSE;
660848b8605Smrg         } else {
661848b8605Smrg            if (q->Result > 0xffffffff) {
662848b8605Smrg               *params = 0xffffffff;
663848b8605Smrg            }
664848b8605Smrg            else {
665848b8605Smrg               *params = (GLuint)q->Result;
666848b8605Smrg            }
667848b8605Smrg         }
668848b8605Smrg         break;
669848b8605Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
670848b8605Smrg	 if (!q->Ready)
671848b8605Smrg	    ctx->Driver.CheckQuery( ctx, q );
672848b8605Smrg         *params = q->Ready;
673848b8605Smrg         break;
674848b8605Smrg      default:
675848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
676848b8605Smrg         return;
677848b8605Smrg   }
678848b8605Smrg}
679848b8605Smrg
680848b8605Smrg
681848b8605Smrg/**
682848b8605Smrg * New with GL_EXT_timer_query
683848b8605Smrg */
684848b8605Smrgvoid GLAPIENTRY
685848b8605Smrg_mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
686848b8605Smrg{
687848b8605Smrg   struct gl_query_object *q = NULL;
688848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
689848b8605Smrg
690848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
691848b8605Smrg      _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id,
692848b8605Smrg                  _mesa_lookup_enum_by_nr(pname));
693848b8605Smrg
694848b8605Smrg   if (id)
695848b8605Smrg      q = _mesa_lookup_query_object(ctx, id);
696848b8605Smrg
697848b8605Smrg   if (!q || q->Active) {
698848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
699848b8605Smrg                  "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
700848b8605Smrg      return;
701848b8605Smrg   }
702848b8605Smrg
703848b8605Smrg   switch (pname) {
704848b8605Smrg      case GL_QUERY_RESULT_ARB:
705848b8605Smrg         if (!q->Ready)
706848b8605Smrg            ctx->Driver.WaitQuery(ctx, q);
707848b8605Smrg         *params = q->Result;
708848b8605Smrg         break;
709848b8605Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
710848b8605Smrg	 if (!q->Ready)
711848b8605Smrg	    ctx->Driver.CheckQuery( ctx, q );
712848b8605Smrg         *params = q->Ready;
713848b8605Smrg         break;
714848b8605Smrg      default:
715848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
716848b8605Smrg         return;
717848b8605Smrg   }
718848b8605Smrg}
719848b8605Smrg
720848b8605Smrg
721848b8605Smrg/**
722848b8605Smrg * New with GL_EXT_timer_query
723848b8605Smrg */
724848b8605Smrgvoid GLAPIENTRY
725848b8605Smrg_mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
726848b8605Smrg{
727848b8605Smrg   struct gl_query_object *q = NULL;
728848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
729848b8605Smrg
730848b8605Smrg   if (MESA_VERBOSE & VERBOSE_API)
731848b8605Smrg      _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id,
732848b8605Smrg                  _mesa_lookup_enum_by_nr(pname));
733848b8605Smrg
734848b8605Smrg   if (id)
735848b8605Smrg      q = _mesa_lookup_query_object(ctx, id);
736848b8605Smrg
737848b8605Smrg   if (!q || q->Active) {
738848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
739848b8605Smrg                  "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
740848b8605Smrg      return;
741848b8605Smrg   }
742848b8605Smrg
743848b8605Smrg   switch (pname) {
744848b8605Smrg      case GL_QUERY_RESULT_ARB:
745848b8605Smrg         if (!q->Ready)
746848b8605Smrg            ctx->Driver.WaitQuery(ctx, q);
747848b8605Smrg         *params = q->Result;
748848b8605Smrg         break;
749848b8605Smrg      case GL_QUERY_RESULT_AVAILABLE_ARB:
750848b8605Smrg	 if (!q->Ready)
751848b8605Smrg	    ctx->Driver.CheckQuery( ctx, q );
752848b8605Smrg         *params = q->Ready;
753848b8605Smrg         break;
754848b8605Smrg      default:
755848b8605Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
756848b8605Smrg         return;
757848b8605Smrg   }
758848b8605Smrg}
759848b8605Smrg
760848b8605Smrg/**
761848b8605Smrg * Allocate/init the context state related to query objects.
762848b8605Smrg */
763848b8605Smrgvoid
764848b8605Smrg_mesa_init_queryobj(struct gl_context *ctx)
765848b8605Smrg{
766848b8605Smrg   ctx->Query.QueryObjects = _mesa_NewHashTable();
767848b8605Smrg   ctx->Query.CurrentOcclusionObject = NULL;
768848b8605Smrg
769848b8605Smrg   ctx->Const.QueryCounterBits.SamplesPassed = 64;
770848b8605Smrg   ctx->Const.QueryCounterBits.TimeElapsed = 64;
771848b8605Smrg   ctx->Const.QueryCounterBits.Timestamp = 64;
772848b8605Smrg   ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
773848b8605Smrg   ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
774848b8605Smrg}
775848b8605Smrg
776848b8605Smrg
777848b8605Smrg/**
778848b8605Smrg * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
779848b8605Smrg */
780848b8605Smrgstatic void
781848b8605Smrgdelete_queryobj_cb(GLuint id, void *data, void *userData)
782848b8605Smrg{
783848b8605Smrg   struct gl_query_object *q= (struct gl_query_object *) data;
784848b8605Smrg   struct gl_context *ctx = (struct gl_context *)userData;
785848b8605Smrg   ctx->Driver.DeleteQuery(ctx, q);
786848b8605Smrg}
787848b8605Smrg
788848b8605Smrg
789848b8605Smrg/**
790848b8605Smrg * Free the context state related to query objects.
791848b8605Smrg */
792848b8605Smrgvoid
793848b8605Smrg_mesa_free_queryobj_data(struct gl_context *ctx)
794848b8605Smrg{
795848b8605Smrg   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
796848b8605Smrg   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
797848b8605Smrg}
798