[WINED3D]
[reactos.git] / reactos / dll / directx / wine / wined3d / query.c
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 #include "wined3d_private.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
24
25 BOOL wined3d_event_query_supported(const struct wined3d_gl_info *gl_info)
26 {
27 return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE];
28 }
29
30 void wined3d_event_query_destroy(struct wined3d_event_query *query)
31 {
32 if (query->context) context_free_event_query(query);
33 HeapFree(GetProcessHeap(), 0, query);
34 }
35
36 static enum wined3d_event_query_result wined3d_event_query_test(const struct wined3d_event_query *query,
37 const struct wined3d_device *device)
38 {
39 struct wined3d_context *context;
40 const struct wined3d_gl_info *gl_info;
41 enum wined3d_event_query_result ret;
42 BOOL fence_result;
43
44 TRACE("(%p) : device %p\n", query, device);
45
46 if (!query->context)
47 {
48 TRACE("Query not started\n");
49 return WINED3D_EVENT_QUERY_NOT_STARTED;
50 }
51
52 if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
53 {
54 WARN("Event query tested from wrong thread\n");
55 return WINED3D_EVENT_QUERY_WRONG_THREAD;
56 }
57
58 context = context_acquire(device, query->context->current_rt);
59 gl_info = context->gl_info;
60
61 if (gl_info->supported[ARB_SYNC])
62 {
63 GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, 0));
64 checkGLcall("glClientWaitSync");
65
66 switch (gl_ret)
67 {
68 case GL_ALREADY_SIGNALED:
69 case GL_CONDITION_SATISFIED:
70 ret = WINED3D_EVENT_QUERY_OK;
71 break;
72
73 case GL_TIMEOUT_EXPIRED:
74 ret = WINED3D_EVENT_QUERY_WAITING;
75 break;
76
77 case GL_WAIT_FAILED:
78 default:
79 ERR("glClientWaitSync returned %#x.\n", gl_ret);
80 ret = WINED3D_EVENT_QUERY_ERROR;
81 }
82 }
83 else if (gl_info->supported[APPLE_FENCE])
84 {
85 fence_result = GL_EXTCALL(glTestFenceAPPLE(query->object.id));
86 checkGLcall("glTestFenceAPPLE");
87 if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
88 else ret = WINED3D_EVENT_QUERY_WAITING;
89 }
90 else if (gl_info->supported[NV_FENCE])
91 {
92 fence_result = GL_EXTCALL(glTestFenceNV(query->object.id));
93 checkGLcall("glTestFenceNV");
94 if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
95 else ret = WINED3D_EVENT_QUERY_WAITING;
96 }
97 else
98 {
99 ERR("Event query created despite lack of GL support\n");
100 ret = WINED3D_EVENT_QUERY_ERROR;
101 }
102
103 context_release(context);
104 return ret;
105 }
106
107 enum wined3d_event_query_result wined3d_event_query_finish(const struct wined3d_event_query *query,
108 const struct wined3d_device *device)
109 {
110 struct wined3d_context *context;
111 const struct wined3d_gl_info *gl_info;
112 enum wined3d_event_query_result ret;
113
114 TRACE("(%p)\n", query);
115
116 if (!query->context)
117 {
118 TRACE("Query not started\n");
119 return WINED3D_EVENT_QUERY_NOT_STARTED;
120 }
121 gl_info = query->context->gl_info;
122
123 if (query->context->tid != GetCurrentThreadId() && !gl_info->supported[ARB_SYNC])
124 {
125 /* A glFinish does not reliably wait for draws in other contexts. The caller has
126 * to find its own way to cope with the thread switch
127 */
128 WARN("Event query finished from wrong thread\n");
129 return WINED3D_EVENT_QUERY_WRONG_THREAD;
130 }
131
132 context = context_acquire(device, query->context->current_rt);
133
134 if (gl_info->supported[ARB_SYNC])
135 {
136 /* Apple seems to be into arbitrary limits, and timeouts larger than
137 * 0xfffffffffffffbff immediately return GL_TIMEOUT_EXPIRED. We don't
138 * really care and can live with waiting a few μs less. (OS X 10.7.4). */
139 GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, GL_SYNC_FLUSH_COMMANDS_BIT, ~(GLuint64)0xffff));
140 checkGLcall("glClientWaitSync");
141
142 switch (gl_ret)
143 {
144 case GL_ALREADY_SIGNALED:
145 case GL_CONDITION_SATISFIED:
146 ret = WINED3D_EVENT_QUERY_OK;
147 break;
148
149 /* We don't expect a timeout for a ~584 year wait */
150 default:
151 ERR("glClientWaitSync returned %#x.\n", gl_ret);
152 ret = WINED3D_EVENT_QUERY_ERROR;
153 }
154 }
155 else if (context->gl_info->supported[APPLE_FENCE])
156 {
157 GL_EXTCALL(glFinishFenceAPPLE(query->object.id));
158 checkGLcall("glFinishFenceAPPLE");
159 ret = WINED3D_EVENT_QUERY_OK;
160 }
161 else if (context->gl_info->supported[NV_FENCE])
162 {
163 GL_EXTCALL(glFinishFenceNV(query->object.id));
164 checkGLcall("glFinishFenceNV");
165 ret = WINED3D_EVENT_QUERY_OK;
166 }
167 else
168 {
169 ERR("Event query created without GL support\n");
170 ret = WINED3D_EVENT_QUERY_ERROR;
171 }
172
173 context_release(context);
174 return ret;
175 }
176
177 void wined3d_event_query_issue(struct wined3d_event_query *query, const struct wined3d_device *device)
178 {
179 const struct wined3d_gl_info *gl_info;
180 struct wined3d_context *context;
181
182 if (query->context)
183 {
184 if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
185 {
186 context_free_event_query(query);
187 context = context_acquire(device, NULL);
188 context_alloc_event_query(context, query);
189 }
190 else
191 {
192 context = context_acquire(device, query->context->current_rt);
193 }
194 }
195 else
196 {
197 context = context_acquire(device, NULL);
198 context_alloc_event_query(context, query);
199 }
200
201 gl_info = context->gl_info;
202
203 if (gl_info->supported[ARB_SYNC])
204 {
205 if (query->object.sync) GL_EXTCALL(glDeleteSync(query->object.sync));
206 checkGLcall("glDeleteSync");
207 query->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
208 checkGLcall("glFenceSync");
209 }
210 else if (gl_info->supported[APPLE_FENCE])
211 {
212 GL_EXTCALL(glSetFenceAPPLE(query->object.id));
213 checkGLcall("glSetFenceAPPLE");
214 }
215 else if (gl_info->supported[NV_FENCE])
216 {
217 GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
218 checkGLcall("glSetFenceNV");
219 }
220
221 context_release(context);
222 }
223
224 ULONG CDECL wined3d_query_incref(struct wined3d_query *query)
225 {
226 ULONG refcount = InterlockedIncrement(&query->ref);
227
228 TRACE("%p increasing refcount to %u.\n", query, refcount);
229
230 return refcount;
231 }
232
233 ULONG CDECL wined3d_query_decref(struct wined3d_query *query)
234 {
235 ULONG refcount = InterlockedDecrement(&query->ref);
236
237 TRACE("%p decreasing refcount to %u.\n", query, refcount);
238
239 if (!refcount)
240 {
241 /* Queries are specific to the GL context that created them. Not
242 * deleting the query will obviously leak it, but that's still better
243 * than potentially deleting a different query with the same id in this
244 * context, and (still) leaking the actual query. */
245 if (query->type == WINED3D_QUERY_TYPE_EVENT)
246 {
247 struct wined3d_event_query *event_query = query->extendedData;
248 if (event_query) wined3d_event_query_destroy(event_query);
249 }
250 else if (query->type == WINED3D_QUERY_TYPE_OCCLUSION)
251 {
252 struct wined3d_occlusion_query *oq = query->extendedData;
253
254 if (oq->context) context_free_occlusion_query(oq);
255 HeapFree(GetProcessHeap(), 0, query->extendedData);
256 }
257
258 HeapFree(GetProcessHeap(), 0, query);
259 }
260
261 return refcount;
262 }
263
264 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
265 void *data, UINT data_size, DWORD flags)
266 {
267 TRACE("query %p, data %p, data_size %u, flags %#x.\n",
268 query, data, data_size, flags);
269
270 return query->query_ops->query_get_data(query, data, data_size, flags);
271 }
272
273 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query)
274 {
275 TRACE("query %p.\n", query);
276
277 return query->data_size;
278 }
279
280 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
281 {
282 TRACE("query %p, flags %#x.\n", query, flags);
283
284 return query->query_ops->query_issue(query, flags);
285 }
286
287 static HRESULT wined3d_occlusion_query_ops_get_data(struct wined3d_query *query,
288 void *pData, DWORD dwSize, DWORD flags)
289 {
290 struct wined3d_occlusion_query *oq = query->extendedData;
291 struct wined3d_device *device = query->device;
292 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
293 struct wined3d_context *context;
294 DWORD* data = pData;
295 GLuint available;
296 GLuint samples;
297 HRESULT res;
298
299 TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
300
301 if (!oq->context)
302 query->state = QUERY_CREATED;
303
304 if (query->state == QUERY_CREATED)
305 {
306 /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
307 TRACE("Query wasn't yet started, returning S_OK\n");
308 if(data) *data = 0;
309 return S_OK;
310 }
311
312 if (query->state == QUERY_BUILDING)
313 {
314 /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
315 TRACE("Query is building, returning S_FALSE\n");
316 return S_FALSE;
317 }
318
319 if (!gl_info->supported[ARB_OCCLUSION_QUERY])
320 {
321 WARN("%p Occlusion queries not supported. Returning 1.\n", query);
322 *data = 1;
323 return S_OK;
324 }
325
326 if (oq->context->tid != GetCurrentThreadId())
327 {
328 FIXME("%p Wrong thread, returning 1.\n", query);
329 *data = 1;
330 return S_OK;
331 }
332
333 context = context_acquire(query->device, oq->context->current_rt);
334
335 GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
336 checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
337 TRACE("available %#x.\n", available);
338
339 if (available)
340 {
341 if (data)
342 {
343 GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_ARB, &samples));
344 checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
345 TRACE("Returning %d samples.\n", samples);
346 *data = samples;
347 }
348 res = S_OK;
349 }
350 else
351 {
352 res = S_FALSE;
353 }
354
355 context_release(context);
356
357 return res;
358 }
359
360 static HRESULT wined3d_event_query_ops_get_data(struct wined3d_query *query,
361 void *pData, DWORD dwSize, DWORD flags)
362 {
363 struct wined3d_event_query *event_query = query->extendedData;
364 BOOL *data = pData;
365 enum wined3d_event_query_result ret;
366
367 TRACE("query %p, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
368
369 if (!pData || !dwSize) return S_OK;
370 if (!event_query)
371 {
372 WARN("Event query not supported by GL, reporting GPU idle.\n");
373 *data = TRUE;
374 return S_OK;
375 }
376
377 ret = wined3d_event_query_test(event_query, query->device);
378 switch(ret)
379 {
380 case WINED3D_EVENT_QUERY_OK:
381 case WINED3D_EVENT_QUERY_NOT_STARTED:
382 *data = TRUE;
383 break;
384
385 case WINED3D_EVENT_QUERY_WAITING:
386 *data = FALSE;
387 break;
388
389 case WINED3D_EVENT_QUERY_WRONG_THREAD:
390 FIXME("(%p) Wrong thread, reporting GPU idle.\n", query);
391 *data = TRUE;
392 break;
393
394 case WINED3D_EVENT_QUERY_ERROR:
395 ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
396 return WINED3DERR_INVALIDCALL;
397 }
398
399 return S_OK;
400 }
401
402 enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query *query)
403 {
404 TRACE("query %p.\n", query);
405
406 return query->type;
407 }
408
409 static HRESULT wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
410 {
411 TRACE("query %p, flags %#x.\n", query, flags);
412
413 TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", query, flags);
414 if (flags & WINED3DISSUE_END)
415 {
416 struct wined3d_event_query *event_query = query->extendedData;
417
418 /* Faked event query support */
419 if (!event_query) return WINED3D_OK;
420
421 wined3d_event_query_issue(event_query, query->device);
422 }
423 else if (flags & WINED3DISSUE_BEGIN)
424 {
425 /* Started implicitly at device creation */
426 ERR("Event query issued with START flag - what to do?\n");
427 }
428
429 if (flags & WINED3DISSUE_BEGIN)
430 query->state = QUERY_BUILDING;
431 else
432 query->state = QUERY_SIGNALLED;
433
434 return WINED3D_OK;
435 }
436
437 static HRESULT wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
438 {
439 struct wined3d_device *device = query->device;
440 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
441
442 TRACE("query %p, flags %#x.\n", query, flags);
443
444 if (gl_info->supported[ARB_OCCLUSION_QUERY])
445 {
446 struct wined3d_occlusion_query *oq = query->extendedData;
447 struct wined3d_context *context;
448
449 /* This is allowed according to msdn and our tests. Reset the query and restart */
450 if (flags & WINED3DISSUE_BEGIN)
451 {
452 if (query->state == QUERY_BUILDING)
453 {
454 if (oq->context->tid != GetCurrentThreadId())
455 {
456 FIXME("Wrong thread, can't restart query.\n");
457
458 context_free_occlusion_query(oq);
459 context = context_acquire(query->device, NULL);
460 context_alloc_occlusion_query(context, oq);
461 }
462 else
463 {
464 context = context_acquire(query->device, oq->context->current_rt);
465
466 GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
467 checkGLcall("glEndQuery()");
468 }
469 }
470 else
471 {
472 if (oq->context) context_free_occlusion_query(oq);
473 context = context_acquire(query->device, NULL);
474 context_alloc_occlusion_query(context, oq);
475 }
476
477 GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, oq->id));
478 checkGLcall("glBeginQuery()");
479
480 context_release(context);
481 }
482 if (flags & WINED3DISSUE_END)
483 {
484 /* Msdn says _END on a non-building occlusion query returns an error, but
485 * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
486 * generating an error
487 */
488 if (query->state == QUERY_BUILDING)
489 {
490 if (oq->context->tid != GetCurrentThreadId())
491 {
492 FIXME("Wrong thread, can't end query.\n");
493 }
494 else
495 {
496 context = context_acquire(query->device, oq->context->current_rt);
497
498 GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
499 checkGLcall("glEndQuery()");
500
501 context_release(context);
502 }
503 }
504 }
505 }
506 else
507 {
508 FIXME("%p Occlusion queries not supported.\n", query);
509 }
510
511 if (flags & WINED3DISSUE_BEGIN)
512 query->state = QUERY_BUILDING;
513 else
514 query->state = QUERY_SIGNALLED;
515
516 return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL. */
517 }
518
519 static const struct wined3d_query_ops event_query_ops =
520 {
521 wined3d_event_query_ops_get_data,
522 wined3d_event_query_ops_issue,
523 };
524
525 static const struct wined3d_query_ops occlusion_query_ops =
526 {
527 wined3d_occlusion_query_ops_get_data,
528 wined3d_occlusion_query_ops_issue,
529 };
530
531 static HRESULT query_init(struct wined3d_query *query, struct wined3d_device *device, enum wined3d_query_type type)
532 {
533 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
534
535 switch (type)
536 {
537 case WINED3D_QUERY_TYPE_OCCLUSION:
538 TRACE("Occlusion query.\n");
539 if (!gl_info->supported[ARB_OCCLUSION_QUERY])
540 {
541 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
542 return WINED3DERR_NOTAVAILABLE;
543 }
544 query->query_ops = &occlusion_query_ops;
545 query->data_size = sizeof(DWORD);
546 query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
547 if (!query->extendedData)
548 {
549 ERR("Failed to allocate occlusion query extended data.\n");
550 return E_OUTOFMEMORY;
551 }
552 ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
553 break;
554
555 case WINED3D_QUERY_TYPE_EVENT:
556 TRACE("Event query.\n");
557 if (!wined3d_event_query_supported(gl_info))
558 {
559 /* Half-Life 2 needs this query. It does not render the main
560 * menu correctly otherwise. Pretend to support it, faking
561 * this query does not do much harm except potentially
562 * lowering performance. */
563 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
564 }
565 query->query_ops = &event_query_ops;
566 query->data_size = sizeof(BOOL);
567 query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
568 if (!query->extendedData)
569 {
570 ERR("Failed to allocate event query memory.\n");
571 return E_OUTOFMEMORY;
572 }
573 break;
574
575 case WINED3D_QUERY_TYPE_VCACHE:
576 case WINED3D_QUERY_TYPE_RESOURCE_MANAGER:
577 case WINED3D_QUERY_TYPE_VERTEX_STATS:
578 case WINED3D_QUERY_TYPE_TIMESTAMP:
579 case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT:
580 case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ:
581 case WINED3D_QUERY_TYPE_PIPELINE_TIMINGS:
582 case WINED3D_QUERY_TYPE_INTERFACE_TIMINGS:
583 case WINED3D_QUERY_TYPE_VERTEX_TIMINGS:
584 case WINED3D_QUERY_TYPE_PIXEL_TIMINGS:
585 case WINED3D_QUERY_TYPE_BANDWIDTH_TIMINGS:
586 case WINED3D_QUERY_TYPE_CACHE_UTILIZATION:
587 default:
588 FIXME("Unhandled query type %#x.\n", type);
589 return WINED3DERR_NOTAVAILABLE;
590 }
591
592 query->type = type;
593 query->state = QUERY_CREATED;
594 query->device = device;
595 query->ref = 1;
596
597 return WINED3D_OK;
598 }
599
600 HRESULT CDECL wined3d_query_create(struct wined3d_device *device,
601 enum wined3d_query_type type, struct wined3d_query **query)
602 {
603 struct wined3d_query *object;
604 HRESULT hr;
605
606 TRACE("device %p, type %#x, query %p.\n", device, type, query);
607
608 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
609 if (!object)
610 return E_OUTOFMEMORY;
611
612 hr = query_init(object, device, type);
613 if (FAILED(hr))
614 {
615 WARN("Failed to initialize query, hr %#x.\n", hr);
616 HeapFree(GetProcessHeap(), 0, object);
617 return hr;
618 }
619
620 TRACE("Created query %p.\n", object);
621 *query = object;
622
623 return WINED3D_OK;
624 }