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