xref: /reactos/dll/directx/wine/wined3d/query.c (revision c2c66aff)
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)
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, &timestamp));
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 BOOL wined3d_statistics_query_ops_poll(struct wined3d_query *query, DWORD flags)
915 {
916     TRACE("query %p, flags %#x.\n", query, flags);
917 
918     return TRUE;
919 }
920 
921 static BOOL wined3d_statistics_query_ops_issue(struct wined3d_query *query, DWORD flags)
922 {
923     FIXME("query %p, flags %#x.\n", query, flags);
924 
925     return FALSE;
926 }
927 
928 static BOOL wined3d_overflow_query_ops_poll(struct wined3d_query *query, DWORD flags)
929 {
930     TRACE("query %p, flags %#x.\n", query, flags);
931 
932     return TRUE;
933 }
934 
935 static BOOL wined3d_overflow_query_ops_issue(struct wined3d_query *query, DWORD flags)
936 {
937     FIXME("query %p, flags %#x.\n", query, flags);
938 
939     return FALSE;
940 }
941 
942 static void wined3d_event_query_ops_destroy(struct wined3d_query *query)
943 {
944     struct wined3d_event_query *event_query = wined3d_event_query_from_query(query);
945 
946     wined3d_fence_free(&event_query->fence);
947     HeapFree(GetProcessHeap(), 0, event_query);
948 }
949 
950 static const struct wined3d_query_ops event_query_ops =
951 {
952     wined3d_event_query_ops_poll,
953     wined3d_event_query_ops_issue,
954     wined3d_event_query_ops_destroy,
955 };
956 
957 static HRESULT wined3d_event_query_create(struct wined3d_device *device,
958         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
959         struct wined3d_query **query)
960 {
961     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
962     struct wined3d_event_query *object;
963     HRESULT hr;
964 
965     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
966             device, type, parent, parent_ops, query);
967 
968     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
969         return E_OUTOFMEMORY;
970 
971     if (FAILED(hr = wined3d_fence_init(&object->fence, gl_info)))
972     {
973         WARN("Event queries not supported.\n");
974         HeapFree(GetProcessHeap(), 0, object);
975         return hr;
976     }
977 
978     wined3d_query_init(&object->query, device, type, &object->signalled,
979             sizeof(object->signalled), &event_query_ops, parent, parent_ops);
980 
981     TRACE("Created query %p.\n", object);
982     *query = &object->query;
983 
984     return WINED3D_OK;
985 }
986 
987 static void wined3d_occlusion_query_ops_destroy(struct wined3d_query *query)
988 {
989     struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query);
990 
991     if (oq->context)
992         context_free_occlusion_query(oq);
993     HeapFree(GetProcessHeap(), 0, oq);
994 }
995 
996 static const struct wined3d_query_ops occlusion_query_ops =
997 {
998     wined3d_occlusion_query_ops_poll,
999     wined3d_occlusion_query_ops_issue,
1000     wined3d_occlusion_query_ops_destroy,
1001 };
1002 
1003 static HRESULT wined3d_occlusion_query_create(struct wined3d_device *device,
1004         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1005         struct wined3d_query **query)
1006 {
1007     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1008     struct wined3d_occlusion_query *object;
1009 
1010     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
1011             device, type, parent, parent_ops, query);
1012 
1013     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
1014     {
1015         WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
1016         return WINED3DERR_NOTAVAILABLE;
1017     }
1018 
1019     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1020         return E_OUTOFMEMORY;
1021 
1022     wined3d_query_init(&object->query, device, type, &object->samples,
1023             sizeof(object->samples), &occlusion_query_ops, parent, parent_ops);
1024 
1025     TRACE("Created query %p.\n", object);
1026     *query = &object->query;
1027 
1028     return WINED3D_OK;
1029 }
1030 
1031 static void wined3d_timestamp_query_ops_destroy(struct wined3d_query *query)
1032 {
1033     struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query);
1034 
1035     if (tq->context)
1036         context_free_timestamp_query(tq);
1037     HeapFree(GetProcessHeap(), 0, tq);
1038 }
1039 
1040 static const struct wined3d_query_ops timestamp_query_ops =
1041 {
1042     wined3d_timestamp_query_ops_poll,
1043     wined3d_timestamp_query_ops_issue,
1044     wined3d_timestamp_query_ops_destroy,
1045 };
1046 
1047 static HRESULT wined3d_timestamp_query_create(struct wined3d_device *device,
1048         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1049         struct wined3d_query **query)
1050 {
1051     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1052     struct wined3d_timestamp_query *object;
1053 
1054     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
1055             device, type, parent, parent_ops, query);
1056 
1057     if (!gl_info->supported[ARB_TIMER_QUERY])
1058     {
1059         WARN("Unsupported in local OpenGL implementation: ARB_TIMER_QUERY.\n");
1060         return WINED3DERR_NOTAVAILABLE;
1061     }
1062 
1063     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1064         return E_OUTOFMEMORY;
1065 
1066     wined3d_query_init(&object->query, device, type, &object->timestamp,
1067             sizeof(object->timestamp), &timestamp_query_ops, parent, parent_ops);
1068 
1069     TRACE("Created query %p.\n", object);
1070     *query = &object->query;
1071 
1072     return WINED3D_OK;
1073 }
1074 
1075 static void wined3d_timestamp_disjoint_query_ops_destroy(struct wined3d_query *query)
1076 {
1077     HeapFree(GetProcessHeap(), 0, query);
1078 }
1079 
1080 static const struct wined3d_query_ops timestamp_disjoint_query_ops =
1081 {
1082     wined3d_timestamp_disjoint_query_ops_poll,
1083     wined3d_timestamp_disjoint_query_ops_issue,
1084     wined3d_timestamp_disjoint_query_ops_destroy,
1085 };
1086 
1087 static HRESULT wined3d_timestamp_disjoint_query_create(struct wined3d_device *device,
1088         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1089         struct wined3d_query **query)
1090 {
1091     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1092     struct wined3d_query *object;
1093 
1094     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
1095             device, type, parent, parent_ops, query);
1096 
1097     if (!gl_info->supported[ARB_TIMER_QUERY])
1098     {
1099         WARN("Unsupported in local OpenGL implementation: ARB_TIMER_QUERY.\n");
1100         return WINED3DERR_NOTAVAILABLE;
1101     }
1102 
1103     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1104         return E_OUTOFMEMORY;
1105 
1106     if (type == WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT)
1107     {
1108         static const struct wined3d_query_data_timestamp_disjoint disjoint_data = {1000 * 1000 * 1000, FALSE};
1109 
1110         wined3d_query_init(object, device, type, &disjoint_data,
1111                 sizeof(disjoint_data), &timestamp_disjoint_query_ops, parent, parent_ops);
1112     }
1113     else
1114     {
1115         static const UINT64 freq = 1000 * 1000 * 1000;
1116 
1117         wined3d_query_init(object, device, type, &freq,
1118                 sizeof(freq), &timestamp_disjoint_query_ops, parent, parent_ops);
1119     }
1120 
1121     TRACE("Created query %p.\n", object);
1122     *query = object;
1123 
1124     return WINED3D_OK;
1125 }
1126 
1127 static void wined3d_so_statistics_query_ops_destroy(struct wined3d_query *query)
1128 {
1129     struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query);
1130 
1131     if (pq->context)
1132         context_free_so_statistics_query(pq);
1133     HeapFree(GetProcessHeap(), 0, pq);
1134 }
1135 
1136 static const struct wined3d_query_ops so_statistics_query_ops =
1137 {
1138     wined3d_so_statistics_query_ops_poll,
1139     wined3d_so_statistics_query_ops_issue,
1140     wined3d_so_statistics_query_ops_destroy,
1141 };
1142 
1143 static HRESULT wined3d_so_statistics_query_create(struct wined3d_device *device,
1144         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1145         struct wined3d_query **query)
1146 {
1147     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1148     struct wined3d_so_statistics_query *object;
1149     unsigned int stream_idx;
1150 
1151     if (WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0 <= type && type <= WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3)
1152         stream_idx = type - WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0;
1153     else
1154         return WINED3DERR_NOTAVAILABLE;
1155 
1156     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
1157             device, type, parent, parent_ops, query);
1158 
1159     if (!gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY])
1160     {
1161         WARN("OpenGL implementation does not support primitive queries.\n");
1162         return WINED3DERR_NOTAVAILABLE;
1163     }
1164     if (!gl_info->supported[ARB_TRANSFORM_FEEDBACK3])
1165     {
1166         WARN("OpenGL implementation does not support indexed queries.\n");
1167         return WINED3DERR_NOTAVAILABLE;
1168     }
1169 
1170     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1171         return E_OUTOFMEMORY;
1172 
1173     wined3d_query_init(&object->query, device, type, &object->statistics,
1174             sizeof(object->statistics), &so_statistics_query_ops, parent, parent_ops);
1175     object->stream_idx = stream_idx;
1176 
1177     TRACE("Created query %p.\n", object);
1178     *query = &object->query;
1179 
1180     return WINED3D_OK;
1181 }
1182 
1183 static void wined3d_pipeline_query_ops_destroy(struct wined3d_query *query)
1184 {
1185     struct wined3d_pipeline_statistics_query *pq = wined3d_pipeline_statistics_query_from_query(query);
1186     if (pq->context)
1187         context_free_pipeline_statistics_query(pq);
1188     HeapFree(GetProcessHeap(), 0, pq);
1189 }
1190 
1191 static const struct wined3d_query_ops pipeline_query_ops =
1192 {
1193     wined3d_pipeline_query_ops_poll,
1194     wined3d_pipeline_query_ops_issue,
1195     wined3d_pipeline_query_ops_destroy,
1196 };
1197 
1198 static HRESULT wined3d_pipeline_query_create(struct wined3d_device *device,
1199         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1200         struct wined3d_query **query)
1201 {
1202     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1203     struct wined3d_pipeline_statistics_query *object;
1204 
1205     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
1206             device, type, parent, parent_ops, query);
1207 
1208     if (!gl_info->supported[ARB_PIPELINE_STATISTICS_QUERY])
1209     {
1210         WARN("OpenGL implementation does not support pipeline statistics queries.\n");
1211         return WINED3DERR_NOTAVAILABLE;
1212     }
1213 
1214     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1215         return E_OUTOFMEMORY;
1216 
1217     wined3d_query_init(&object->query, device, type, &object->statistics,
1218             sizeof(object->statistics), &pipeline_query_ops, parent, parent_ops);
1219 
1220     TRACE("Created query %p.\n", object);
1221     *query = &object->query;
1222 
1223     return WINED3D_OK;
1224 }
1225 
1226 static void wined3d_statistics_query_ops_destroy(struct wined3d_query *query)
1227 {
1228     HeapFree(GetProcessHeap(), 0, query);
1229 }
1230 
1231 static const struct wined3d_query_ops statistics_query_ops =
1232 {
1233     wined3d_statistics_query_ops_poll,
1234     wined3d_statistics_query_ops_issue,
1235     wined3d_statistics_query_ops_destroy,
1236 };
1237 
1238 static HRESULT wined3d_statistics_query_create(struct wined3d_device *device,
1239         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1240         struct wined3d_query **query)
1241 {
1242     static const struct wined3d_query_data_so_statistics statistics = { 1, 1 };
1243     struct wined3d_query *object;
1244 
1245     FIXME("device %p, type %#x, parent %p, query %p.\n", device, type, parent, query);
1246 
1247     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1248         return E_OUTOFMEMORY;
1249 
1250     wined3d_query_init(object, device, type, &statistics,
1251             sizeof(statistics), &statistics_query_ops, parent, parent_ops);
1252 
1253     TRACE("Created query %p.\n", object);
1254     *query = object;
1255 
1256     return WINED3D_OK;
1257 }
1258 
1259 static void wined3d_overflow_query_ops_destroy(struct wined3d_query *query)
1260 {
1261     HeapFree(GetProcessHeap(), 0, query);
1262 }
1263 
1264 static const struct wined3d_query_ops overflow_query_ops =
1265 {
1266     wined3d_overflow_query_ops_poll,
1267     wined3d_overflow_query_ops_issue,
1268     wined3d_overflow_query_ops_destroy,
1269 };
1270 
1271 static HRESULT wined3d_overflow_query_create(struct wined3d_device *device,
1272         enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops,
1273         struct wined3d_query **query)
1274 {
1275     static const BOOL overflow = FALSE;
1276     struct wined3d_query *object;
1277 
1278     FIXME("device %p, type %#x, parent %p, query %p.\n", device, type, parent, query);
1279 
1280     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1281         return E_OUTOFMEMORY;
1282 
1283     wined3d_query_init(object, device, type, &overflow,
1284             sizeof(overflow), &overflow_query_ops, parent, parent_ops);
1285 
1286     TRACE("Created query %p.\n", object);
1287     *query = object;
1288 
1289     return WINED3D_OK;
1290 }
1291 
1292 HRESULT CDECL wined3d_query_create(struct wined3d_device *device, enum wined3d_query_type type,
1293         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query)
1294 {
1295     TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n",
1296             device, type, parent, parent_ops, query);
1297 
1298     switch (type)
1299     {
1300         case WINED3D_QUERY_TYPE_EVENT:
1301             return wined3d_event_query_create(device, type, parent, parent_ops, query);
1302 
1303         case WINED3D_QUERY_TYPE_OCCLUSION:
1304             return wined3d_occlusion_query_create(device, type, parent, parent_ops, query);
1305 
1306         case WINED3D_QUERY_TYPE_TIMESTAMP:
1307             return wined3d_timestamp_query_create(device, type, parent, parent_ops, query);
1308 
1309         case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT:
1310         case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ:
1311             return wined3d_timestamp_disjoint_query_create(device, type, parent, parent_ops, query);
1312 
1313         case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0:
1314         case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1:
1315         case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2:
1316         case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3:
1317             return wined3d_so_statistics_query_create(device, type, parent, parent_ops, query);
1318 
1319         case WINED3D_QUERY_TYPE_PIPELINE_STATISTICS:
1320             return wined3d_pipeline_query_create(device, type, parent, parent_ops, query);
1321 
1322         case WINED3D_QUERY_TYPE_SO_STATISTICS:
1323             return wined3d_statistics_query_create(device, type, parent, parent_ops, query);
1324 
1325         case WINED3D_QUERY_TYPE_SO_OVERFLOW:
1326             return wined3d_overflow_query_create(device, type, parent, parent_ops, query);
1327 
1328         default:
1329             FIXME("Unhandled query type %#x.\n", type);
1330             return WINED3DERR_NOTAVAILABLE;
1331     }
1332 }
1333