OSDN Git Service

554e0b0d1814e96cc98dea3ff702183af9b33f32
[android-x86/external-mesa.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 "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  */
41 struct 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  */
60 void
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  */
71 void
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  */
82 void
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  */
96 void
97 _mesa_delete_query(GLcontext *ctx, struct gl_query_object *q)
98 {
99    _mesa_free(q);
100 }
101
102
103 static struct gl_query_object *
104 lookup_query_object(GLcontext *ctx, GLuint id)
105 {
106    return (struct gl_query_object *)
107       _mesa_HashLookup(ctx->Query.QueryObjects, id);
108 }
109
110
111
112 void 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
148 void 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
180 GLboolean 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
193 void 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
272 void 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
316 void 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
359 void 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
399 void 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  */
444 void 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  */
481 void 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  */
520 void
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  */
533 static void
534 delete_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  */
545 void
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 }