queryobj.c revision cdc920a0
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 "main/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   free(q);
118}
119
120
121void
122_mesa_init_query_object_functions(struct dd_function_table *driver)
123{
124   driver->NewQueryObject = _mesa_new_query_object;
125   driver->DeleteQuery = _mesa_delete_query;
126   driver->BeginQuery = _mesa_begin_query;
127   driver->EndQuery = _mesa_end_query;
128   driver->WaitQuery = _mesa_wait_query;
129   driver->CheckQuery = _mesa_check_query;
130}
131
132
133void GLAPIENTRY
134_mesa_GenQueriesARB(GLsizei n, GLuint *ids)
135{
136   GLuint first;
137   GET_CURRENT_CONTEXT(ctx);
138   ASSERT_OUTSIDE_BEGIN_END(ctx);
139
140   if (n < 0) {
141      _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
142      return;
143   }
144
145   /* No query objects can be active at this time! */
146   if (ctx->Query.CurrentOcclusionObject ||
147       ctx->Query.CurrentTimerObject) {
148      _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
149      return;
150   }
151
152   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
153   if (first) {
154      GLsizei i;
155      for (i = 0; i < n; i++) {
156         struct gl_query_object *q
157            = ctx->Driver.NewQueryObject(ctx, first + i);
158         if (!q) {
159            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
160            return;
161         }
162         ids[i] = first + i;
163         _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
164      }
165   }
166}
167
168
169void GLAPIENTRY
170_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
171{
172   GLint i;
173   GET_CURRENT_CONTEXT(ctx);
174   ASSERT_OUTSIDE_BEGIN_END(ctx);
175
176   if (n < 0) {
177      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
178      return;
179   }
180
181   /* No query objects can be active at this time! */
182   if (ctx->Query.CurrentOcclusionObject ||
183       ctx->Query.CurrentTimerObject) {
184      _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
185      return;
186   }
187
188   for (i = 0; i < n; i++) {
189      if (ids[i] > 0) {
190         struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
191         if (q) {
192            ASSERT(!q->Active); /* should be caught earlier */
193            _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
194            ctx->Driver.DeleteQuery(ctx, q);
195         }
196      }
197   }
198}
199
200
201GLboolean GLAPIENTRY
202_mesa_IsQueryARB(GLuint id)
203{
204   GET_CURRENT_CONTEXT(ctx);
205   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
206
207   if (id && _mesa_lookup_query_object(ctx, id))
208      return GL_TRUE;
209   else
210      return GL_FALSE;
211}
212
213
214static void GLAPIENTRY
215_mesa_BeginQueryARB(GLenum target, GLuint id)
216{
217   struct gl_query_object *q;
218   GET_CURRENT_CONTEXT(ctx);
219   ASSERT_OUTSIDE_BEGIN_END(ctx);
220
221   FLUSH_VERTICES(ctx, _NEW_DEPTH);
222
223   switch (target) {
224      case GL_SAMPLES_PASSED_ARB:
225         if (!ctx->Extensions.ARB_occlusion_query) {
226            _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
227            return;
228         }
229         if (ctx->Query.CurrentOcclusionObject) {
230            _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
231            return;
232         }
233         break;
234      case GL_TIME_ELAPSED_EXT:
235         if (!ctx->Extensions.EXT_timer_query) {
236            _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
237            return;
238         }
239         if (ctx->Query.CurrentTimerObject) {
240            _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
241            return;
242         }
243         break;
244      default:
245         _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
246         return;
247   }
248
249   if (id == 0) {
250      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
251      return;
252   }
253
254   q = _mesa_lookup_query_object(ctx, id);
255   if (!q) {
256      /* create new object */
257      q = ctx->Driver.NewQueryObject(ctx, id);
258      if (!q) {
259         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
260         return;
261      }
262      _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
263   }
264   else {
265      /* pre-existing object */
266      if (q->Active) {
267         _mesa_error(ctx, GL_INVALID_OPERATION,
268                     "glBeginQueryARB(query already active)");
269         return;
270      }
271   }
272
273   q->Target = target;
274   q->Active = GL_TRUE;
275   q->Result = 0;
276   q->Ready = GL_FALSE;
277
278   if (target == GL_SAMPLES_PASSED_ARB) {
279      ctx->Query.CurrentOcclusionObject = q;
280   }
281   else if (target == GL_TIME_ELAPSED_EXT) {
282      ctx->Query.CurrentTimerObject = q;
283   }
284
285   ctx->Driver.BeginQuery(ctx, q);
286}
287
288
289static void GLAPIENTRY
290_mesa_EndQueryARB(GLenum target)
291{
292   struct gl_query_object *q;
293   GET_CURRENT_CONTEXT(ctx);
294   ASSERT_OUTSIDE_BEGIN_END(ctx);
295
296   FLUSH_VERTICES(ctx, _NEW_DEPTH);
297
298   switch (target) {
299      case GL_SAMPLES_PASSED_ARB:
300         if (!ctx->Extensions.ARB_occlusion_query) {
301            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
302            return;
303         }
304         q = ctx->Query.CurrentOcclusionObject;
305         ctx->Query.CurrentOcclusionObject = NULL;
306         break;
307      case GL_TIME_ELAPSED_EXT:
308         if (!ctx->Extensions.EXT_timer_query) {
309            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
310            return;
311         }
312         q = ctx->Query.CurrentTimerObject;
313         ctx->Query.CurrentTimerObject = NULL;
314         break;
315      default:
316         _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
317         return;
318   }
319
320   if (!q || !q->Active) {
321      _mesa_error(ctx, GL_INVALID_OPERATION,
322                  "glEndQueryARB(no matching glBeginQueryARB)");
323      return;
324   }
325
326   q->Active = GL_FALSE;
327   ctx->Driver.EndQuery(ctx, q);
328}
329
330
331void GLAPIENTRY
332_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
333{
334   struct gl_query_object *q;
335   GET_CURRENT_CONTEXT(ctx);
336   ASSERT_OUTSIDE_BEGIN_END(ctx);
337
338   switch (target) {
339      case GL_SAMPLES_PASSED_ARB:
340         if (!ctx->Extensions.ARB_occlusion_query) {
341            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
342            return;
343         }
344         q = ctx->Query.CurrentOcclusionObject;
345         break;
346      case GL_TIME_ELAPSED_EXT:
347         if (!ctx->Extensions.EXT_timer_query) {
348            _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
349            return;
350         }
351         q = ctx->Query.CurrentTimerObject;
352         break;
353      default:
354         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)");
355         return;
356   }
357
358   switch (pname) {
359      case GL_QUERY_COUNTER_BITS_ARB:
360         *params = 8 * sizeof(q->Result);
361         break;
362      case GL_CURRENT_QUERY_ARB:
363         *params = q ? q->Id : 0;
364         break;
365      default:
366         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
367         return;
368   }
369}
370
371
372void GLAPIENTRY
373_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
374{
375   struct gl_query_object *q = NULL;
376   GET_CURRENT_CONTEXT(ctx);
377   ASSERT_OUTSIDE_BEGIN_END(ctx);
378
379   if (id)
380      q = _mesa_lookup_query_object(ctx, id);
381
382   if (!q || q->Active) {
383      _mesa_error(ctx, GL_INVALID_OPERATION,
384                  "glGetQueryObjectivARB(id=%d is invalid or active)", id);
385      return;
386   }
387
388   switch (pname) {
389      case GL_QUERY_RESULT_ARB:
390         if (!q->Ready)
391            ctx->Driver.WaitQuery(ctx, q);
392         /* if result is too large for returned type, clamp to max value */
393         if (q->Result > 0x7fffffff) {
394            *params = 0x7fffffff;
395         }
396         else {
397            *params = (GLint)q->Result;
398         }
399         break;
400      case GL_QUERY_RESULT_AVAILABLE_ARB:
401	 if (!q->Ready)
402	    ctx->Driver.CheckQuery( ctx, q );
403         *params = q->Ready;
404         break;
405      default:
406         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
407         return;
408   }
409}
410
411
412void GLAPIENTRY
413_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
414{
415   struct gl_query_object *q = NULL;
416   GET_CURRENT_CONTEXT(ctx);
417   ASSERT_OUTSIDE_BEGIN_END(ctx);
418
419   if (id)
420      q = _mesa_lookup_query_object(ctx, id);
421
422   if (!q || q->Active) {
423      _mesa_error(ctx, GL_INVALID_OPERATION,
424                  "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
425      return;
426   }
427
428   switch (pname) {
429      case GL_QUERY_RESULT_ARB:
430         if (!q->Ready)
431            ctx->Driver.WaitQuery(ctx, q);
432         /* if result is too large for returned type, clamp to max value */
433         if (q->Result > 0xffffffff) {
434            *params = 0xffffffff;
435         }
436         else {
437            *params = (GLuint)q->Result;
438         }
439         break;
440      case GL_QUERY_RESULT_AVAILABLE_ARB:
441	 if (!q->Ready)
442	    ctx->Driver.CheckQuery( ctx, q );
443         *params = q->Ready;
444         break;
445      default:
446         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
447         return;
448   }
449}
450
451
452/**
453 * New with GL_EXT_timer_query
454 */
455static void GLAPIENTRY
456_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
457{
458   struct gl_query_object *q = NULL;
459   GET_CURRENT_CONTEXT(ctx);
460   ASSERT_OUTSIDE_BEGIN_END(ctx);
461
462   if (id)
463      q = _mesa_lookup_query_object(ctx, id);
464
465   if (!q || q->Active) {
466      _mesa_error(ctx, GL_INVALID_OPERATION,
467                  "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
468      return;
469   }
470
471   switch (pname) {
472      case GL_QUERY_RESULT_ARB:
473         if (!q->Ready)
474            ctx->Driver.WaitQuery(ctx, q);
475         *params = q->Result;
476         break;
477      case GL_QUERY_RESULT_AVAILABLE_ARB:
478	 if (!q->Ready)
479	    ctx->Driver.CheckQuery( ctx, q );
480         *params = q->Ready;
481         break;
482      default:
483         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
484         return;
485   }
486}
487
488
489/**
490 * New with GL_EXT_timer_query
491 */
492static void GLAPIENTRY
493_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
494{
495   struct gl_query_object *q = NULL;
496   GET_CURRENT_CONTEXT(ctx);
497   ASSERT_OUTSIDE_BEGIN_END(ctx);
498
499   if (id)
500      q = _mesa_lookup_query_object(ctx, id);
501
502   if (!q || q->Active) {
503      _mesa_error(ctx, GL_INVALID_OPERATION,
504                  "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
505      return;
506   }
507
508   switch (pname) {
509      case GL_QUERY_RESULT_ARB:
510         if (!q->Ready)
511            ctx->Driver.WaitQuery(ctx, q);
512         *params = q->Result;
513         break;
514      case GL_QUERY_RESULT_AVAILABLE_ARB:
515	 if (!q->Ready)
516	    ctx->Driver.CheckQuery( ctx, q );
517         *params = q->Ready;
518         break;
519      default:
520         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
521         return;
522   }
523}
524
525
526void
527_mesa_init_queryobj_dispatch(struct _glapi_table *disp)
528{
529   SET_GenQueriesARB(disp, _mesa_GenQueriesARB);
530   SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB);
531   SET_IsQueryARB(disp, _mesa_IsQueryARB);
532   SET_BeginQueryARB(disp, _mesa_BeginQueryARB);
533   SET_EndQueryARB(disp, _mesa_EndQueryARB);
534   SET_GetQueryivARB(disp, _mesa_GetQueryivARB);
535   SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB);
536   SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB);
537
538   SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT);
539   SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT);
540}
541
542
543#endif /* FEATURE_queryobj */
544
545
546/**
547 * Allocate/init the context state related to query objects.
548 */
549void
550_mesa_init_queryobj(GLcontext *ctx)
551{
552   ctx->Query.QueryObjects = _mesa_NewHashTable();
553   ctx->Query.CurrentOcclusionObject = NULL;
554}
555
556
557/**
558 * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
559 */
560static void
561delete_queryobj_cb(GLuint id, void *data, void *userData)
562{
563   struct gl_query_object *q= (struct gl_query_object *) data;
564   GLcontext *ctx = (GLcontext *)userData;
565   ctx->Driver.DeleteQuery(ctx, q);
566}
567
568
569/**
570 * Free the context state related to query objects.
571 */
572void
573_mesa_free_queryobj_data(GLcontext *ctx)
574{
575   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
576   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
577}
578