queryobj.c revision c1f859d4
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
33
34/**
35 * Allocate a new query object.  This is a fallback routine called via
36 * ctx->Driver.NewQueryObject().
37 * \param ctx - rendering context
38 * \param id - the new object's ID
39 * \return pointer to new query_object object or NULL if out of memory.
40 */
41struct gl_query_object *
42_mesa_new_query_object(GLcontext *ctx, GLuint id)
43{
44   struct gl_query_object *q = MALLOC_STRUCT(gl_query_object);
45   (void) ctx;
46   if (q) {
47      q->Id = id;
48      q->Result = 0;
49      q->Active = GL_FALSE;
50      q->Ready = GL_TRUE;   /* correct, see spec */
51   }
52   return q;
53}
54
55
56/**
57 * Begin a query.  Software driver fallback.
58 * Called via ctx->Driver.BeginQuery().
59 */
60void
61_mesa_begin_query(GLcontext *ctx, struct gl_query_object *q)
62{
63   /* no-op */
64}
65
66
67/**
68 * End a query.  Software driver fallback.
69 * Called via ctx->Driver.EndQuery().
70 */
71void
72_mesa_end_query(GLcontext *ctx, struct gl_query_object *q)
73{
74   q->Ready = GL_TRUE;
75}
76
77
78/**
79 * Wait for query to complete.  Software driver fallback.
80 * Called via ctx->Driver.WaitQuery().
81 */
82void
83_mesa_wait_query(GLcontext *ctx, struct gl_query_object *q)
84{
85   /* For software drivers, _mesa_end_query() should have completed the query.
86    * For real hardware, implement a proper WaitQuery() driver function.
87    */
88   assert(q->Ready);
89}
90
91
92/**
93 * Delete a query object.  Called via ctx->Driver.DeleteQuery().
94 * Not removed from hash table here.
95 */
96void
97_mesa_delete_query(GLcontext *ctx, struct gl_query_object *q)
98{
99   _mesa_free(q);
100}
101
102
103static struct gl_query_object *
104lookup_query_object(GLcontext *ctx, GLuint id)
105{
106   return (struct gl_query_object *)
107      _mesa_HashLookup(ctx->Query.QueryObjects, id);
108}
109
110
111
112void GLAPIENTRY
113_mesa_GenQueriesARB(GLsizei n, GLuint *ids)
114{
115   GLuint first;
116   GET_CURRENT_CONTEXT(ctx);
117   ASSERT_OUTSIDE_BEGIN_END(ctx);
118
119   if (n < 0) {
120      _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
121      return;
122   }
123
124   /* No query objects can be active at this time! */
125   if (ctx->Query.CurrentOcclusionObject ||
126       ctx->Query.CurrentTimerObject) {
127      _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
128      return;
129   }
130
131   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
132   if (first) {
133      GLsizei i;
134      for (i = 0; i < n; i++) {
135         struct gl_query_object *q
136            = ctx->Driver.NewQueryObject(ctx, first + i);
137         if (!q) {
138            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
139            return;
140         }
141         ids[i] = first + i;
142         _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
143      }
144   }
145}
146
147
148void GLAPIENTRY
149_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
150{
151   GLint i;
152   GET_CURRENT_CONTEXT(ctx);
153   ASSERT_OUTSIDE_BEGIN_END(ctx);
154
155   if (n < 0) {
156      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
157      return;
158   }
159
160   /* No query objects can be active at this time! */
161   if (ctx->Query.CurrentOcclusionObject ||
162       ctx->Query.CurrentTimerObject) {
163      _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
164      return;
165   }
166
167   for (i = 0; i < n; i++) {
168      if (ids[i] > 0) {
169         struct gl_query_object *q = lookup_query_object(ctx, ids[i]);
170         if (q) {
171            ASSERT(!q->Active); /* should be caught earlier */
172            _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
173            ctx->Driver.DeleteQuery(ctx, q);
174         }
175      }
176   }
177}
178
179
180GLboolean GLAPIENTRY
181_mesa_IsQueryARB(GLuint id)
182{
183   GET_CURRENT_CONTEXT(ctx);
184   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
185
186   if (id && lookup_query_object(ctx, id))
187      return GL_TRUE;
188   else
189      return GL_FALSE;
190}
191
192
193void GLAPIENTRY
194_mesa_BeginQueryARB(GLenum target, GLuint id)
195{
196   struct gl_query_object *q;
197   GET_CURRENT_CONTEXT(ctx);
198   ASSERT_OUTSIDE_BEGIN_END(ctx);
199
200   FLUSH_VERTICES(ctx, _NEW_DEPTH);
201
202   switch (target) {
203      case GL_SAMPLES_PASSED_ARB:
204         if (!ctx->Extensions.ARB_occlusion_query) {
205            _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
206            return;
207         }
208         if (ctx->Query.CurrentOcclusionObject) {
209            _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
210            return;
211         }
212         break;
213#if FEATURE_EXT_timer_query
214      case GL_TIME_ELAPSED_EXT:
215         if (!ctx->Extensions.EXT_timer_query) {
216            _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
217            return;
218         }
219         if (ctx->Query.CurrentTimerObject) {
220            _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
221            return;
222         }
223         break;
224#endif
225      default:
226         _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
227         return;
228   }
229
230   if (id == 0) {
231      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
232      return;
233   }
234
235   q = lookup_query_object(ctx, id);
236   if (!q) {
237      /* create new object */
238      q = ctx->Driver.NewQueryObject(ctx, id);
239      if (!q) {
240         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
241         return;
242      }
243      _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
244   }
245   else {
246      /* pre-existing object */
247      if (q->Active) {
248         _mesa_error(ctx, GL_INVALID_OPERATION,
249                     "glBeginQueryARB(query already active)");
250         return;
251      }
252   }
253
254   q->Target = target;
255   q->Active = GL_TRUE;
256   q->Result = 0;
257   q->Ready = GL_FALSE;
258
259   if (target == GL_SAMPLES_PASSED_ARB) {
260      ctx->Query.CurrentOcclusionObject = q;
261   }
262#if FEATURE_EXT_timer_query
263   else if (target == GL_TIME_ELAPSED_EXT) {
264      ctx->Query.CurrentTimerObject = q;
265   }
266#endif
267
268   ctx->Driver.BeginQuery(ctx, q);
269}
270
271
272void GLAPIENTRY
273_mesa_EndQueryARB(GLenum target)
274{
275   struct gl_query_object *q;
276   GET_CURRENT_CONTEXT(ctx);
277   ASSERT_OUTSIDE_BEGIN_END(ctx);
278
279   FLUSH_VERTICES(ctx, _NEW_DEPTH);
280
281   switch (target) {
282      case GL_SAMPLES_PASSED_ARB:
283         if (!ctx->Extensions.ARB_occlusion_query) {
284            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
285            return;
286         }
287         q = ctx->Query.CurrentOcclusionObject;
288         ctx->Query.CurrentOcclusionObject = NULL;
289         break;
290#if FEATURE_EXT_timer_query
291      case GL_TIME_ELAPSED_EXT:
292         if (!ctx->Extensions.EXT_timer_query) {
293            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
294            return;
295         }
296         q = ctx->Query.CurrentTimerObject;
297         ctx->Query.CurrentTimerObject = NULL;
298         break;
299#endif
300      default:
301         _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
302         return;
303   }
304
305   if (!q || !q->Active) {
306      _mesa_error(ctx, GL_INVALID_OPERATION,
307                  "glEndQueryARB(no matching glBeginQueryARB)");
308      return;
309   }
310
311   q->Active = GL_FALSE;
312   ctx->Driver.EndQuery(ctx, q);
313}
314
315
316void GLAPIENTRY
317_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
318{
319   struct gl_query_object *q;
320   GET_CURRENT_CONTEXT(ctx);
321   ASSERT_OUTSIDE_BEGIN_END(ctx);
322
323   switch (target) {
324      case GL_SAMPLES_PASSED_ARB:
325         if (!ctx->Extensions.ARB_occlusion_query) {
326            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
327            return;
328         }
329         q = ctx->Query.CurrentOcclusionObject;
330         break;
331#if FEATURE_EXT_timer_query
332      case GL_TIME_ELAPSED_EXT:
333         if (!ctx->Extensions.EXT_timer_query) {
334            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
335            return;
336         }
337         q = ctx->Query.CurrentTimerObject;
338         break;
339#endif
340      default:
341         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)");
342         return;
343   }
344
345   switch (pname) {
346      case GL_QUERY_COUNTER_BITS_ARB:
347         *params = 8 * sizeof(q->Result);
348         break;
349      case GL_CURRENT_QUERY_ARB:
350         *params = q ? q->Id : 0;
351         break;
352      default:
353         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
354         return;
355   }
356}
357
358
359void GLAPIENTRY
360_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
361{
362   struct gl_query_object *q = NULL;
363   GET_CURRENT_CONTEXT(ctx);
364   ASSERT_OUTSIDE_BEGIN_END(ctx);
365
366   if (id)
367      q = lookup_query_object(ctx, id);
368
369   if (!q || q->Active) {
370      _mesa_error(ctx, GL_INVALID_OPERATION,
371                  "glGetQueryObjectivARB(id=%d is invalid or active)", id);
372      return;
373   }
374
375   switch (pname) {
376      case GL_QUERY_RESULT_ARB:
377         if (!q->Ready)
378            ctx->Driver.WaitQuery(ctx, q);
379         /* if result is too large for returned type, clamp to max value */
380         if (q->Result > 0x7fffffff) {
381            *params = 0x7fffffff;
382         }
383         else {
384            *params = (GLint)q->Result;
385         }
386         break;
387      case GL_QUERY_RESULT_AVAILABLE_ARB:
388	 if (!q->Ready)
389	    ctx->Driver.CheckQuery( ctx, q );
390         *params = q->Ready;
391         break;
392      default:
393         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
394         return;
395   }
396}
397
398
399void GLAPIENTRY
400_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
401{
402   struct gl_query_object *q = NULL;
403   GET_CURRENT_CONTEXT(ctx);
404   ASSERT_OUTSIDE_BEGIN_END(ctx);
405
406   if (id)
407      q = lookup_query_object(ctx, id);
408
409   if (!q || q->Active) {
410      _mesa_error(ctx, GL_INVALID_OPERATION,
411                  "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
412      return;
413   }
414
415   switch (pname) {
416      case GL_QUERY_RESULT_ARB:
417         if (!q->Ready)
418            ctx->Driver.WaitQuery(ctx, q);
419         /* if result is too large for returned type, clamp to max value */
420         if (q->Result > 0xffffffff) {
421            *params = 0xffffffff;
422         }
423         else {
424            *params = (GLuint)q->Result;
425         }
426         break;
427      case GL_QUERY_RESULT_AVAILABLE_ARB:
428	 if (!q->Ready)
429	    ctx->Driver.CheckQuery( ctx, q );
430         *params = q->Ready;
431         break;
432      default:
433         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
434         return;
435   }
436}
437
438
439#if FEATURE_EXT_timer_query
440
441/**
442 * New with GL_EXT_timer_query
443 */
444void GLAPIENTRY
445_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
446{
447   struct gl_query_object *q = NULL;
448   GET_CURRENT_CONTEXT(ctx);
449   ASSERT_OUTSIDE_BEGIN_END(ctx);
450
451   if (id)
452      q = lookup_query_object(ctx, id);
453
454   if (!q || q->Active) {
455      _mesa_error(ctx, GL_INVALID_OPERATION,
456                  "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
457      return;
458   }
459
460   switch (pname) {
461      case GL_QUERY_RESULT_ARB:
462         if (!q->Ready)
463            ctx->Driver.WaitQuery(ctx, q);
464         *params = q->Result;
465         break;
466      case GL_QUERY_RESULT_AVAILABLE_ARB:
467	 if (!q->Ready)
468	    ctx->Driver.CheckQuery( ctx, q );
469         *params = q->Ready;
470         break;
471      default:
472         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
473         return;
474   }
475}
476
477
478/**
479 * New with GL_EXT_timer_query
480 */
481void GLAPIENTRY
482_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
483{
484   struct gl_query_object *q = NULL;
485   GET_CURRENT_CONTEXT(ctx);
486   ASSERT_OUTSIDE_BEGIN_END(ctx);
487
488   if (id)
489      q = lookup_query_object(ctx, id);
490
491   if (!q || q->Active) {
492      _mesa_error(ctx, GL_INVALID_OPERATION,
493                  "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
494      return;
495   }
496
497   switch (pname) {
498      case GL_QUERY_RESULT_ARB:
499         if (!q->Ready)
500            ctx->Driver.WaitQuery(ctx, q);
501         *params = q->Result;
502         break;
503      case GL_QUERY_RESULT_AVAILABLE_ARB:
504	 if (!q->Ready)
505	    ctx->Driver.CheckQuery( ctx, q );
506         *params = q->Ready;
507         break;
508      default:
509         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
510         return;
511   }
512}
513
514#endif /* FEATURE_EXT_timer_query */
515
516
517/**
518 * Allocate/init the context state related to query objects.
519 */
520void
521_mesa_init_query(GLcontext *ctx)
522{
523#if FEATURE_ARB_occlusion_query
524   ctx->Query.QueryObjects = _mesa_NewHashTable();
525   ctx->Query.CurrentOcclusionObject = NULL;
526#endif
527}
528
529
530/**
531 * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
532 */
533static void
534delete_queryobj_cb(GLuint id, void *data, void *userData)
535{
536   struct gl_query_object *q= (struct gl_query_object *) data;
537   GLcontext *ctx = (GLcontext *)userData;
538   ctx->Driver.DeleteQuery(ctx, q);
539}
540
541
542/**
543 * Free the context state related to query objects.
544 */
545void
546_mesa_free_query_data(GLcontext *ctx)
547{
548   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
549   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
550}
551