2 * IWineD3DQuery implementation
4 * Copyright 2005 Oliver Stieber
5 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6 * Copyright 2009-2010 Henri Verbeet for CodeWeavers.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wined3d_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
29 BOOL wined3d_event_query_supported(const struct wined3d_gl_info *gl_info)
31 return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE];
34 void wined3d_event_query_destroy(struct wined3d_event_query *query)
36 if (query->context) context_free_event_query(query);
37 HeapFree(GetProcessHeap(), 0, query);
40 static enum wined3d_event_query_result wined3d_event_query_test(struct wined3d_event_query *query,
41 struct wined3d_device *device)
43 struct wined3d_context *context;
44 const struct wined3d_gl_info *gl_info;
45 enum wined3d_event_query_result ret;
48 TRACE("(%p) : device %p\n", query, device);
52 TRACE("Query not started\n");
53 return WINED3D_EVENT_QUERY_NOT_STARTED;
56 if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
58 WARN("Event query tested from wrong thread\n");
59 return WINED3D_EVENT_QUERY_WRONG_THREAD;
62 context = context_acquire(device, query->context->current_rt);
63 gl_info = context->gl_info;
67 if (gl_info->supported[ARB_SYNC])
69 GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, 0));
70 checkGLcall("glClientWaitSync");
74 case GL_ALREADY_SIGNALED:
75 case GL_CONDITION_SATISFIED:
76 ret = WINED3D_EVENT_QUERY_OK;
79 case GL_TIMEOUT_EXPIRED:
80 ret = WINED3D_EVENT_QUERY_WAITING;
85 ERR("glClientWaitSync returned %#x.\n", gl_ret);
86 ret = WINED3D_EVENT_QUERY_ERROR;
89 else if (gl_info->supported[APPLE_FENCE])
91 fence_result = GL_EXTCALL(glTestFenceAPPLE(query->object.id));
92 checkGLcall("glTestFenceAPPLE");
93 if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
94 else ret = WINED3D_EVENT_QUERY_WAITING;
96 else if (gl_info->supported[NV_FENCE])
98 fence_result = GL_EXTCALL(glTestFenceNV(query->object.id));
99 checkGLcall("glTestFenceNV");
100 if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
101 else ret = WINED3D_EVENT_QUERY_WAITING;
105 ERR("Event query created despite lack of GL support\n");
106 ret = WINED3D_EVENT_QUERY_ERROR;
111 context_release(context);
115 enum wined3d_event_query_result wined3d_event_query_finish(struct wined3d_event_query *query,
116 struct wined3d_device *device)
118 struct wined3d_context *context;
119 const struct wined3d_gl_info *gl_info;
120 enum wined3d_event_query_result ret;
122 TRACE("(%p)\n", query);
126 TRACE("Query not started\n");
127 return WINED3D_EVENT_QUERY_NOT_STARTED;
129 gl_info = query->context->gl_info;
131 if (query->context->tid != GetCurrentThreadId() && !gl_info->supported[ARB_SYNC])
133 /* A glFinish does not reliably wait for draws in other contexts. The caller has
134 * to find its own way to cope with the thread switch
136 WARN("Event query finished from wrong thread\n");
137 return WINED3D_EVENT_QUERY_WRONG_THREAD;
140 context = context_acquire(device, query->context->current_rt);
143 if (gl_info->supported[ARB_SYNC])
145 GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, ~(GLuint64)0));
146 checkGLcall("glClientWaitSync");
150 case GL_ALREADY_SIGNALED:
151 case GL_CONDITION_SATISFIED:
152 ret = WINED3D_EVENT_QUERY_OK;
155 /* We don't expect a timeout for a ~584 year wait */
157 ERR("glClientWaitSync returned %#x.\n", gl_ret);
158 ret = WINED3D_EVENT_QUERY_ERROR;
161 else if (context->gl_info->supported[APPLE_FENCE])
163 GL_EXTCALL(glFinishFenceAPPLE(query->object.id));
164 checkGLcall("glFinishFenceAPPLE");
165 ret = WINED3D_EVENT_QUERY_OK;
167 else if (context->gl_info->supported[NV_FENCE])
169 GL_EXTCALL(glFinishFenceNV(query->object.id));
170 checkGLcall("glFinishFenceNV");
171 ret = WINED3D_EVENT_QUERY_OK;
175 ERR("Event query created without GL support\n");
176 ret = WINED3D_EVENT_QUERY_ERROR;
180 context_release(context);
184 void wined3d_event_query_issue(struct wined3d_event_query *query, struct wined3d_device *device)
186 const struct wined3d_gl_info *gl_info;
187 struct wined3d_context *context;
191 if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
193 context_free_event_query(query);
194 context = context_acquire(device, NULL);
195 context_alloc_event_query(context, query);
199 context = context_acquire(device, query->context->current_rt);
204 context = context_acquire(device, NULL);
205 context_alloc_event_query(context, query);
208 gl_info = context->gl_info;
212 if (gl_info->supported[ARB_SYNC])
214 if (query->object.sync) GL_EXTCALL(glDeleteSync(query->object.sync));
215 checkGLcall("glDeleteSync");
216 query->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
217 checkGLcall("glFenceSync");
219 else if (gl_info->supported[APPLE_FENCE])
221 GL_EXTCALL(glSetFenceAPPLE(query->object.id));
222 checkGLcall("glSetFenceAPPLE");
224 else if (gl_info->supported[NV_FENCE])
226 GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
227 checkGLcall("glSetFenceNV");
232 context_release(context);
235 ULONG CDECL wined3d_query_incref(struct wined3d_query *query)
237 ULONG refcount = InterlockedIncrement(&query->ref);
239 TRACE("%p increasing refcount to %u.\n", query, refcount);
244 ULONG CDECL wined3d_query_decref(struct wined3d_query *query)
246 ULONG refcount = InterlockedDecrement(&query->ref);
248 TRACE("%p decreasing refcount to %u.\n", query, refcount);
252 /* Queries are specific to the GL context that created them. Not
253 * deleting the query will obviously leak it, but that's still better
254 * than potentially deleting a different query with the same id in this
255 * context, and (still) leaking the actual query. */
256 if (query->type == WINED3DQUERYTYPE_EVENT)
258 struct wined3d_event_query *event_query = query->extendedData;
259 if (event_query) wined3d_event_query_destroy(event_query);
261 else if (query->type == WINED3DQUERYTYPE_OCCLUSION)
263 struct wined3d_occlusion_query *oq = query->extendedData;
265 if (oq->context) context_free_occlusion_query(oq);
266 HeapFree(GetProcessHeap(), 0, query->extendedData);
269 HeapFree(GetProcessHeap(), 0, query);
275 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
276 void *data, UINT data_size, DWORD flags)
278 TRACE("query %p, data %p, data_size %u, flags %#x.\n",
279 query, data, data_size, flags);
281 return query->query_ops->query_get_data(query, data, data_size, flags);
284 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query)
286 TRACE("query %p.\n", query);
288 return query->data_size;
291 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
293 TRACE("query %p, flags %#x.\n", query, flags);
295 return query->query_ops->query_issue(query, flags);
298 static HRESULT wined3d_occlusion_query_ops_get_data(struct wined3d_query *query,
299 void *pData, DWORD dwSize, DWORD flags)
301 struct wined3d_occlusion_query *oq = query->extendedData;
302 struct wined3d_device *device = query->device;
303 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
304 struct wined3d_context *context;
310 TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
313 query->state = QUERY_CREATED;
315 if (query->state == QUERY_CREATED)
317 /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
318 TRACE("Query wasn't yet started, returning S_OK\n");
323 if (query->state == QUERY_BUILDING)
325 /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
326 TRACE("Query is building, returning S_FALSE\n");
330 if (!gl_info->supported[ARB_OCCLUSION_QUERY])
332 WARN("%p Occlusion queries not supported. Returning 1.\n", query);
337 if (oq->context->tid != GetCurrentThreadId())
339 FIXME("%p Wrong thread, returning 1.\n", query);
344 context = context_acquire(query->device, oq->context->current_rt);
348 GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
349 checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
350 TRACE("available %#x.\n", available);
356 GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_ARB, &samples));
357 checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
358 TRACE("Returning %d samples.\n", samples);
370 context_release(context);
375 static HRESULT wined3d_event_query_ops_get_data(struct wined3d_query *query,
376 void *pData, DWORD dwSize, DWORD flags)
378 struct wined3d_event_query *event_query = query->extendedData;
380 enum wined3d_event_query_result ret;
382 TRACE("query %p, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
384 if (!pData || !dwSize) return S_OK;
387 WARN("Event query not supported by GL, reporting GPU idle.\n");
392 ret = wined3d_event_query_test(event_query, query->device);
395 case WINED3D_EVENT_QUERY_OK:
396 case WINED3D_EVENT_QUERY_NOT_STARTED:
400 case WINED3D_EVENT_QUERY_WAITING:
404 case WINED3D_EVENT_QUERY_WRONG_THREAD:
405 FIXME("(%p) Wrong thread, reporting GPU idle.\n", query);
409 case WINED3D_EVENT_QUERY_ERROR:
410 ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
411 return WINED3DERR_INVALIDCALL;
417 WINED3DQUERYTYPE CDECL wined3d_query_get_type(const struct wined3d_query *query)
419 TRACE("query %p.\n", query);
424 static HRESULT wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
426 TRACE("query %p, flags %#x.\n", query, flags);
428 TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", query, flags);
429 if (flags & WINED3DISSUE_END)
431 struct wined3d_event_query *event_query = query->extendedData;
433 /* Faked event query support */
434 if (!event_query) return WINED3D_OK;
436 wined3d_event_query_issue(event_query, query->device);
438 else if (flags & WINED3DISSUE_BEGIN)
440 /* Started implicitly at device creation */
441 ERR("Event query issued with START flag - what to do?\n");
444 if (flags & WINED3DISSUE_BEGIN)
445 query->state = QUERY_BUILDING;
447 query->state = QUERY_SIGNALLED;
452 static HRESULT wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
454 struct wined3d_device *device = query->device;
455 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
457 TRACE("query %p, flags %#x.\n", query, flags);
459 if (gl_info->supported[ARB_OCCLUSION_QUERY])
461 struct wined3d_occlusion_query *oq = query->extendedData;
462 struct wined3d_context *context;
464 /* This is allowed according to msdn and our tests. Reset the query and restart */
465 if (flags & WINED3DISSUE_BEGIN)
467 if (query->state == QUERY_BUILDING)
469 if (oq->context->tid != GetCurrentThreadId())
471 FIXME("Wrong thread, can't restart query.\n");
473 context_free_occlusion_query(oq);
474 context = context_acquire(query->device, NULL);
475 context_alloc_occlusion_query(context, oq);
479 context = context_acquire(query->device, oq->context->current_rt);
482 GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
483 checkGLcall("glEndQuery()");
489 if (oq->context) context_free_occlusion_query(oq);
490 context = context_acquire(query->device, NULL);
491 context_alloc_occlusion_query(context, oq);
495 GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, oq->id));
496 checkGLcall("glBeginQuery()");
499 context_release(context);
501 if (flags & WINED3DISSUE_END)
503 /* Msdn says _END on a non-building occlusion query returns an error, but
504 * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
505 * generating an error
507 if (query->state == QUERY_BUILDING)
509 if (oq->context->tid != GetCurrentThreadId())
511 FIXME("Wrong thread, can't end query.\n");
515 context = context_acquire(query->device, oq->context->current_rt);
518 GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
519 checkGLcall("glEndQuery()");
522 context_release(context);
529 FIXME("%p Occlusion queries not supported.\n", query);
532 if (flags & WINED3DISSUE_BEGIN)
533 query->state = QUERY_BUILDING;
535 query->state = QUERY_SIGNALLED;
537 return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL. */
540 static const struct wined3d_query_ops event_query_ops =
542 wined3d_event_query_ops_get_data,
543 wined3d_event_query_ops_issue,
546 static const struct wined3d_query_ops occlusion_query_ops =
548 wined3d_occlusion_query_ops_get_data,
549 wined3d_occlusion_query_ops_issue,
552 static HRESULT query_init(struct wined3d_query *query, struct wined3d_device *device, WINED3DQUERYTYPE type)
554 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
558 case WINED3DQUERYTYPE_OCCLUSION:
559 TRACE("Occlusion query.\n");
560 if (!gl_info->supported[ARB_OCCLUSION_QUERY])
562 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
563 return WINED3DERR_NOTAVAILABLE;
565 query->query_ops = &occlusion_query_ops;
566 query->data_size = sizeof(DWORD);
567 query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
568 if (!query->extendedData)
570 ERR("Failed to allocate occlusion query extended data.\n");
571 return E_OUTOFMEMORY;
573 ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
576 case WINED3DQUERYTYPE_EVENT:
577 TRACE("Event query.\n");
578 if (!wined3d_event_query_supported(gl_info))
580 /* Half-Life 2 needs this query. It does not render the main
581 * menu correctly otherwise. Pretend to support it, faking
582 * this query does not do much harm except potentially
583 * lowering performance. */
584 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
586 query->query_ops = &event_query_ops;
587 query->data_size = sizeof(BOOL);
588 query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
589 if (!query->extendedData)
591 ERR("Failed to allocate event query memory.\n");
592 return E_OUTOFMEMORY;
596 case WINED3DQUERYTYPE_VCACHE:
597 case WINED3DQUERYTYPE_RESOURCEMANAGER:
598 case WINED3DQUERYTYPE_VERTEXSTATS:
599 case WINED3DQUERYTYPE_TIMESTAMP:
600 case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
601 case WINED3DQUERYTYPE_TIMESTAMPFREQ:
602 case WINED3DQUERYTYPE_PIPELINETIMINGS:
603 case WINED3DQUERYTYPE_INTERFACETIMINGS:
604 case WINED3DQUERYTYPE_VERTEXTIMINGS:
605 case WINED3DQUERYTYPE_PIXELTIMINGS:
606 case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
607 case WINED3DQUERYTYPE_CACHEUTILIZATION:
609 FIXME("Unhandled query type %#x.\n", type);
610 return WINED3DERR_NOTAVAILABLE;
614 query->state = QUERY_CREATED;
615 query->device = device;
621 HRESULT CDECL wined3d_query_create(struct wined3d_device *device,
622 WINED3DQUERYTYPE type, struct wined3d_query **query)
624 struct wined3d_query *object;
627 TRACE("device %p, type %#x, query %p.\n", device, type, query);
629 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
632 ERR("Failed to allocate query memory.\n");
633 return E_OUTOFMEMORY;
636 hr = query_init(object, device, type);
639 WARN("Failed to initialize query, hr %#x.\n", hr);
640 HeapFree(GetProcessHeap(), 0, object);
644 TRACE("Created query %p.\n", object);