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