queryobj.c revision 4a49301e
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.1
4 *
5 * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26#include "glheader.h"
27#include "context.h"
28#include "hash.h"
29#include "imports.h"
30#include "queryobj.h"
31#include "mtypes.h"
32#include "glapi/dispatch.h"
33
34
35#if FEATURE_queryobj
36
37
38/**
39 * Allocate a new query object.  This is a fallback routine called via
40 * ctx->Driver.NewQueryObject().
41 * \param ctx - rendering context
42 * \param id - the new object's ID
43 * \return pointer to new query_object object or NULL if out of memory.
44 */
45static struct gl_query_object *
46_mesa_new_query_object(GLcontext *ctx, GLuint id)
47{
48   struct gl_query_object *q = MALLOC_STRUCT(gl_query_object);
49   (void) ctx;
50   if (q) {
51      q->Id = id;
52      q->Result = 0;
53      q->Active = GL_FALSE;
54      q->Ready = GL_TRUE;   /* correct, see spec */
55   }
56   return q;
57}
58
59
60/**
61 * Begin a query.  Software driver fallback.
62 * Called via ctx->Driver.BeginQuery().
63 */
64static void
65_mesa_begin_query(GLcontext *ctx, struct gl_query_object *q)
66{
67   /* no-op */
68}
69
70
71/**
72 * End a query.  Software driver fallback.
73 * Called via ctx->Driver.EndQuery().
74 */
75static void
76_mesa_end_query(GLcontext *ctx, struct gl_query_object *q)
77{
78   q->Ready = GL_TRUE;
79}
80
81
82/**
83 * Wait for query to complete.  Software driver fallback.
84 * Called via ctx->Driver.WaitQuery().
85 */
86static void
87_mesa_wait_query(GLcontext *ctx, struct gl_query_object *q)
88{
89   /* For software drivers, _mesa_end_query() should have completed the query.
90    * For real hardware, implement a proper WaitQuery() driver function,
91    * which may require issuing a flush.
92    */
93   assert(q->Ready);
94}
95
96
97/**
98 * Check if a query results are ready.  Software driver fallback.
99 * Called via ctx->Driver.CheckQuery().
100 */
101static void
102_mesa_check_query(GLcontext *ctx, struct gl_query_object *q)
103{
104   /* No-op for sw rendering.
105    * HW drivers may need to flush at this time.
106    */
107}
108
109
110/**
111 * Delete a query object.  Called via ctx->Driver.DeleteQuery().
112 * Not removed from hash table here.
113 */
114static void
115_mesa_delete_query(GLcontext *ctx, struct gl_query_object *q)
116{
117   _mesa_free(q);
118}
119
120
121static struct gl_query_object *
122lookup_query_object(GLcontext *ctx, GLuint id)
123{
124   return (struct gl_query_object *)
125      _mesa_HashLookup(ctx->Query.QueryObjects, id);
126}
127
128
129
130void
131_mesa_init_query_object_functions(struct dd_function_table *driver)
132{
133   driver->NewQueryObject = _mesa_new_query_object;
134   driver->DeleteQuery = _mesa_delete_query;
135   driver->BeginQuery = _mesa_begin_query;
136   driver->EndQuery = _mesa_end_query;
137   driver->WaitQuery = _mesa_wait_query;
138   driver->CheckQuery = _mesa_check_query;
139}
140
141
142void GLAPIENTRY
143_mesa_GenQueriesARB(GLsizei n, GLuint *ids)
144{
145   GLuint first;
146   GET_CURRENT_CONTEXT(ctx);
147   ASSERT_OUTSIDE_BEGIN_END(ctx);
148
149   if (n < 0) {
150      _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
151      return;
152   }
153
154   /* No query objects can be active at this time! */
155   if (ctx->Query.CurrentOcclusionObject ||
156       ctx->Query.CurrentTimerObject) {
157      _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
158      return;
159   }
160
161   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
162   if (first) {
163      GLsizei i;
164      for (i = 0; i < n; i++) {
165         struct gl_query_object *q
166            = ctx->Driver.NewQueryObject(ctx, first + i);
167         if (!q) {
168            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
169            return;
170         }
171         ids[i] = first + i;
172         _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
173      }
174   }
175}
176
177
178void GLAPIENTRY
179_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
180{
181   GLint i;
182   GET_CURRENT_CONTEXT(ctx);
183   ASSERT_OUTSIDE_BEGIN_END(ctx);
184
185   if (n < 0) {
186      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
187      return;
188   }
189
190   /* No query objects can be active at this time! */
191   if (ctx->Query.CurrentOcclusionObject ||
192       ctx->Query.CurrentTimerObject) {
193      _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
194      return;
195   }
196
197   for (i = 0; i < n; i++) {
198      if (ids[i] > 0) {
199         struct gl_query_object *q = lookup_query_object(ctx, ids[i]);
200         if (q) {
201            ASSERT(!q->Active); /* should be caught earlier */
202            _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
203            ctx->Driver.DeleteQuery(ctx, q);
204         }
205      }
206   }
207}
208
209
210GLboolean GLAPIENTRY
211_mesa_IsQueryARB(GLuint id)
212{
213   GET_CURRENT_CONTEXT(ctx);
214   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
215
216   if (id && lookup_query_object(ctx, id))
217      return GL_TRUE;
218   else
219      return GL_FALSE;
220}
221
222
223static void GLAPIENTRY
224_mesa_BeginQueryARB(GLenum target, GLuint id)
225{
226   struct gl_query_object *q;
227   GET_CURRENT_CONTEXT(ctx);
228   ASSERT_OUTSIDE_BEGIN_END(ctx);
229
230   FLUSH_VERTICES(ctx, _NEW_DEPTH);
231
232   switch (target) {
233      case GL_SAMPLES_PASSED_ARB:
234         if (!ctx->Extensions.ARB_occlusion_query) {
235            _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
236            return;
237         }
238         if (ctx->Query.CurrentOcclusionObject) {
239            _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
240            return;
241         }
242         break;
243      case GL_TIME_ELAPSED_EXT:
244         if (!ctx->Extensions.EXT_timer_query) {
245            _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
246            return;
247         }
248         if (ctx->Query.CurrentTimerObject) {
249            _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
250            return;
251         }
252         break;
253      default:
254         _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
255         return;
256   }
257
258   if (id == 0) {
259      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
260      return;
261   }
262
263   q = lookup_query_object(ctx, id);
264   if (!q) {
265      /* create new object */
266      q = ctx->Driver.NewQueryObject(ctx, id);
267      if (!q) {
268         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
269         return;
270      }
271      _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
272   }
273   else {
274      /* pre-existing object */
275      if (q->Active) {
276         _mesa_error(ctx, GL_INVALID_OPERATION,
277                     "glBeginQueryARB(query already active)");
278         return;
279      }
280   }
281
282   q->Target = target;
283   q->Active = GL_TRUE;
284   q->Result = 0;
285   q->Ready = GL_FALSE;
286
287   if (target == GL_SAMPLES_PASSED_ARB) {
288      ctx->Query.CurrentOcclusionObject = q;
289   }
290   else if (target == GL_TIME_ELAPSED_EXT) {
291      ctx->Query.CurrentTimerObject = q;
292   }
293
294   ctx->Driver.BeginQuery(ctx, q);
295}
296
297
298static void GLAPIENTRY
299_mesa_EndQueryARB(GLenum target)
300{
301   struct gl_query_object *q;
302   GET_CURRENT_CONTEXT(ctx);
303   ASSERT_OUTSIDE_BEGIN_END(ctx);
304
305   FLUSH_VERTICES(ctx, _NEW_DEPTH);
306
307   switch (target) {
308      case GL_SAMPLES_PASSED_ARB:
309         if (!ctx->Extensions.ARB_occlusion_query) {
310            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
311            return;
312         }
313         q = ctx->Query.CurrentOcclusionObject;
314         ctx->Query.CurrentOcclusionObject = NULL;
315         break;
316      case GL_TIME_ELAPSED_EXT:
317         if (!ctx->Extensions.EXT_timer_query) {
318            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
319            return;
320         }
321         q = ctx->Query.CurrentTimerObject;
322         ctx->Query.CurrentTimerObject = NULL;
323         break;
324      default:
325         _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
326         return;
327   }
328
329   if (!q || !q->Active) {
330      _mesa_error(ctx, GL_INVALID_OPERATION,
331                  "glEndQueryARB(no matching glBeginQueryARB)");
332      return;
333   }
334
335   q->Active = GL_FALSE;
336   ctx->Driver.EndQuery(ctx, q);
337}
338
339
340void GLAPIENTRY
341_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
342{
343   struct gl_query_object *q;
344   GET_CURRENT_CONTEXT(ctx);
345   ASSERT_OUTSIDE_BEGIN_END(ctx);
346
347   switch (target) {
348      case GL_SAMPLES_PASSED_ARB:
349         if (!ctx->Extensions.ARB_occlusion_query) {
350            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
351            return;
352         }
353         q = ctx->Query.CurrentOcclusionObject;
354         break;
355      case GL_TIME_ELAPSED_EXT:
356         if (!ctx->Extensions.EXT_timer_query) {
357            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
358            return;
359         }
360         q = ctx->Query.CurrentTimerObject;
361         break;
362      default:
363         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)");
364         return;
365   }
366
367   switch (pname) {
368      case GL_QUERY_COUNTER_BITS_ARB:
369         *params = 8 * sizeof(q->Result);
370         break;
371      case GL_CURRENT_QUERY_ARB:
372         *params = q ? q->Id : 0;
373         break;
374      default:
375         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
376         return;
377   }
378}
379
380
381void GLAPIENTRY
382_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
383{
384   struct gl_query_object *q = NULL;
385   GET_CURRENT_CONTEXT(ctx);
386   ASSERT_OUTSIDE_BEGIN_END(ctx);
387
388   if (id)
389      q = lookup_query_object(ctx, id);
390
391   if (!q || q->Active) {
392      _mesa_error(ctx, GL_INVALID_OPERATION,
393                  "glGetQueryObjectivARB(id=%d is invalid or active)", id);
394      return;
395   }
396
397   switch (pname) {
398      case GL_QUERY_RESULT_ARB:
399         if (!q->Ready)
400            ctx->Driver.WaitQuery(ctx, q);
401         /* if result is too large for returned type, clamp to max value */
402         if (q->Result > 0x7fffffff) {
403            *params = 0x7fffffff;
404         }
405         else {
406            *params = (GLint)q->Result;
407         }
408         break;
409      case GL_QUERY_RESULT_AVAILABLE_ARB:
410	 if (!q->Ready)
411	    ctx->Driver.CheckQuery( ctx, q );
412         *params = q->Ready;
413         break;
414      default:
415         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
416         return;
417   }
418}
419
420
421void GLAPIENTRY
422_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
423{
424   struct gl_query_object *q = NULL;
425   GET_CURRENT_CONTEXT(ctx);
426   ASSERT_OUTSIDE_BEGIN_END(ctx);
427
428   if (id)
429      q = lookup_query_object(ctx, id);
430
431   if (!q || q->Active) {
432      _mesa_error(ctx, GL_INVALID_OPERATION,
433                  "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
434      return;
435   }
436
437   switch (pname) {
438      case GL_QUERY_RESULT_ARB:
439         if (!q->Ready)
440            ctx->Driver.WaitQuery(ctx, q);
441         /* if result is too large for returned type, clamp to max value */
442         if (q->Result > 0xffffffff) {
443            *params = 0xffffffff;
444         }
445         else {
446            *params = (GLuint)q->Result;
447         }
448         break;
449      case GL_QUERY_RESULT_AVAILABLE_ARB:
450	 if (!q->Ready)
451	    ctx->Driver.CheckQuery( ctx, q );
452         *params = q->Ready;
453         break;
454      default:
455         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
456         return;
457   }
458}
459
460
461/**
462 * New with GL_EXT_timer_query
463 */
464static void GLAPIENTRY
465_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
466{
467   struct gl_query_object *q = NULL;
468   GET_CURRENT_CONTEXT(ctx);
469   ASSERT_OUTSIDE_BEGIN_END(ctx);
470
471   if (id)
472      q = lookup_query_object(ctx, id);
473
474   if (!q || q->Active) {
475      _mesa_error(ctx, GL_INVALID_OPERATION,
476                  "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
477      return;
478   }
479
480   switch (pname) {
481      case GL_QUERY_RESULT_ARB:
482         if (!q->Ready)
483            ctx->Driver.WaitQuery(ctx, q);
484         *params = q->Result;
485         break;
486      case GL_QUERY_RESULT_AVAILABLE_ARB:
487	 if (!q->Ready)
488	    ctx->Driver.CheckQuery( ctx, q );
489         *params = q->Ready;
490         break;
491      default:
492         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
493         return;
494   }
495}
496
497
498/**
499 * New with GL_EXT_timer_query
500 */
501static void GLAPIENTRY
502_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
503{
504   struct gl_query_object *q = NULL;
505   GET_CURRENT_CONTEXT(ctx);
506   ASSERT_OUTSIDE_BEGIN_END(ctx);
507
508   if (id)
509      q = lookup_query_object(ctx, id);
510
511   if (!q || q->Active) {
512      _mesa_error(ctx, GL_INVALID_OPERATION,
513                  "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
514      return;
515   }
516
517   switch (pname) {
518      case GL_QUERY_RESULT_ARB:
519         if (!q->Ready)
520            ctx->Driver.WaitQuery(ctx, q);
521         *params = q->Result;
522         break;
523      case GL_QUERY_RESULT_AVAILABLE_ARB:
524	 if (!q->Ready)
525	    ctx->Driver.CheckQuery( ctx, q );
526         *params = q->Ready;
527         break;
528      default:
529         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
530         return;
531   }
532}
533
534
535void
536_mesa_init_queryobj_dispatch(struct _glapi_table *disp)
537{
538   SET_GenQueriesARB(disp, _mesa_GenQueriesARB);
539   SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB);
540   SET_IsQueryARB(disp, _mesa_IsQueryARB);
541   SET_BeginQueryARB(disp, _mesa_BeginQueryARB);
542   SET_EndQueryARB(disp, _mesa_EndQueryARB);
543   SET_GetQueryivARB(disp, _mesa_GetQueryivARB);
544   SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB);
545   SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB);
546
547   SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT);
548   SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT);
549}
550
551
552#endif /* FEATURE_queryobj */
553
554
555/**
556 * Allocate/init the context state related to query objects.
557 */
558void
559_mesa_init_queryobj(GLcontext *ctx)
560{
561   ctx->Query.QueryObjects = _mesa_NewHashTable();
562   ctx->Query.CurrentOcclusionObject = NULL;
563}
564
565
566/**
567 * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
568 */
569static void
570delete_queryobj_cb(GLuint id, void *data, void *userData)
571{
572   struct gl_query_object *q= (struct gl_query_object *) data;
573   GLcontext *ctx = (GLcontext *)userData;
574   ctx->Driver.DeleteQuery(ctx, q);
575}
576
577
578/**
579 * Free the context state related to query objects.
580 */
581void
582_mesa_free_queryobj_data(GLcontext *ctx)
583{
584   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
585   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
586}
587