1 /* 2 * Copyright 2005 Oliver Stieber 3 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers 4 * Copyright 2009-2010 Henri Verbeet for CodeWeavers. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 22 #include "wined3d_private.h" 23 24 WINE_DEFAULT_DEBUG_CHANNEL(d3d); 25 26 static UINT64 get_query_result64(GLuint id, const struct wined3d_gl_info *gl_info) 27 { 28 if (gl_info->supported[ARB_TIMER_QUERY]) 29 { 30 GLuint64 result; 31 GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, &result)); 32 return result; 33 } 34 else 35 { 36 GLuint result; 37 GL_EXTCALL(glGetQueryObjectuiv(id, GL_QUERY_RESULT, &result)); 38 return result; 39 } 40 } 41 42 static void wined3d_query_init(struct wined3d_query *query, struct wined3d_device *device, 43 enum wined3d_query_type type, const void *data, DWORD data_size, 44 const struct wined3d_query_ops *query_ops, void *parent, const struct wined3d_parent_ops *parent_ops) 45 { 46 query->ref = 1; 47 query->parent = parent; 48 query->parent_ops = parent_ops; 49 query->device = device; 50 query->state = QUERY_CREATED; 51 query->type = type; 52 query->data = data; 53 query->data_size = data_size; 54 query->query_ops = query_ops; 55 list_init(&query->poll_list_entry); 56 } 57 58 static struct wined3d_event_query *wined3d_event_query_from_query(struct wined3d_query *query) 59 { 60 return CONTAINING_RECORD(query, struct wined3d_event_query, query); 61 } 62 63 static struct wined3d_occlusion_query *wined3d_occlusion_query_from_query(struct wined3d_query *query) 64 { 65 return CONTAINING_RECORD(query, struct wined3d_occlusion_query, query); 66 } 67 68 static struct wined3d_timestamp_query *wined3d_timestamp_query_from_query(struct wined3d_query *query) 69 { 70 return CONTAINING_RECORD(query, struct wined3d_timestamp_query, query); 71 } 72 73 static struct wined3d_so_statistics_query *wined3d_so_statistics_query_from_query(struct wined3d_query *query) 74 { 75 return CONTAINING_RECORD(query, struct wined3d_so_statistics_query, query); 76 } 77 78 static struct wined3d_pipeline_statistics_query *wined3d_pipeline_statistics_query_from_query( 79 struct wined3d_query *query) 80 { 81 return CONTAINING_RECORD(query, struct wined3d_pipeline_statistics_query, query); 82 } 83 84 static BOOL wined3d_fence_supported(const struct wined3d_gl_info *gl_info) 85 { 86 return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE]; 87 } 88 89 static enum wined3d_fence_result wined3d_fence_test(const struct wined3d_fence *fence, 90 const struct wined3d_device *device, DWORD flags) 91 { 92 const struct wined3d_gl_info *gl_info; 93 struct wined3d_context *context; 94 enum wined3d_fence_result ret; 95 BOOL fence_result; 96 97 TRACE("fence %p, device %p, flags %#x.\n", fence, device, flags); 98 99 if (!fence->context) 100 { 101 TRACE("Fence not issued.\n"); 102 return WINED3D_FENCE_NOT_STARTED; 103 } 104 105 if (!(context = context_reacquire(device, fence->context))) 106 { 107 if (!fence->context->gl_info->supported[ARB_SYNC]) 108 { 109 WARN("Fence tested from wrong thread.\n"); 110 return WINED3D_FENCE_WRONG_THREAD; 111 } 112 context = context_acquire(device, NULL, 0); 113 } 114 gl_info = context->gl_info; 115 116 if (gl_info->supported[ARB_SYNC]) 117 { 118 GLenum gl_ret = GL_EXTCALL(glClientWaitSync(fence->object.sync, 119 (flags & WINED3DGETDATA_FLUSH) ? GL_SYNC_FLUSH_COMMANDS_BIT : 0, 0)); 120 checkGLcall("glClientWaitSync"); 121 122 switch (gl_ret) 123 { 124 case GL_ALREADY_SIGNALED: 125 case GL_CONDITION_SATISFIED: 126 ret = WINED3D_FENCE_OK; 127 break; 128 129 case GL_TIMEOUT_EXPIRED: 130 ret = WINED3D_FENCE_WAITING; 131 break; 132 133 case GL_WAIT_FAILED: 134 default: 135 ERR("glClientWaitSync returned %#x.\n", gl_ret); 136 ret = WINED3D_FENCE_ERROR; 137 } 138 } 139 else if (gl_info->supported[APPLE_FENCE]) 140 { 141 fence_result = GL_EXTCALL(glTestFenceAPPLE(fence->object.id)); 142 checkGLcall("glTestFenceAPPLE"); 143 if (fence_result) 144 ret = WINED3D_FENCE_OK; 145 else 146 ret = WINED3D_FENCE_WAITING; 147 } 148 else if (gl_info->supported[NV_FENCE]) 149 { 150 fence_result = GL_EXTCALL(glTestFenceNV(fence->object.id)); 151 checkGLcall("glTestFenceNV"); 152 if (fence_result) 153 ret = WINED3D_FENCE_OK; 154 else 155 ret = WINED3D_FENCE_WAITING; 156 } 157 else 158 { 159 ERR("Fence created despite lack of GL support.\n"); 160 ret = WINED3D_FENCE_ERROR; 161 } 162 163 context_release(context); 164 return ret; 165 } 166 167 enum wined3d_fence_result wined3d_fence_wait(const struct wined3d_fence *fence, 168 const struct wined3d_device *device) 169 { 170 const struct wined3d_gl_info *gl_info; 171 struct wined3d_context *context; 172 enum wined3d_fence_result ret; 173 174 TRACE("fence %p, device %p.\n", fence, device); 175 176 if (!fence->context) 177 { 178 TRACE("Fence not issued.\n"); 179 return WINED3D_FENCE_NOT_STARTED; 180 } 181 gl_info = fence->context->gl_info; 182 183 if (!(context = context_reacquire(device, fence->context))) 184 { 185 /* A glFinish does not reliably wait for draws in other contexts. The caller has 186 * to find its own way to cope with the thread switch 187 */ 188 if (!gl_info->supported[ARB_SYNC]) 189 { 190 WARN("Fence finished from wrong thread.\n"); 191 return WINED3D_FENCE_WRONG_THREAD; 192 } 193 context = context_acquire(device, NULL, 0); 194 } 195 gl_info = context->gl_info; 196 197 if (gl_info->supported[ARB_SYNC]) 198 { 199 /* Timeouts near 0xffffffffffffffff may immediately return GL_TIMEOUT_EXPIRED, 200 * possibly because macOS internally adds some slop to the timer. To avoid this, 201 * we use a large number that isn't near the point of overflow (macOS 10.12.5). 202 */ 203 GLenum gl_ret = GL_EXTCALL(glClientWaitSync(fence->object.sync, 204 GL_SYNC_FLUSH_COMMANDS_BIT, ~(GLuint64)0 >> 1)); 205 checkGLcall("glClientWaitSync"); 206 207 switch (gl_ret) 208 { 209 case GL_ALREADY_SIGNALED: 210 case GL_CONDITION_SATISFIED: 211 ret = WINED3D_FENCE_OK; 212 break; 213 214 /* We don't expect a timeout for a ~292 year wait */ 215 default: 216 ERR("glClientWaitSync returned %#x.\n", gl_ret); 217 ret = WINED3D_FENCE_ERROR; 218 } 219 } 220 else if (context->gl_info->supported[APPLE_FENCE]) 221 { 222 GL_EXTCALL(glFinishFenceAPPLE(fence->object.id)); 223 checkGLcall("glFinishFenceAPPLE"); 224 ret = WINED3D_FENCE_OK; 225 } 226 else if (context->gl_info->supported[NV_FENCE]) 227 { 228 GL_EXTCALL(glFinishFenceNV(fence->object.id)); 229 checkGLcall("glFinishFenceNV"); 230 ret = WINED3D_FENCE_OK; 231 } 232 else 233 { 234 ERR("Fence created without GL support.\n"); 235 ret = WINED3D_FENCE_ERROR; 236 } 237 238 context_release(context); 239 return ret; 240 } 241 242 void wined3d_fence_issue(struct wined3d_fence *fence, const struct wined3d_device *device) 243 { 244 struct wined3d_context *context = NULL; 245 const struct wined3d_gl_info *gl_info; 246 247 if (fence->context && !(context = context_reacquire(device, fence->context)) 248 && !fence->context->gl_info->supported[ARB_SYNC]) 249 context_free_fence(fence); 250 if (!context) 251 context = context_acquire(device, NULL, 0); 252 gl_info = context->gl_info; 253 if (!fence->context) 254 context_alloc_fence(context, fence); 255 256 if (gl_info->supported[ARB_SYNC]) 257 { 258 if (fence->object.sync) 259 GL_EXTCALL(glDeleteSync(fence->object.sync)); 260 checkGLcall("glDeleteSync"); 261 fence->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); 262 checkGLcall("glFenceSync"); 263 } 264 else if (gl_info->supported[APPLE_FENCE]) 265 { 266 GL_EXTCALL(glSetFenceAPPLE(fence->object.id)); 267 checkGLcall("glSetFenceAPPLE"); 268 } 269 else if (gl_info->supported[NV_FENCE]) 270 { 271 GL_EXTCALL(glSetFenceNV(fence->object.id, GL_ALL_COMPLETED_NV)); 272 checkGLcall("glSetFenceNV"); 273 } 274 275 context_release(context); 276 } 277 278 static void wined3d_fence_free(struct wined3d_fence *fence) 279 { 280 if (fence->context) 281 context_free_fence(fence); 282 } 283 284 void wined3d_fence_destroy(struct wined3d_fence *fence) 285 { 286 wined3d_fence_free(fence); 287 HeapFree(GetProcessHeap(), 0, fence); 288 } 289 290 static HRESULT wined3d_fence_init(struct wined3d_fence *fence, const struct wined3d_gl_info *gl_info) 291 { 292 if (!wined3d_fence_supported(gl_info)) 293 { 294 WARN("Fences not supported.\n"); 295 return WINED3DERR_NOTAVAILABLE; 296 } 297 298 return WINED3D_OK; 299 } 300 301 HRESULT wined3d_fence_create(struct wined3d_device *device, struct wined3d_fence **fence) 302 { 303 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 304 struct wined3d_fence *object; 305 HRESULT hr; 306 307 TRACE("device %p, fence %p.\n", device, fence); 308 309 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 310 return E_OUTOFMEMORY; 311 312 if (FAILED(hr = wined3d_fence_init(object, gl_info))) 313 { 314 HeapFree(GetProcessHeap(), 0, object); 315 return hr; 316 } 317 318 TRACE("Created fence %p.\n", object); 319 *fence = object; 320 321 return WINED3D_OK; 322 } 323 324 ULONG CDECL wined3d_query_incref(struct wined3d_query *query) 325 { 326 ULONG refcount = InterlockedIncrement(&query->ref); 327 328 TRACE("%p increasing refcount to %u.\n", query, refcount); 329 330 return refcount; 331 } 332 333 static void wined3d_query_destroy_object(void *object) 334 { 335 struct wined3d_query *query = object; 336 337 if (!list_empty(&query->poll_list_entry)) 338 list_remove(&query->poll_list_entry); 339 340 /* Queries are specific to the GL context that created them. Not 341 * deleting the query will obviously leak it, but that's still better 342 * than potentially deleting a different query with the same id in this 343 * context, and (still) leaking the actual query. */ 344 query->query_ops->query_destroy(query); 345 } 346 347 ULONG CDECL wined3d_query_decref(struct wined3d_query *query) 348 { 349 ULONG refcount = InterlockedDecrement(&query->ref); 350 351 TRACE("%p decreasing refcount to %u.\n", query, refcount); 352 353 if (!refcount) 354 { 355 query->parent_ops->wined3d_object_destroyed(query->parent); 356 wined3d_cs_destroy_object(query->device->cs, wined3d_query_destroy_object, query); 357 } 358 359 return refcount; 360 } 361 362 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query, 363 void *data, UINT data_size, DWORD flags) 364 { 365 TRACE("query %p, data %p, data_size %u, flags %#x.\n", 366 query, data, data_size, flags); 367 368 if (flags) 369 WARN("Ignoring flags %#x.\n", flags); 370 371 if (query->state == QUERY_BUILDING) 372 { 373 WARN("Query is building, returning S_FALSE.\n"); 374 return S_FALSE; 375 } 376 377 if (query->state == QUERY_CREATED) 378 { 379 WARN("Query wasn't started yet.\n"); 380 return WINED3DERR_INVALIDCALL; 381 } 382 383 if (!query->device->cs->thread) 384 { 385 if (!query->query_ops->query_poll(query, flags)) 386 return S_FALSE; 387 } 388 else if (query->counter_main != query->counter_retrieved) 389 { 390 if (flags & WINED3DGETDATA_FLUSH && !query->device->cs->queries_flushed) 391 wined3d_cs_emit_flush(query->device->cs); 392 return S_FALSE; 393 } 394 395 if (data) 396 memcpy(data, query->data, min(data_size, query->data_size)); 397 398 return S_OK; 399 } 400 401 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query) 402 { 403 TRACE("query %p.\n", query); 404 405 return query->data_size; 406 } 407 408 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags) 409 { 410 TRACE("query %p, flags %#x.\n", query, flags); 411 412 if (flags & WINED3DISSUE_END) 413 ++query->counter_main; 414 415 wined3d_cs_emit_query_issue(query->device->cs, query, flags); 416 417 if (flags & WINED3DISSUE_BEGIN) 418 query->state = QUERY_BUILDING; 419 else 420 query->state = QUERY_SIGNALLED; 421 422 return WINED3D_OK; 423 } 424 425 static BOOL wined3d_occlusion_query_ops_poll(struct wined3d_query *query, DWORD flags) 426 { 427 struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); 428 struct wined3d_device *device = query->device; 429 const struct wined3d_gl_info *gl_info; 430 struct wined3d_context *context; 431 GLuint available; 432 433 TRACE("query %p, flags %#x.\n", query, flags); 434 435 if (!(context = context_reacquire(device, oq->context))) 436 { 437 FIXME("%p Wrong thread, returning 1.\n", query); 438 oq->samples = 1; 439 return TRUE; 440 } 441 gl_info = context->gl_info; 442 443 GL_EXTCALL(glGetQueryObjectuiv(oq->id, GL_QUERY_RESULT_AVAILABLE, &available)); 444 TRACE("Available %#x.\n", available); 445 446 if (available) 447 { 448 oq->samples = get_query_result64(oq->id, gl_info); 449 TRACE("Returning 0x%s samples.\n", wine_dbgstr_longlong(oq->samples)); 450 } 451 452 checkGLcall("poll occlusion query"); 453 context_release(context); 454 455 return available; 456 } 457 458 static BOOL wined3d_event_query_ops_poll(struct wined3d_query *query, DWORD flags) 459 { 460 struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); 461 enum wined3d_fence_result ret; 462 463 TRACE("query %p, flags %#x.\n", query, flags); 464 465 ret = wined3d_fence_test(&event_query->fence, query->device, flags); 466 switch (ret) 467 { 468 case WINED3D_FENCE_OK: 469 case WINED3D_FENCE_NOT_STARTED: 470 return event_query->signalled = TRUE; 471 472 case WINED3D_FENCE_WAITING: 473 return event_query->signalled = FALSE; 474 475 case WINED3D_FENCE_WRONG_THREAD: 476 FIXME("(%p) Wrong thread, reporting GPU idle.\n", query); 477 return event_query->signalled = TRUE; 478 479 case WINED3D_FENCE_ERROR: 480 ERR("The GL event query failed.\n"); 481 return event_query->signalled = TRUE; 482 483 default: 484 ERR("Unexpected wined3d_event_query_test result %#x.\n", ret); 485 return event_query->signalled = TRUE; 486 } 487 } 488 489 void * CDECL wined3d_query_get_parent(const struct wined3d_query *query) 490 { 491 TRACE("query %p.\n", query); 492 493 return query->parent; 494 } 495 496 enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query *query) 497 { 498 TRACE("query %p.\n", query); 499 500 return query->type; 501 } 502 503 static BOOL wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags) 504 { 505 TRACE("query %p, flags %#x.\n", query, flags); 506 507 if (flags & WINED3DISSUE_END) 508 { 509 struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); 510 511 wined3d_fence_issue(&event_query->fence, query->device); 512 return TRUE; 513 } 514 else if (flags & WINED3DISSUE_BEGIN) 515 { 516 /* Started implicitly at query creation. */ 517 ERR("Event query issued with START flag - what to do?\n"); 518 } 519 520 return FALSE; 521 } 522 523 static BOOL wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags) 524 { 525 struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); 526 struct wined3d_device *device = query->device; 527 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 528 struct wined3d_context *context; 529 BOOL poll = FALSE; 530 531 TRACE("query %p, flags %#x.\n", query, flags); 532 533 /* This is allowed according to MSDN and our tests. Reset the query and 534 * restart. */ 535 if (flags & WINED3DISSUE_BEGIN) 536 { 537 if (oq->started) 538 { 539 if ((context = context_reacquire(device, oq->context))) 540 { 541 GL_EXTCALL(glEndQuery(GL_SAMPLES_PASSED)); 542 checkGLcall("glEndQuery()"); 543 } 544 else 545 { 546 FIXME("Wrong thread, can't restart query.\n"); 547 context_free_occlusion_query(oq); 548 context = context_acquire(device, NULL, 0); 549 context_alloc_occlusion_query(context, oq); 550 } 551 } 552 else 553 { 554 if (oq->context) 555 context_free_occlusion_query(oq); 556 context = context_acquire(device, NULL, 0); 557 context_alloc_occlusion_query(context, oq); 558 } 559 560 GL_EXTCALL(glBeginQuery(GL_SAMPLES_PASSED, oq->id)); 561 checkGLcall("glBeginQuery()"); 562 563 context_release(context); 564 oq->started = TRUE; 565 } 566 if (flags & WINED3DISSUE_END) 567 { 568 /* MSDN says END on a non-building occlusion query returns an error, 569 * but our tests show that it returns OK. But OpenGL doesn't like it, 570 * so avoid generating an error. */ 571 if (oq->started) 572 { 573 if ((context = context_reacquire(device, oq->context))) 574 { 575 GL_EXTCALL(glEndQuery(GL_SAMPLES_PASSED)); 576 checkGLcall("glEndQuery()"); 577 578 context_release(context); 579 poll = TRUE; 580 } 581 else 582 { 583 FIXME("Wrong thread, can't end query.\n"); 584 } 585 } 586 oq->started = FALSE; 587 } 588 589 return poll; 590 } 591 592 static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD flags) 593 { 594 struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); 595 struct wined3d_device *device = query->device; 596 const struct wined3d_gl_info *gl_info; 597 struct wined3d_context *context; 598 GLuint64 timestamp; 599 GLuint available; 600 601 TRACE("query %p, flags %#x.\n", query, flags); 602 603 if (!(context = context_reacquire(device, tq->context))) 604 { 605 FIXME("%p Wrong thread, returning 1.\n", query); 606 tq->timestamp = 1; 607 return TRUE; 608 } 609 gl_info = context->gl_info; 610 611 GL_EXTCALL(glGetQueryObjectuiv(tq->id, GL_QUERY_RESULT_AVAILABLE, &available)); 612 checkGLcall("glGetQueryObjectuiv(GL_QUERY_RESULT_AVAILABLE)"); 613 TRACE("available %#x.\n", available); 614 615 if (available) 616 { 617 GL_EXTCALL(glGetQueryObjectui64v(tq->id, GL_QUERY_RESULT, ×tamp)); 618 checkGLcall("glGetQueryObjectui64v(GL_QUERY_RESULT)"); 619 TRACE("Returning timestamp %s.\n", wine_dbgstr_longlong(timestamp)); 620 tq->timestamp = timestamp; 621 } 622 623 context_release(context); 624 625 return available; 626 } 627 628 static BOOL wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD flags) 629 { 630 struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); 631 const struct wined3d_gl_info *gl_info; 632 struct wined3d_context *context; 633 634 TRACE("query %p, flags %#x.\n", query, flags); 635 636 if (flags & WINED3DISSUE_BEGIN) 637 { 638 WARN("Ignoring WINED3DISSUE_BEGIN with a TIMESTAMP query.\n"); 639 } 640 if (flags & WINED3DISSUE_END) 641 { 642 if (tq->context) 643 context_free_timestamp_query(tq); 644 context = context_acquire(query->device, NULL, 0); 645 gl_info = context->gl_info; 646 context_alloc_timestamp_query(context, tq); 647 GL_EXTCALL(glQueryCounter(tq->id, GL_TIMESTAMP)); 648 checkGLcall("glQueryCounter()"); 649 context_release(context); 650 651 return TRUE; 652 } 653 654 return FALSE; 655 } 656 657 static BOOL wined3d_timestamp_disjoint_query_ops_poll(struct wined3d_query *query, DWORD flags) 658 { 659 TRACE("query %p, flags %#x.\n", query, flags); 660 661 return TRUE; 662 } 663 664 static BOOL wined3d_timestamp_disjoint_query_ops_issue(struct wined3d_query *query, DWORD flags) 665 { 666 TRACE("query %p, flags %#x.\n", query, flags); 667 668 return FALSE; 669 } 670 671 static BOOL wined3d_so_statistics_query_ops_poll(struct wined3d_query *query, DWORD flags) 672 { 673 struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); 674 struct wined3d_device *device = query->device; 675 GLuint written_available, generated_available; 676 const struct wined3d_gl_info *gl_info; 677 struct wined3d_context *context; 678 679 TRACE("query %p, flags %#x.\n", query, flags); 680 681 if (!(context = context_reacquire(device, pq->context))) 682 { 683 FIXME("%p Wrong thread, returning 0 primitives.\n", query); 684 memset(&pq->statistics, 0, sizeof(pq->statistics)); 685 return TRUE; 686 } 687 gl_info = context->gl_info; 688 689 GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.written, 690 GL_QUERY_RESULT_AVAILABLE, &written_available)); 691 GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.generated, 692 GL_QUERY_RESULT_AVAILABLE, &generated_available)); 693 TRACE("Available %#x, %#x.\n", written_available, generated_available); 694 695 if (written_available && generated_available) 696 { 697 pq->statistics.primitives_written = get_query_result64(pq->u.query.written, gl_info); 698 pq->statistics.primitives_generated = get_query_result64(pq->u.query.generated, gl_info); 699 TRACE("Returning %s, %s primitives.\n", 700 wine_dbgstr_longlong(pq->statistics.primitives_written), 701 wine_dbgstr_longlong(pq->statistics.primitives_generated)); 702 } 703 704 checkGLcall("poll SO statistics query"); 705 context_release(context); 706 707 return written_available && generated_available; 708 } 709 710 static BOOL wined3d_so_statistics_query_ops_issue(struct wined3d_query *query, DWORD flags) 711 { 712 struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); 713 struct wined3d_device *device = query->device; 714 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 715 struct wined3d_context *context; 716 BOOL poll = FALSE; 717 718 TRACE("query %p, flags %#x.\n", query, flags); 719 720 if (flags & WINED3DISSUE_BEGIN) 721 { 722 if (pq->started) 723 { 724 if ((context = context_reacquire(device, pq->context))) 725 { 726 GL_EXTCALL(glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, pq->stream_idx)); 727 GL_EXTCALL(glEndQueryIndexed(GL_PRIMITIVES_GENERATED, pq->stream_idx)); 728 } 729 else 730 { 731 FIXME("Wrong thread, can't restart query.\n"); 732 context_free_so_statistics_query(pq); 733 context = context_acquire(device, NULL, 0); 734 context_alloc_so_statistics_query(context, pq); 735 } 736 } 737 else 738 { 739 if (pq->context) 740 context_free_so_statistics_query(pq); 741 context = context_acquire(device, NULL, 0); 742 context_alloc_so_statistics_query(context, pq); 743 } 744 745 GL_EXTCALL(glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 746 pq->stream_idx, pq->u.query.written)); 747 GL_EXTCALL(glBeginQueryIndexed(GL_PRIMITIVES_GENERATED, 748 pq->stream_idx, pq->u.query.generated)); 749 checkGLcall("begin query"); 750 751 context_release(context); 752 pq->started = TRUE; 753 } 754 if (flags & WINED3DISSUE_END) 755 { 756 if (pq->started) 757 { 758 if ((context = context_reacquire(device, pq->context))) 759 { 760 GL_EXTCALL(glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, pq->stream_idx)); 761 GL_EXTCALL(glEndQueryIndexed(GL_PRIMITIVES_GENERATED, pq->stream_idx)); 762 checkGLcall("end query"); 763 764 context_release(context); 765 poll = TRUE; 766 } 767 else 768 { 769 FIXME("Wrong thread, can't end query.\n"); 770 } 771 } 772 pq->started = FALSE; 773 } 774 775 return poll; 776 } 777 778 static BOOL wined3d_pipeline_query_ops_poll(struct wined3d_query *query, DWORD flags) 779 { 780 struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query); 781 struct wined3d_device *device = query->device; 782 const struct wined3d_gl_info *gl_info; 783 struct wined3d_context *context; 784 GLuint available; 785 int i; 786 787 TRACE("query %p, flags %#x.\n", query, flags); 788 789 if (!(context = context_reacquire(device, pq->context))) 790 { 791 FIXME("%p Wrong thread.\n", query); 792 memset(&pq->statistics, 0, sizeof(pq->statistics)); 793 return TRUE; 794 } 795 gl_info = context->gl_info; 796 797 for (i = 0; i < ARRAY_SIZE(pq->u.id); ++i) 798 { 799 GL_EXTCALL(glGetQueryObjectuiv(pq->u.id[i], GL_QUERY_RESULT_AVAILABLE, &available)); 800 if (!available) 801 break; 802 } 803 804 if (available) 805 { 806 pq->statistics.vertices_submitted = get_query_result64(pq->u.query.vertices, gl_info); 807 pq->statistics.primitives_submitted = get_query_result64(pq->u.query.primitives, gl_info); 808 pq->statistics.vs_invocations = get_query_result64(pq->u.query.vertex_shader, gl_info); 809 pq->statistics.hs_invocations = get_query_result64(pq->u.query.tess_control_shader, gl_info); 810 pq->statistics.ds_invocations = get_query_result64(pq->u.query.tess_eval_shader, gl_info); 811 pq->statistics.gs_invocations = get_query_result64(pq->u.query.geometry_shader, gl_info); 812 pq->statistics.gs_primitives = get_query_result64(pq->u.query.geometry_primitives, gl_info); 813 pq->statistics.ps_invocations = get_query_result64(pq->u.query.fragment_shader, gl_info); 814 pq->statistics.cs_invocations = get_query_result64(pq->u.query.compute_shader, gl_info); 815 pq->statistics.clipping_input_primitives = get_query_result64(pq->u.query.clipping_input, gl_info); 816 pq->statistics.clipping_output_primitives = get_query_result64(pq->u.query.clipping_output, gl_info); 817 } 818 819 checkGLcall("poll pipeline statistics query"); 820 context_release(context); 821 return available; 822 } 823 824 static void wined3d_pipeline_statistics_query_end(struct wined3d_pipeline_statistics_query *query, 825 struct wined3d_context *context) 826 { 827 const struct wined3d_gl_info *gl_info = context->gl_info; 828 829 GL_EXTCALL(glEndQuery(GL_VERTICES_SUBMITTED_ARB)); 830 GL_EXTCALL(glEndQuery(GL_PRIMITIVES_SUBMITTED_ARB)); 831 GL_EXTCALL(glEndQuery(GL_VERTEX_SHADER_INVOCATIONS_ARB)); 832 GL_EXTCALL(glEndQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB)); 833 GL_EXTCALL(glEndQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB)); 834 GL_EXTCALL(glEndQuery(GL_GEOMETRY_SHADER_INVOCATIONS)); 835 GL_EXTCALL(glEndQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB)); 836 GL_EXTCALL(glEndQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB)); 837 GL_EXTCALL(glEndQuery(GL_COMPUTE_SHADER_INVOCATIONS_ARB)); 838 GL_EXTCALL(glEndQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB)); 839 GL_EXTCALL(glEndQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB)); 840 checkGLcall("end query"); 841 } 842 843 static BOOL wined3d_pipeline_query_ops_issue(struct wined3d_query *query, DWORD flags) 844 { 845 struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query); 846 struct wined3d_device *device = query->device; 847 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 848 struct wined3d_context *context; 849 BOOL poll = FALSE; 850 851 TRACE("query %p, flags %#x.\n", query, flags); 852 853 if (flags & WINED3DISSUE_BEGIN) 854 { 855 if (pq->started) 856 { 857 if ((context = context_reacquire(device, pq->context))) 858 { 859 wined3d_pipeline_statistics_query_end(pq, context); 860 } 861 else 862 { 863 FIXME("Wrong thread, can't restart query.\n"); 864 context_free_pipeline_statistics_query(pq); 865 context = context_acquire(device, NULL, 0); 866 context_alloc_pipeline_statistics_query(context, pq); 867 } 868 } 869 else 870 { 871 if (pq->context) 872 context_free_pipeline_statistics_query(pq); 873 context = context_acquire(device, NULL, 0); 874 context_alloc_pipeline_statistics_query(context, pq); 875 } 876 877 GL_EXTCALL(glBeginQuery(GL_VERTICES_SUBMITTED_ARB, pq->u.query.vertices)); 878 GL_EXTCALL(glBeginQuery(GL_PRIMITIVES_SUBMITTED_ARB, pq->u.query.primitives)); 879 GL_EXTCALL(glBeginQuery(GL_VERTEX_SHADER_INVOCATIONS_ARB, pq->u.query.vertex_shader)); 880 GL_EXTCALL(glBeginQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB, pq->u.query.tess_control_shader)); 881 GL_EXTCALL(glBeginQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, pq->u.query.tess_eval_shader)); 882 GL_EXTCALL(glBeginQuery(GL_GEOMETRY_SHADER_INVOCATIONS, pq->u.query.geometry_shader)); 883 GL_EXTCALL(glBeginQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, pq->u.query.geometry_primitives)); 884 GL_EXTCALL(glBeginQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB, pq->u.query.fragment_shader)); 885 GL_EXTCALL(glBeginQuery(GL_COMPUTE_SHADER_INVOCATIONS_ARB, pq->u.query.compute_shader)); 886 GL_EXTCALL(glBeginQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB, pq->u.query.clipping_input)); 887 GL_EXTCALL(glBeginQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB, pq->u.query.clipping_output)); 888 checkGLcall("begin query"); 889 890 context_release(context); 891 pq->started = TRUE; 892 } 893 if (flags & WINED3DISSUE_END) 894 { 895 if (pq->started) 896 { 897 if ((context = context_reacquire(device, pq->context))) 898 { 899 wined3d_pipeline_statistics_query_end(pq, context); 900 context_release(context); 901 poll = TRUE; 902 } 903 else 904 { 905 FIXME("Wrong thread, can't end query.\n"); 906 } 907 } 908 pq->started = FALSE; 909 } 910 911 return poll; 912 } 913 914 static void wined3d_event_query_ops_destroy(struct wined3d_query *query) 915 { 916 struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); 917 918 wined3d_fence_free(&event_query->fence); 919 HeapFree(GetProcessHeap(), 0, event_query); 920 } 921 922 static const struct wined3d_query_ops event_query_ops = 923 { 924 wined3d_event_query_ops_poll, 925 wined3d_event_query_ops_issue, 926 wined3d_event_query_ops_destroy, 927 }; 928 929 static HRESULT wined3d_event_query_create(struct wined3d_device *device, 930 enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, 931 struct wined3d_query **query) 932 { 933 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 934 struct wined3d_event_query *object; 935 HRESULT hr; 936 937 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 938 device, type, parent, parent_ops, query); 939 940 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 941 return E_OUTOFMEMORY; 942 943 if (FAILED(hr = wined3d_fence_init(&object->fence, gl_info))) 944 { 945 WARN("Event queries not supported.\n"); 946 HeapFree(GetProcessHeap(), 0, object); 947 return hr; 948 } 949 950 wined3d_query_init(&object->query, device, type, &object->signalled, 951 sizeof(object->signalled), &event_query_ops, parent, parent_ops); 952 953 TRACE("Created query %p.\n", object); 954 *query = &object->query; 955 956 return WINED3D_OK; 957 } 958 959 static void wined3d_occlusion_query_ops_destroy(struct wined3d_query *query) 960 { 961 struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); 962 963 if (oq->context) 964 context_free_occlusion_query(oq); 965 HeapFree(GetProcessHeap(), 0, oq); 966 } 967 968 static const struct wined3d_query_ops occlusion_query_ops = 969 { 970 wined3d_occlusion_query_ops_poll, 971 wined3d_occlusion_query_ops_issue, 972 wined3d_occlusion_query_ops_destroy, 973 }; 974 975 static HRESULT wined3d_occlusion_query_create(struct wined3d_device *device, 976 enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, 977 struct wined3d_query **query) 978 { 979 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 980 struct wined3d_occlusion_query *object; 981 982 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 983 device, type, parent, parent_ops, query); 984 985 if (!gl_info->supported[ARB_OCCLUSION_QUERY]) 986 { 987 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n"); 988 return WINED3DERR_NOTAVAILABLE; 989 } 990 991 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 992 return E_OUTOFMEMORY; 993 994 wined3d_query_init(&object->query, device, type, &object->samples, 995 sizeof(object->samples), &occlusion_query_ops, parent, parent_ops); 996 997 TRACE("Created query %p.\n", object); 998 *query = &object->query; 999 1000 return WINED3D_OK; 1001 } 1002 1003 static void wined3d_timestamp_query_ops_destroy(struct wined3d_query *query) 1004 { 1005 struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); 1006 1007 if (tq->context) 1008 context_free_timestamp_query(tq); 1009 HeapFree(GetProcessHeap(), 0, tq); 1010 } 1011 1012 static const struct wined3d_query_ops timestamp_query_ops = 1013 { 1014 wined3d_timestamp_query_ops_poll, 1015 wined3d_timestamp_query_ops_issue, 1016 wined3d_timestamp_query_ops_destroy, 1017 }; 1018 1019 static HRESULT wined3d_timestamp_query_create(struct wined3d_device *device, 1020 enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, 1021 struct wined3d_query **query) 1022 { 1023 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 1024 struct wined3d_timestamp_query *object; 1025 1026 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 1027 device, type, parent, parent_ops, query); 1028 1029 if (!gl_info->supported[ARB_TIMER_QUERY]) 1030 { 1031 WARN("Unsupported in local OpenGL implementation: ARB_TIMER_QUERY.\n"); 1032 return WINED3DERR_NOTAVAILABLE; 1033 } 1034 1035 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 1036 return E_OUTOFMEMORY; 1037 1038 wined3d_query_init(&object->query, device, type, &object->timestamp, 1039 sizeof(object->timestamp), ×tamp_query_ops, parent, parent_ops); 1040 1041 TRACE("Created query %p.\n", object); 1042 *query = &object->query; 1043 1044 return WINED3D_OK; 1045 } 1046 1047 static void wined3d_timestamp_disjoint_query_ops_destroy(struct wined3d_query *query) 1048 { 1049 HeapFree(GetProcessHeap(), 0, query); 1050 } 1051 1052 static const struct wined3d_query_ops timestamp_disjoint_query_ops = 1053 { 1054 wined3d_timestamp_disjoint_query_ops_poll, 1055 wined3d_timestamp_disjoint_query_ops_issue, 1056 wined3d_timestamp_disjoint_query_ops_destroy, 1057 }; 1058 1059 static HRESULT wined3d_timestamp_disjoint_query_create(struct wined3d_device *device, 1060 enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, 1061 struct wined3d_query **query) 1062 { 1063 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 1064 struct wined3d_query *object; 1065 1066 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 1067 device, type, parent, parent_ops, query); 1068 1069 if (!gl_info->supported[ARB_TIMER_QUERY]) 1070 { 1071 WARN("Unsupported in local OpenGL implementation: ARB_TIMER_QUERY.\n"); 1072 return WINED3DERR_NOTAVAILABLE; 1073 } 1074 1075 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 1076 return E_OUTOFMEMORY; 1077 1078 if (type == WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT) 1079 { 1080 static const struct wined3d_query_data_timestamp_disjoint disjoint_data = {1000 * 1000 * 1000, FALSE}; 1081 1082 wined3d_query_init(object, device, type, &disjoint_data, 1083 sizeof(disjoint_data), ×tamp_disjoint_query_ops, parent, parent_ops); 1084 } 1085 else 1086 { 1087 static const UINT64 freq = 1000 * 1000 * 1000; 1088 1089 wined3d_query_init(object, device, type, &freq, 1090 sizeof(freq), ×tamp_disjoint_query_ops, parent, parent_ops); 1091 } 1092 1093 TRACE("Created query %p.\n", object); 1094 *query = object; 1095 1096 return WINED3D_OK; 1097 } 1098 1099 static void wined3d_so_statistics_query_ops_destroy(struct wined3d_query *query) 1100 { 1101 struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); 1102 1103 if (pq->context) 1104 context_free_so_statistics_query(pq); 1105 HeapFree(GetProcessHeap(), 0, pq); 1106 } 1107 1108 static const struct wined3d_query_ops so_statistics_query_ops = 1109 { 1110 wined3d_so_statistics_query_ops_poll, 1111 wined3d_so_statistics_query_ops_issue, 1112 wined3d_so_statistics_query_ops_destroy, 1113 }; 1114 1115 static HRESULT wined3d_so_statistics_query_create(struct wined3d_device *device, 1116 enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, 1117 struct wined3d_query **query) 1118 { 1119 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 1120 struct wined3d_so_statistics_query *object; 1121 unsigned int stream_idx; 1122 1123 if (WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0 <= type && type <= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3) 1124 stream_idx = type - WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0; 1125 else 1126 return WINED3DERR_NOTAVAILABLE; 1127 1128 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 1129 device, type, parent, parent_ops, query); 1130 1131 if (!gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY]) 1132 { 1133 WARN("OpenGL implementation does not support primitive queries.\n"); 1134 return WINED3DERR_NOTAVAILABLE; 1135 } 1136 if (!gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) 1137 { 1138 WARN("OpenGL implementation does not support indexed queries.\n"); 1139 return WINED3DERR_NOTAVAILABLE; 1140 } 1141 1142 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 1143 return E_OUTOFMEMORY; 1144 1145 wined3d_query_init(&object->query, device, type, &object->statistics, 1146 sizeof(object->statistics), &so_statistics_query_ops, parent, parent_ops); 1147 object->stream_idx = stream_idx; 1148 1149 TRACE("Created query %p.\n", object); 1150 *query = &object->query; 1151 1152 return WINED3D_OK; 1153 } 1154 1155 static void wined3d_pipeline_query_ops_destroy(struct wined3d_query *query) 1156 { 1157 struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query); 1158 if (pq->context) 1159 context_free_pipeline_statistics_query(pq); 1160 HeapFree(GetProcessHeap(), 0, pq); 1161 } 1162 1163 static const struct wined3d_query_ops pipeline_query_ops = 1164 { 1165 wined3d_pipeline_query_ops_poll, 1166 wined3d_pipeline_query_ops_issue, 1167 wined3d_pipeline_query_ops_destroy, 1168 }; 1169 1170 static HRESULT wined3d_pipeline_query_create(struct wined3d_device *device, 1171 enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, 1172 struct wined3d_query **query) 1173 { 1174 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 1175 struct wined3d_pipeline_statistics_query *object; 1176 1177 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 1178 device, type, parent, parent_ops, query); 1179 1180 if (!gl_info->supported[ARB_PIPELINE_STATISTICS_QUERY]) 1181 { 1182 WARN("OpenGL implementation does not support pipeline statistics queries.\n"); 1183 return WINED3DERR_NOTAVAILABLE; 1184 } 1185 1186 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 1187 return E_OUTOFMEMORY; 1188 1189 wined3d_query_init(&object->query, device, type, &object->statistics, 1190 sizeof(object->statistics), &pipeline_query_ops, parent, parent_ops); 1191 1192 TRACE("Created query %p.\n", object); 1193 *query = &object->query; 1194 1195 return WINED3D_OK; 1196 } 1197 1198 HRESULT CDECL wined3d_query_create(struct wined3d_device *device, enum wined3d_query_type type, 1199 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) 1200 { 1201 TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", 1202 device, type, parent, parent_ops, query); 1203 1204 switch (type) 1205 { 1206 case WINED3D_QUERY_TYPE_EVENT: 1207 return wined3d_event_query_create(device, type, parent, parent_ops, query); 1208 1209 case WINED3D_QUERY_TYPE_OCCLUSION: 1210 return wined3d_occlusion_query_create(device, type, parent, parent_ops, query); 1211 1212 case WINED3D_QUERY_TYPE_TIMESTAMP: 1213 return wined3d_timestamp_query_create(device, type, parent, parent_ops, query); 1214 1215 case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT: 1216 case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ: 1217 return wined3d_timestamp_disjoint_query_create(device, type, parent, parent_ops, query); 1218 1219 case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: 1220 case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: 1221 case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: 1222 case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: 1223 return wined3d_so_statistics_query_create(device, type, parent, parent_ops, query); 1224 1225 case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS: 1226 return wined3d_pipeline_query_create(device, type, parent, parent_ops, query); 1227 1228 default: 1229 FIXME("Unhandled query type %#x.\n", type); 1230 return WINED3DERR_NOTAVAILABLE; 1231 } 1232 } 1233