VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Wine_new/wined3d/query.c@ 46757

Last change on this file since 46757 was 46521, checked in by vboxsync, 12 years ago

wine/new: export

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette