Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / mesa / main / queryobj.c
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  */
47 static 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  */
66 static 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  */
77 static 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  */
88 static 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  */
103 static 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  */
116 static void
117 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
118 {
119    free(q);
120 }
121
122
123 void
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  */
139 static struct gl_query_object **
140 get_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
176 static 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
215 static 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
251 static 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
267 static 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
322 static 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
355 static 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
389 static 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
440 static 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  */
494 static 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  */
535 static 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
573 void
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  */
596 void
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  */
607 static void
608 delete_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  */
619 void
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 }