dinput: SetActionMap setting the device buffer.
[wine] / dlls / wined3d / query.c
1 /*
2  * IWineD3DQuery implementation
3  *
4  * Copyright 2005 Oliver Stieber
5  * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6  * Copyright 2009-2010 Henri Verbeet for CodeWeavers.
7  *
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.
12  *
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.
17  *
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
21  */
22
23
24 #include "config.h"
25 #include "wined3d_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
28
29 BOOL wined3d_event_query_supported(const struct wined3d_gl_info *gl_info)
30 {
31     return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE];
32 }
33
34 void wined3d_event_query_destroy(struct wined3d_event_query *query)
35 {
36     if (query->context) context_free_event_query(query);
37     HeapFree(GetProcessHeap(), 0, query);
38 }
39
40 static enum wined3d_event_query_result wined3d_event_query_test(struct wined3d_event_query *query,
41         struct wined3d_device *device)
42 {
43     struct wined3d_context *context;
44     const struct wined3d_gl_info *gl_info;
45     enum wined3d_event_query_result ret;
46     BOOL fence_result;
47
48     TRACE("(%p) : device %p\n", query, device);
49
50     if (!query->context)
51     {
52         TRACE("Query not started\n");
53         return WINED3D_EVENT_QUERY_NOT_STARTED;
54     }
55
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
62     context = context_acquire(device, query->context->current_rt);
63     gl_info = context->gl_info;
64
65     ENTER_GL();
66
67     if (gl_info->supported[ARB_SYNC])
68     {
69         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, 0));
70         checkGLcall("glClientWaitSync");
71
72         switch (gl_ret)
73         {
74             case GL_ALREADY_SIGNALED:
75             case GL_CONDITION_SATISFIED:
76                 ret = WINED3D_EVENT_QUERY_OK;
77                 break;
78
79             case GL_TIMEOUT_EXPIRED:
80                 ret = WINED3D_EVENT_QUERY_WAITING;
81                 break;
82
83             case GL_WAIT_FAILED:
84             default:
85                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
86                 ret = WINED3D_EVENT_QUERY_ERROR;
87         }
88     }
89     else if (gl_info->supported[APPLE_FENCE])
90     {
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;
95     }
96     else if (gl_info->supported[NV_FENCE])
97     {
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;
102     }
103     else
104     {
105         ERR("Event query created despite lack of GL support\n");
106         ret = WINED3D_EVENT_QUERY_ERROR;
107     }
108
109     LEAVE_GL();
110
111     context_release(context);
112     return ret;
113 }
114
115 enum wined3d_event_query_result wined3d_event_query_finish(struct wined3d_event_query *query,
116         struct wined3d_device *device)
117 {
118     struct wined3d_context *context;
119     const struct wined3d_gl_info *gl_info;
120     enum wined3d_event_query_result ret;
121
122     TRACE("(%p)\n", query);
123
124     if (!query->context)
125     {
126         TRACE("Query not started\n");
127         return WINED3D_EVENT_QUERY_NOT_STARTED;
128     }
129     gl_info = query->context->gl_info;
130
131     if (query->context->tid != GetCurrentThreadId() && !gl_info->supported[ARB_SYNC])
132     {
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
135          */
136         WARN("Event query finished from wrong thread\n");
137         return WINED3D_EVENT_QUERY_WRONG_THREAD;
138     }
139
140     context = context_acquire(device, query->context->current_rt);
141
142     ENTER_GL();
143     if (gl_info->supported[ARB_SYNC])
144     {
145         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, ~(GLuint64)0));
146         checkGLcall("glClientWaitSync");
147
148         switch (gl_ret)
149         {
150             case GL_ALREADY_SIGNALED:
151             case GL_CONDITION_SATISFIED:
152                 ret = WINED3D_EVENT_QUERY_OK;
153                 break;
154
155                 /* We don't expect a timeout for a ~584 year wait */
156             default:
157                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
158                 ret = WINED3D_EVENT_QUERY_ERROR;
159         }
160     }
161     else if (context->gl_info->supported[APPLE_FENCE])
162     {
163         GL_EXTCALL(glFinishFenceAPPLE(query->object.id));
164         checkGLcall("glFinishFenceAPPLE");
165         ret = WINED3D_EVENT_QUERY_OK;
166     }
167     else if (context->gl_info->supported[NV_FENCE])
168     {
169         GL_EXTCALL(glFinishFenceNV(query->object.id));
170         checkGLcall("glFinishFenceNV");
171         ret = WINED3D_EVENT_QUERY_OK;
172     }
173     else
174     {
175         ERR("Event query created without GL support\n");
176         ret = WINED3D_EVENT_QUERY_ERROR;
177     }
178     LEAVE_GL();
179
180     context_release(context);
181     return ret;
182 }
183
184 void wined3d_event_query_issue(struct wined3d_event_query *query, struct wined3d_device *device)
185 {
186     const struct wined3d_gl_info *gl_info;
187     struct wined3d_context *context;
188
189     if (query->context)
190     {
191         if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
192         {
193             context_free_event_query(query);
194             context = context_acquire(device, NULL);
195             context_alloc_event_query(context, query);
196         }
197         else
198         {
199             context = context_acquire(device, query->context->current_rt);
200         }
201     }
202     else
203     {
204         context = context_acquire(device, NULL);
205         context_alloc_event_query(context, query);
206     }
207
208     gl_info = context->gl_info;
209
210     ENTER_GL();
211
212     if (gl_info->supported[ARB_SYNC])
213     {
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");
218     }
219     else if (gl_info->supported[APPLE_FENCE])
220     {
221         GL_EXTCALL(glSetFenceAPPLE(query->object.id));
222         checkGLcall("glSetFenceAPPLE");
223     }
224     else if (gl_info->supported[NV_FENCE])
225     {
226         GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
227         checkGLcall("glSetFenceNV");
228     }
229
230     LEAVE_GL();
231
232     context_release(context);
233 }
234
235 ULONG CDECL wined3d_query_incref(struct wined3d_query *query)
236 {
237     ULONG refcount = InterlockedIncrement(&query->ref);
238
239     TRACE("%p increasing refcount to %u.\n", query, refcount);
240
241     return refcount;
242 }
243
244 ULONG CDECL wined3d_query_decref(struct wined3d_query *query)
245 {
246     ULONG refcount = InterlockedDecrement(&query->ref);
247
248     TRACE("%p decreasing refcount to %u.\n", query, refcount);
249
250     if (!refcount)
251     {
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)
257         {
258             struct wined3d_event_query *event_query = query->extendedData;
259             if (event_query) wined3d_event_query_destroy(event_query);
260         }
261         else if (query->type == WINED3DQUERYTYPE_OCCLUSION)
262         {
263             struct wined3d_occlusion_query *oq = query->extendedData;
264
265             if (oq->context) context_free_occlusion_query(oq);
266             HeapFree(GetProcessHeap(), 0, query->extendedData);
267         }
268
269         HeapFree(GetProcessHeap(), 0, query);
270     }
271
272     return refcount;
273 }
274
275 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
276         void *data, UINT data_size, DWORD flags)
277 {
278     TRACE("query %p, data %p, data_size %u, flags %#x.\n",
279             query, data, data_size, flags);
280
281     return query->query_ops->query_get_data(query, data, data_size, flags);
282 }
283
284 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query)
285 {
286     TRACE("query %p.\n", query);
287
288     return query->data_size;
289 }
290
291 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
292 {
293     TRACE("query %p, flags %#x.\n", query, flags);
294
295     return query->query_ops->query_issue(query, flags);
296 }
297
298 static HRESULT wined3d_occlusion_query_ops_get_data(struct wined3d_query *query,
299         void *pData, DWORD dwSize, DWORD flags)
300 {
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;
305     DWORD* data = pData;
306     GLuint available;
307     GLuint samples;
308     HRESULT res;
309
310     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
311
312     if (!oq->context)
313         query->state = QUERY_CREATED;
314
315     if (query->state == QUERY_CREATED)
316     {
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");
319         if(data) *data = 0;
320         return S_OK;
321     }
322
323     if (query->state == QUERY_BUILDING)
324     {
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");
327         return S_FALSE;
328     }
329
330     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
331     {
332         WARN("%p Occlusion queries not supported. Returning 1.\n", query);
333         *data = 1;
334         return S_OK;
335     }
336
337     if (oq->context->tid != GetCurrentThreadId())
338     {
339         FIXME("%p Wrong thread, returning 1.\n", query);
340         *data = 1;
341         return S_OK;
342     }
343
344     context = context_acquire(query->device, oq->context->current_rt);
345
346     ENTER_GL();
347
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);
351
352     if (available)
353     {
354         if (data)
355         {
356             GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_ARB, &samples));
357             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
358             TRACE("Returning %d samples.\n", samples);
359             *data = samples;
360         }
361         res = S_OK;
362     }
363     else
364     {
365         res = S_FALSE;
366     }
367
368     LEAVE_GL();
369
370     context_release(context);
371
372     return res;
373 }
374
375 static HRESULT wined3d_event_query_ops_get_data(struct wined3d_query *query,
376         void *pData, DWORD dwSize, DWORD flags)
377 {
378     struct wined3d_event_query *event_query = query->extendedData;
379     BOOL *data = pData;
380     enum wined3d_event_query_result ret;
381
382     TRACE("query %p, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
383
384     if (!pData || !dwSize) return S_OK;
385     if (!event_query)
386     {
387         WARN("Event query not supported by GL, reporting GPU idle.\n");
388         *data = TRUE;
389         return S_OK;
390     }
391
392     ret = wined3d_event_query_test(event_query, query->device);
393     switch(ret)
394     {
395         case WINED3D_EVENT_QUERY_OK:
396         case WINED3D_EVENT_QUERY_NOT_STARTED:
397             *data = TRUE;
398             break;
399
400         case WINED3D_EVENT_QUERY_WAITING:
401             *data = FALSE;
402             break;
403
404         case WINED3D_EVENT_QUERY_WRONG_THREAD:
405             FIXME("(%p) Wrong thread, reporting GPU idle.\n", query);
406             *data = TRUE;
407             break;
408
409         case WINED3D_EVENT_QUERY_ERROR:
410             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
411             return WINED3DERR_INVALIDCALL;
412     }
413
414     return S_OK;
415 }
416
417 WINED3DQUERYTYPE CDECL wined3d_query_get_type(const struct wined3d_query *query)
418 {
419     TRACE("query %p.\n", query);
420
421     return query->type;
422 }
423
424 static HRESULT wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
425 {
426     TRACE("query %p, flags %#x.\n", query, flags);
427
428     TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", query, flags);
429     if (flags & WINED3DISSUE_END)
430     {
431         struct wined3d_event_query *event_query = query->extendedData;
432
433         /* Faked event query support */
434         if (!event_query) return WINED3D_OK;
435
436         wined3d_event_query_issue(event_query, query->device);
437     }
438     else if (flags & WINED3DISSUE_BEGIN)
439     {
440         /* Started implicitly at device creation */
441         ERR("Event query issued with START flag - what to do?\n");
442     }
443
444     if (flags & WINED3DISSUE_BEGIN)
445         query->state = QUERY_BUILDING;
446     else
447         query->state = QUERY_SIGNALLED;
448
449     return WINED3D_OK;
450 }
451
452 static HRESULT wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
453 {
454     struct wined3d_device *device = query->device;
455     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
456
457     TRACE("query %p, flags %#x.\n", query, flags);
458
459     if (gl_info->supported[ARB_OCCLUSION_QUERY])
460     {
461         struct wined3d_occlusion_query *oq = query->extendedData;
462         struct wined3d_context *context;
463
464         /* This is allowed according to msdn and our tests. Reset the query and restart */
465         if (flags & WINED3DISSUE_BEGIN)
466         {
467             if (query->state == QUERY_BUILDING)
468             {
469                 if (oq->context->tid != GetCurrentThreadId())
470                 {
471                     FIXME("Wrong thread, can't restart query.\n");
472
473                     context_free_occlusion_query(oq);
474                     context = context_acquire(query->device, NULL);
475                     context_alloc_occlusion_query(context, oq);
476                 }
477                 else
478                 {
479                     context = context_acquire(query->device, oq->context->current_rt);
480
481                     ENTER_GL();
482                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
483                     checkGLcall("glEndQuery()");
484                     LEAVE_GL();
485                 }
486             }
487             else
488             {
489                 if (oq->context) context_free_occlusion_query(oq);
490                 context = context_acquire(query->device, NULL);
491                 context_alloc_occlusion_query(context, oq);
492             }
493
494             ENTER_GL();
495             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, oq->id));
496             checkGLcall("glBeginQuery()");
497             LEAVE_GL();
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 (oq->context->tid != GetCurrentThreadId())
510                 {
511                     FIXME("Wrong thread, can't end query.\n");
512                 }
513                 else
514                 {
515                     context = context_acquire(query->device, oq->context->current_rt);
516
517                     ENTER_GL();
518                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
519                     checkGLcall("glEndQuery()");
520                     LEAVE_GL();
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
540 static const struct wined3d_query_ops event_query_ops =
541 {
542     wined3d_event_query_ops_get_data,
543     wined3d_event_query_ops_issue,
544 };
545
546 static const struct wined3d_query_ops occlusion_query_ops =
547 {
548     wined3d_occlusion_query_ops_get_data,
549     wined3d_occlusion_query_ops_issue,
550 };
551
552 static HRESULT query_init(struct wined3d_query *query, struct wined3d_device *device, WINED3DQUERYTYPE type)
553 {
554     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
555
556     switch (type)
557     {
558         case WINED3DQUERYTYPE_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 WINED3DQUERYTYPE_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 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:
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
621 HRESULT CDECL wined3d_query_create(struct wined3d_device *device,
622         WINED3DQUERYTYPE 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     {
632         ERR("Failed to allocate query memory.\n");
633         return E_OUTOFMEMORY;
634     }
635
636     hr = query_init(object, device, type);
637     if (FAILED(hr))
638     {
639         WARN("Failed to initialize query, hr %#x.\n", hr);
640         HeapFree(GetProcessHeap(), 0, object);
641         return hr;
642     }
643
644     TRACE("Created query %p.\n", object);
645     *query = object;
646
647     return WINED3D_OK;
648 }