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/compiler.h" 38#include "main/context.h" 39 40#include "pipe/p_context.h" 41#include "pipe/p_defines.h" 42#include "pipe/p_screen.h" 43#include "util/u_inlines.h" 44#include "st_context.h" 45#include "st_cb_queryobj.h" 46#include "st_cb_bitmap.h" 47#include "st_cb_bufferobjects.h" 48#include "st_util.h" 49 50 51static struct gl_query_object * 52st_NewQueryObject(struct gl_context *ctx, GLuint id) 53{ 54 struct st_query_object *stq = ST_CALLOC_STRUCT(st_query_object); 55 if (stq) { 56 stq->base.Id = id; 57 stq->base.Ready = GL_TRUE; 58 stq->pq = NULL; 59 stq->type = PIPE_QUERY_TYPES; /* an invalid value */ 60 return &stq->base; 61 } 62 return NULL; 63} 64 65 66static void 67free_queries(struct pipe_context *pipe, struct st_query_object *stq) 68{ 69 if (stq->pq) { 70 pipe->destroy_query(pipe, stq->pq); 71 stq->pq = NULL; 72 } 73 74 if (stq->pq_begin) { 75 pipe->destroy_query(pipe, stq->pq_begin); 76 stq->pq_begin = NULL; 77 } 78} 79 80 81static void 82st_DeleteQuery(struct gl_context *ctx, struct gl_query_object *q) 83{ 84 struct pipe_context *pipe = st_context(ctx)->pipe; 85 struct st_query_object *stq = st_query_object(q); 86 87 free_queries(pipe, stq); 88 89 free(stq); 90} 91 92static int 93target_to_index(const struct st_context *st, const struct gl_query_object *q) 94{ 95 if (q->Target == GL_PRIMITIVES_GENERATED || 96 q->Target == GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN || 97 q->Target == GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB) 98 return q->Stream; 99 100 if (st->has_single_pipe_stat) { 101 switch (q->Target) { 102 case GL_VERTICES_SUBMITTED_ARB: 103 return PIPE_STAT_QUERY_IA_VERTICES; 104 case GL_PRIMITIVES_SUBMITTED_ARB: 105 return PIPE_STAT_QUERY_IA_PRIMITIVES; 106 case GL_VERTEX_SHADER_INVOCATIONS_ARB: 107 return PIPE_STAT_QUERY_VS_INVOCATIONS; 108 case GL_GEOMETRY_SHADER_INVOCATIONS: 109 return PIPE_STAT_QUERY_GS_INVOCATIONS; 110 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: 111 return PIPE_STAT_QUERY_GS_PRIMITIVES; 112 case GL_CLIPPING_INPUT_PRIMITIVES_ARB: 113 return PIPE_STAT_QUERY_C_INVOCATIONS; 114 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: 115 return PIPE_STAT_QUERY_C_PRIMITIVES; 116 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: 117 return PIPE_STAT_QUERY_PS_INVOCATIONS; 118 case GL_TESS_CONTROL_SHADER_PATCHES_ARB: 119 return PIPE_STAT_QUERY_HS_INVOCATIONS; 120 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: 121 return PIPE_STAT_QUERY_DS_INVOCATIONS; 122 case GL_COMPUTE_SHADER_INVOCATIONS_ARB: 123 return PIPE_STAT_QUERY_CS_INVOCATIONS; 124 default: 125 break; 126 } 127 } 128 129 return 0; 130} 131 132static void 133st_BeginQuery(struct gl_context *ctx, struct gl_query_object *q) 134{ 135 struct st_context *st = st_context(ctx); 136 struct pipe_context *pipe = st->pipe; 137 struct st_query_object *stq = st_query_object(q); 138 unsigned type; 139 bool ret = false; 140 141 st_flush_bitmap_cache(st_context(ctx)); 142 143 /* convert GL query type to Gallium query type */ 144 switch (q->Target) { 145 case GL_ANY_SAMPLES_PASSED: 146 type = PIPE_QUERY_OCCLUSION_PREDICATE; 147 break; 148 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 149 type = PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE; 150 break; 151 case GL_SAMPLES_PASSED_ARB: 152 type = PIPE_QUERY_OCCLUSION_COUNTER; 153 break; 154 case GL_PRIMITIVES_GENERATED: 155 type = PIPE_QUERY_PRIMITIVES_GENERATED; 156 break; 157 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 158 type = PIPE_QUERY_PRIMITIVES_EMITTED; 159 break; 160 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB: 161 type = PIPE_QUERY_SO_OVERFLOW_PREDICATE; 162 break; 163 case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB: 164 type = PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE; 165 break; 166 case GL_TIME_ELAPSED: 167 if (st->has_time_elapsed) 168 type = PIPE_QUERY_TIME_ELAPSED; 169 else 170 type = PIPE_QUERY_TIMESTAMP; 171 break; 172 case GL_VERTICES_SUBMITTED_ARB: 173 case GL_PRIMITIVES_SUBMITTED_ARB: 174 case GL_VERTEX_SHADER_INVOCATIONS_ARB: 175 case GL_TESS_CONTROL_SHADER_PATCHES_ARB: 176 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: 177 case GL_GEOMETRY_SHADER_INVOCATIONS: 178 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: 179 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: 180 case GL_COMPUTE_SHADER_INVOCATIONS_ARB: 181 case GL_CLIPPING_INPUT_PRIMITIVES_ARB: 182 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: 183 type = st->has_single_pipe_stat ? PIPE_QUERY_PIPELINE_STATISTICS_SINGLE 184 : PIPE_QUERY_PIPELINE_STATISTICS; 185 break; 186 default: 187 assert(0 && "unexpected query target in st_BeginQuery()"); 188 return; 189 } 190 191 if (stq->type != type) { 192 /* free old query of different type */ 193 free_queries(pipe, stq); 194 stq->type = PIPE_QUERY_TYPES; /* an invalid value */ 195 } 196 197 if (q->Target == GL_TIME_ELAPSED && 198 type == PIPE_QUERY_TIMESTAMP) { 199 /* Determine time elapsed by emitting two timestamp queries. */ 200 if (!stq->pq_begin) { 201 stq->pq_begin = pipe->create_query(pipe, type, 0); 202 stq->type = type; 203 } 204 if (stq->pq_begin) 205 ret = pipe->end_query(pipe, stq->pq_begin); 206 } else { 207 if (!stq->pq) { 208 stq->pq = pipe->create_query(pipe, type, target_to_index(st, q)); 209 stq->type = type; 210 } 211 if (stq->pq) 212 ret = pipe->begin_query(pipe, stq->pq); 213 } 214 215 if (!ret) { 216 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery"); 217 218 free_queries(pipe, stq); 219 q->Active = GL_FALSE; 220 return; 221 } 222 223 assert(stq->type == type); 224} 225 226 227static void 228st_EndQuery(struct gl_context *ctx, struct gl_query_object *q) 229{ 230 struct pipe_context *pipe = st_context(ctx)->pipe; 231 struct st_query_object *stq = st_query_object(q); 232 bool ret = false; 233 234 st_flush_bitmap_cache(st_context(ctx)); 235 236 if ((q->Target == GL_TIMESTAMP || 237 q->Target == GL_TIME_ELAPSED) && 238 !stq->pq) { 239 stq->pq = pipe->create_query(pipe, PIPE_QUERY_TIMESTAMP, 0); 240 stq->type = PIPE_QUERY_TIMESTAMP; 241 } 242 243 if (stq->pq) 244 ret = pipe->end_query(pipe, stq->pq); 245 246 if (!ret) { 247 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glEndQuery"); 248 return; 249 } 250} 251 252 253static boolean 254get_query_result(struct pipe_context *pipe, 255 struct st_query_object *stq, 256 boolean wait) 257{ 258 union pipe_query_result data; 259 260 if (!stq->pq) { 261 /* Only needed in case we failed to allocate the gallium query earlier. 262 * Return TRUE so we don't spin on this forever. 263 */ 264 return TRUE; 265 } 266 267 if (!pipe->get_query_result(pipe, stq->pq, wait, &data)) 268 return FALSE; 269 270 switch (stq->type) { 271 case PIPE_QUERY_PIPELINE_STATISTICS: 272 switch (stq->base.Target) { 273 case GL_VERTICES_SUBMITTED_ARB: 274 stq->base.Result = data.pipeline_statistics.ia_vertices; 275 break; 276 case GL_PRIMITIVES_SUBMITTED_ARB: 277 stq->base.Result = data.pipeline_statistics.ia_primitives; 278 break; 279 case GL_VERTEX_SHADER_INVOCATIONS_ARB: 280 stq->base.Result = data.pipeline_statistics.vs_invocations; 281 break; 282 case GL_TESS_CONTROL_SHADER_PATCHES_ARB: 283 stq->base.Result = data.pipeline_statistics.hs_invocations; 284 break; 285 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: 286 stq->base.Result = data.pipeline_statistics.ds_invocations; 287 break; 288 case GL_GEOMETRY_SHADER_INVOCATIONS: 289 stq->base.Result = data.pipeline_statistics.gs_invocations; 290 break; 291 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: 292 stq->base.Result = data.pipeline_statistics.gs_primitives; 293 break; 294 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: 295 stq->base.Result = data.pipeline_statistics.ps_invocations; 296 break; 297 case GL_COMPUTE_SHADER_INVOCATIONS_ARB: 298 stq->base.Result = data.pipeline_statistics.cs_invocations; 299 break; 300 case GL_CLIPPING_INPUT_PRIMITIVES_ARB: 301 stq->base.Result = data.pipeline_statistics.c_invocations; 302 break; 303 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: 304 stq->base.Result = data.pipeline_statistics.c_primitives; 305 break; 306 default: 307 unreachable("invalid pipeline statistics counter"); 308 } 309 break; 310 case PIPE_QUERY_OCCLUSION_PREDICATE: 311 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: 312 case PIPE_QUERY_SO_OVERFLOW_PREDICATE: 313 case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE: 314 stq->base.Result = !!data.b; 315 break; 316 default: 317 stq->base.Result = data.u64; 318 break; 319 } 320 321 if (stq->base.Target == GL_TIME_ELAPSED && 322 stq->type == PIPE_QUERY_TIMESTAMP) { 323 /* Calculate the elapsed time from the two timestamp queries */ 324 GLuint64EXT Result0 = 0; 325 assert(stq->pq_begin); 326 pipe->get_query_result(pipe, stq->pq_begin, TRUE, (void *)&Result0); 327 stq->base.Result -= Result0; 328 } else { 329 assert(!stq->pq_begin); 330 } 331 332 return TRUE; 333} 334 335 336static void 337st_WaitQuery(struct gl_context *ctx, struct gl_query_object *q) 338{ 339 struct pipe_context *pipe = st_context(ctx)->pipe; 340 struct st_query_object *stq = st_query_object(q); 341 342 /* this function should only be called if we don't have a ready result */ 343 assert(!stq->base.Ready); 344 345 while (!stq->base.Ready && 346 !get_query_result(pipe, stq, TRUE)) 347 { 348 /* nothing */ 349 } 350 351 q->Ready = GL_TRUE; 352} 353 354 355static void 356st_CheckQuery(struct gl_context *ctx, struct gl_query_object *q) 357{ 358 struct pipe_context *pipe = st_context(ctx)->pipe; 359 struct st_query_object *stq = st_query_object(q); 360 assert(!q->Ready); /* we should not get called if Ready is TRUE */ 361 q->Ready = get_query_result(pipe, stq, FALSE); 362} 363 364 365static uint64_t 366st_GetTimestamp(struct gl_context *ctx) 367{ 368 struct pipe_context *pipe = st_context(ctx)->pipe; 369 struct pipe_screen *screen = pipe->screen; 370 371 /* Prefer the per-screen function */ 372 if (screen->get_timestamp) { 373 return screen->get_timestamp(screen); 374 } 375 else { 376 /* Fall back to the per-context function */ 377 assert(pipe->get_timestamp); 378 return pipe->get_timestamp(pipe); 379 } 380} 381 382static void 383st_StoreQueryResult(struct gl_context *ctx, struct gl_query_object *q, 384 struct gl_buffer_object *buf, intptr_t offset, 385 GLenum pname, GLenum ptype) 386{ 387 struct pipe_context *pipe = st_context(ctx)->pipe; 388 struct st_query_object *stq = st_query_object(q); 389 struct st_buffer_object *stObj = st_buffer_object(buf); 390 boolean wait = pname == GL_QUERY_RESULT; 391 enum pipe_query_value_type result_type; 392 int index; 393 394 /* GL_QUERY_TARGET is a bit of an extension since it has nothing to 395 * do with the GPU end of the query. Write it in "by hand". 396 */ 397 if (pname == GL_QUERY_TARGET) { 398 /* Assume that the data must be LE. The endianness situation wrt CPU and 399 * GPU is incredibly confusing, but the vast majority of GPUs are 400 * LE. When a BE one comes along, this needs some form of resolution. 401 */ 402 unsigned data[2] = { CPU_TO_LE32(q->Target), 0 }; 403 pipe_buffer_write(pipe, stObj->buffer, offset, 404 (ptype == GL_INT64_ARB || 405 ptype == GL_UNSIGNED_INT64_ARB) ? 8 : 4, 406 data); 407 return; 408 } 409 410 switch (ptype) { 411 case GL_INT: 412 result_type = PIPE_QUERY_TYPE_I32; 413 break; 414 case GL_UNSIGNED_INT: 415 result_type = PIPE_QUERY_TYPE_U32; 416 break; 417 case GL_INT64_ARB: 418 result_type = PIPE_QUERY_TYPE_I64; 419 break; 420 case GL_UNSIGNED_INT64_ARB: 421 result_type = PIPE_QUERY_TYPE_U64; 422 break; 423 default: 424 unreachable("Unexpected result type"); 425 } 426 427 if (pname == GL_QUERY_RESULT_AVAILABLE) { 428 index = -1; 429 } else if (stq->type == PIPE_QUERY_PIPELINE_STATISTICS) { 430 switch (q->Target) { 431 case GL_VERTICES_SUBMITTED_ARB: 432 index = PIPE_STAT_QUERY_IA_VERTICES; 433 break; 434 case GL_PRIMITIVES_SUBMITTED_ARB: 435 index = PIPE_STAT_QUERY_IA_PRIMITIVES; 436 break; 437 case GL_VERTEX_SHADER_INVOCATIONS_ARB: 438 index = PIPE_STAT_QUERY_VS_INVOCATIONS; 439 break; 440 case GL_GEOMETRY_SHADER_INVOCATIONS: 441 index = PIPE_STAT_QUERY_GS_INVOCATIONS; 442 break; 443 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: 444 index = PIPE_STAT_QUERY_GS_PRIMITIVES; 445 break; 446 case GL_CLIPPING_INPUT_PRIMITIVES_ARB: 447 index = PIPE_STAT_QUERY_C_INVOCATIONS; 448 break; 449 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: 450 index = PIPE_STAT_QUERY_C_PRIMITIVES; 451 break; 452 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: 453 index = PIPE_STAT_QUERY_PS_INVOCATIONS; 454 break; 455 case GL_TESS_CONTROL_SHADER_PATCHES_ARB: 456 index = PIPE_STAT_QUERY_HS_INVOCATIONS; 457 break; 458 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: 459 index = PIPE_STAT_QUERY_DS_INVOCATIONS; 460 break; 461 case GL_COMPUTE_SHADER_INVOCATIONS_ARB: 462 index = PIPE_STAT_QUERY_CS_INVOCATIONS; 463 break; 464 default: 465 unreachable("Unexpected target"); 466 } 467 } else { 468 index = 0; 469 } 470 471 pipe->get_query_result_resource(pipe, stq->pq, wait, result_type, index, 472 stObj->buffer, offset); 473} 474 475void st_init_query_functions(struct dd_function_table *functions) 476{ 477 functions->NewQueryObject = st_NewQueryObject; 478 functions->DeleteQuery = st_DeleteQuery; 479 functions->BeginQuery = st_BeginQuery; 480 functions->EndQuery = st_EndQuery; 481 functions->WaitQuery = st_WaitQuery; 482 functions->CheckQuery = st_CheckQuery; 483 functions->GetTimestamp = st_GetTimestamp; 484 functions->StoreQueryResult = st_StoreQueryResult; 485} 486