1/**************************************************************************
2 *
3 * Copyright 2007 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28
29/**
30 * glBegin/EndQuery interface to pipe
31 *
32 * \author Brian Paul
33 */
34
35
36#include "main/imports.h"
37#include "main/context.h"
38
39#include "pipe/p_context.h"
40#include "pipe/p_defines.h"
41#include "pipe/p_screen.h"
42#include "util/u_inlines.h"
43#include "st_context.h"
44#include "st_cb_queryobj.h"
45#include "st_cb_bitmap.h"
46#include "st_cb_bufferobjects.h"
47
48
49static struct gl_query_object *
50st_NewQueryObject(struct gl_context *ctx, GLuint id)
51{
52   struct st_query_object *stq = ST_CALLOC_STRUCT(st_query_object);
53   if (stq) {
54      stq->base.Id = id;
55      stq->base.Ready = GL_TRUE;
56      stq->pq = NULL;
57      stq->type = PIPE_QUERY_TYPES; /* an invalid value */
58      return &stq->base;
59   }
60   return NULL;
61}
62
63
64static void
65free_queries(struct pipe_context *pipe, struct st_query_object *stq)
66{
67   if (stq->pq) {
68      pipe->destroy_query(pipe, stq->pq);
69      stq->pq = NULL;
70   }
71
72   if (stq->pq_begin) {
73      pipe->destroy_query(pipe, stq->pq_begin);
74      stq->pq_begin = NULL;
75   }
76}
77
78
79static void
80st_DeleteQuery(struct gl_context *ctx, struct gl_query_object *q)
81{
82   struct pipe_context *pipe = st_context(ctx)->pipe;
83   struct st_query_object *stq = st_query_object(q);
84
85   free_queries(pipe, stq);
86
87   free(stq);
88}
89
90
91static void
92st_BeginQuery(struct gl_context *ctx, struct gl_query_object *q)
93{
94   struct st_context *st = st_context(ctx);
95   struct pipe_context *pipe = st->pipe;
96   struct st_query_object *stq = st_query_object(q);
97   unsigned type;
98   bool ret = false;
99
100   st_flush_bitmap_cache(st_context(ctx));
101
102   /* convert GL query type to Gallium query type */
103   switch (q->Target) {
104   case GL_ANY_SAMPLES_PASSED:
105   case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
106      type = PIPE_QUERY_OCCLUSION_PREDICATE;
107      break;
108   case GL_SAMPLES_PASSED_ARB:
109      type = PIPE_QUERY_OCCLUSION_COUNTER;
110      break;
111   case GL_PRIMITIVES_GENERATED:
112      type = PIPE_QUERY_PRIMITIVES_GENERATED;
113      break;
114   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
115      type = PIPE_QUERY_PRIMITIVES_EMITTED;
116      break;
117   case GL_TIME_ELAPSED:
118      if (st->has_time_elapsed)
119         type = PIPE_QUERY_TIME_ELAPSED;
120      else
121         type = PIPE_QUERY_TIMESTAMP;
122      break;
123   case GL_VERTICES_SUBMITTED_ARB:
124   case GL_PRIMITIVES_SUBMITTED_ARB:
125   case GL_VERTEX_SHADER_INVOCATIONS_ARB:
126   case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
127   case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
128   case GL_GEOMETRY_SHADER_INVOCATIONS:
129   case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
130   case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
131   case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
132   case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
133   case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
134      type = PIPE_QUERY_PIPELINE_STATISTICS;
135      break;
136   default:
137      assert(0 && "unexpected query target in st_BeginQuery()");
138      return;
139   }
140
141   if (stq->type != type) {
142      /* free old query of different type */
143      free_queries(pipe, stq);
144      stq->type = PIPE_QUERY_TYPES; /* an invalid value */
145   }
146
147   if (q->Target == GL_TIME_ELAPSED &&
148       type == PIPE_QUERY_TIMESTAMP) {
149      /* Determine time elapsed by emitting two timestamp queries. */
150      if (!stq->pq_begin) {
151         stq->pq_begin = pipe->create_query(pipe, type, 0);
152         stq->type = type;
153      }
154      if (stq->pq_begin)
155         ret = pipe->end_query(pipe, stq->pq_begin);
156   } else {
157      if (!stq->pq) {
158         stq->pq = pipe->create_query(pipe, type, q->Stream);
159         stq->type = type;
160      }
161      if (stq->pq)
162         ret = pipe->begin_query(pipe, stq->pq);
163   }
164
165   if (!ret) {
166      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery");
167
168      free_queries(pipe, stq);
169      q->Active = GL_FALSE;
170      return;
171   }
172
173   assert(stq->type == type);
174}
175
176
177static void
178st_EndQuery(struct gl_context *ctx, struct gl_query_object *q)
179{
180   struct pipe_context *pipe = st_context(ctx)->pipe;
181   struct st_query_object *stq = st_query_object(q);
182   bool ret = false;
183
184   st_flush_bitmap_cache(st_context(ctx));
185
186   if ((q->Target == GL_TIMESTAMP ||
187        q->Target == GL_TIME_ELAPSED) &&
188       !stq->pq) {
189      stq->pq = pipe->create_query(pipe, PIPE_QUERY_TIMESTAMP, 0);
190      stq->type = PIPE_QUERY_TIMESTAMP;
191   }
192
193   if (stq->pq)
194      ret = pipe->end_query(pipe, stq->pq);
195
196   if (!ret) {
197      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glEndQuery");
198      return;
199   }
200}
201
202
203static boolean
204get_query_result(struct pipe_context *pipe,
205                 struct st_query_object *stq,
206                 boolean wait)
207{
208   union pipe_query_result data;
209
210   if (!stq->pq) {
211      /* Only needed in case we failed to allocate the gallium query earlier.
212       * Return TRUE so we don't spin on this forever.
213       */
214      return TRUE;
215   }
216
217   if (!pipe->get_query_result(pipe, stq->pq, wait, &data))
218      return FALSE;
219
220   switch (stq->base.Target) {
221   case GL_VERTICES_SUBMITTED_ARB:
222      stq->base.Result = data.pipeline_statistics.ia_vertices;
223      break;
224   case GL_PRIMITIVES_SUBMITTED_ARB:
225      stq->base.Result = data.pipeline_statistics.ia_primitives;
226      break;
227   case GL_VERTEX_SHADER_INVOCATIONS_ARB:
228      stq->base.Result = data.pipeline_statistics.vs_invocations;
229      break;
230   case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
231      stq->base.Result = data.pipeline_statistics.hs_invocations;
232      break;
233   case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
234      stq->base.Result = data.pipeline_statistics.ds_invocations;
235      break;
236   case GL_GEOMETRY_SHADER_INVOCATIONS:
237      stq->base.Result = data.pipeline_statistics.gs_invocations;
238      break;
239   case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
240      stq->base.Result = data.pipeline_statistics.gs_primitives;
241      break;
242   case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
243      stq->base.Result = data.pipeline_statistics.ps_invocations;
244      break;
245   case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
246      stq->base.Result = data.pipeline_statistics.cs_invocations;
247      break;
248   case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
249      stq->base.Result = data.pipeline_statistics.c_invocations;
250      break;
251   case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
252      stq->base.Result = data.pipeline_statistics.c_primitives;
253      break;
254   default:
255      switch (stq->type) {
256      case PIPE_QUERY_OCCLUSION_PREDICATE:
257         stq->base.Result = !!data.b;
258         break;
259      default:
260         stq->base.Result = data.u64;
261         break;
262      }
263      break;
264   }
265
266   if (stq->base.Target == GL_TIME_ELAPSED &&
267       stq->type == PIPE_QUERY_TIMESTAMP) {
268      /* Calculate the elapsed time from the two timestamp queries */
269      GLuint64EXT Result0 = 0;
270      assert(stq->pq_begin);
271      pipe->get_query_result(pipe, stq->pq_begin, TRUE, (void *)&Result0);
272      stq->base.Result -= Result0;
273   } else {
274      assert(!stq->pq_begin);
275   }
276
277   return TRUE;
278}
279
280
281static void
282st_WaitQuery(struct gl_context *ctx, struct gl_query_object *q)
283{
284   struct pipe_context *pipe = st_context(ctx)->pipe;
285   struct st_query_object *stq = st_query_object(q);
286
287   /* this function should only be called if we don't have a ready result */
288   assert(!stq->base.Ready);
289
290   while (!stq->base.Ready &&
291	  !get_query_result(pipe, stq, TRUE))
292   {
293      /* nothing */
294   }
295
296   q->Ready = GL_TRUE;
297}
298
299
300static void
301st_CheckQuery(struct gl_context *ctx, struct gl_query_object *q)
302{
303   struct pipe_context *pipe = st_context(ctx)->pipe;
304   struct st_query_object *stq = st_query_object(q);
305   assert(!q->Ready);   /* we should not get called if Ready is TRUE */
306   q->Ready = get_query_result(pipe, stq, FALSE);
307}
308
309
310static uint64_t
311st_GetTimestamp(struct gl_context *ctx)
312{
313   struct pipe_context *pipe = st_context(ctx)->pipe;
314   struct pipe_screen *screen = pipe->screen;
315
316   /* Prefer the per-screen function */
317   if (screen->get_timestamp) {
318      return screen->get_timestamp(screen);
319   }
320   else {
321      /* Fall back to the per-context function */
322      assert(pipe->get_timestamp);
323      return pipe->get_timestamp(pipe);
324   }
325}
326
327static void
328st_StoreQueryResult(struct gl_context *ctx, struct gl_query_object *q,
329                    struct gl_buffer_object *buf, intptr_t offset,
330                    GLenum pname, GLenum ptype)
331{
332   struct pipe_context *pipe = st_context(ctx)->pipe;
333   struct st_query_object *stq = st_query_object(q);
334   struct st_buffer_object *stObj = st_buffer_object(buf);
335   boolean wait = pname == GL_QUERY_RESULT;
336   enum pipe_query_value_type result_type;
337   int index;
338
339   /* GL_QUERY_TARGET is a bit of an extension since it has nothing to
340    * do with the GPU end of the query. Write it in "by hand".
341    */
342   if (pname == GL_QUERY_TARGET) {
343      /* Assume that the data must be LE. The endianness situation wrt CPU and
344       * GPU is incredibly confusing, but the vast majority of GPUs are
345       * LE. When a BE one comes along, this needs some form of resolution.
346       */
347      unsigned data[2] = { CPU_TO_LE32(q->Target), 0 };
348      pipe_buffer_write(pipe, stObj->buffer, offset,
349                        (ptype == GL_INT64_ARB ||
350                         ptype == GL_UNSIGNED_INT64_ARB) ? 8 : 4,
351                        data);
352      return;
353   }
354
355   switch (ptype) {
356   case GL_INT:
357      result_type = PIPE_QUERY_TYPE_I32;
358      break;
359   case GL_UNSIGNED_INT:
360      result_type = PIPE_QUERY_TYPE_U32;
361      break;
362   case GL_INT64_ARB:
363      result_type = PIPE_QUERY_TYPE_I64;
364      break;
365   case GL_UNSIGNED_INT64_ARB:
366      result_type = PIPE_QUERY_TYPE_U64;
367      break;
368   default:
369      unreachable("Unexpected result type");
370   }
371
372   if (pname == GL_QUERY_RESULT_AVAILABLE) {
373      index = -1;
374   } else if (stq->type == PIPE_QUERY_PIPELINE_STATISTICS) {
375      switch (q->Target) {
376      case GL_VERTICES_SUBMITTED_ARB:
377         index = 0;
378         break;
379      case GL_PRIMITIVES_SUBMITTED_ARB:
380         index = 1;
381         break;
382      case GL_VERTEX_SHADER_INVOCATIONS_ARB:
383         index = 2;
384         break;
385      case GL_GEOMETRY_SHADER_INVOCATIONS:
386         index = 3;
387         break;
388      case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
389         index = 4;
390         break;
391      case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
392         index = 5;
393         break;
394      case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
395         index = 6;
396         break;
397      case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
398         index = 7;
399         break;
400      case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
401         index = 8;
402         break;
403      case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
404         index = 9;
405         break;
406      case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
407         index = 10;
408         break;
409      default:
410         unreachable("Unexpected target");
411      }
412   } else {
413      index = 0;
414   }
415
416   pipe->get_query_result_resource(pipe, stq->pq, wait, result_type, index,
417                                   stObj->buffer, offset);
418}
419
420void st_init_query_functions(struct dd_function_table *functions)
421{
422   functions->NewQueryObject = st_NewQueryObject;
423   functions->DeleteQuery = st_DeleteQuery;
424   functions->BeginQuery = st_BeginQuery;
425   functions->EndQuery = st_EndQuery;
426   functions->WaitQuery = st_WaitQuery;
427   functions->CheckQuery = st_CheckQuery;
428   functions->GetTimestamp = st_GetTimestamp;
429   functions->StoreQueryResult = st_StoreQueryResult;
430}
431