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