include/msvcrt: Define more CPU control word flags.
[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 enum wined3d_event_query_result wined3d_event_query_test(struct wined3d_event_query *query, IWineD3DDeviceImpl *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 (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
56     {
57         WARN("Event query tested from wrong thread\n");
58         return WINED3D_EVENT_QUERY_WRONG_THREAD;
59     }
60
61     context = context_acquire(device, query->context->current_rt);
62     gl_info = context->gl_info;
63
64     ENTER_GL();
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         ERR("Event query created despite lack of GL support\n");
105         ret = WINED3D_EVENT_QUERY_ERROR;
106     }
107
108     LEAVE_GL();
109
110     context_release(context);
111     return ret;
112 }
113
114 enum wined3d_event_query_result wined3d_event_query_finish(struct wined3d_event_query *query, IWineD3DDeviceImpl *device)
115 {
116     struct wined3d_context *context;
117     const struct wined3d_gl_info *gl_info;
118     enum wined3d_event_query_result ret;
119
120     TRACE("(%p)\n", query);
121
122     if (!query->context)
123     {
124         TRACE("Query not started\n");
125         return WINED3D_EVENT_QUERY_NOT_STARTED;
126     }
127     gl_info = query->context->gl_info;
128
129     if (query->context->tid != GetCurrentThreadId() && !gl_info->supported[ARB_SYNC])
130     {
131         /* A glFinish does not reliably wait for draws in other contexts. The caller has
132          * to find its own way to cope with the thread switch
133          */
134         WARN("Event query finished from wrong thread\n");
135         return WINED3D_EVENT_QUERY_WRONG_THREAD;
136     }
137
138     context = context_acquire(device, query->context->current_rt);
139
140     ENTER_GL();
141     if (gl_info->supported[ARB_SYNC])
142     {
143         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, ~(GLuint64)0));
144         checkGLcall("glClientWaitSync");
145
146         switch (gl_ret)
147         {
148             case GL_ALREADY_SIGNALED:
149             case GL_CONDITION_SATISFIED:
150                 ret = WINED3D_EVENT_QUERY_OK;
151                 break;
152
153                 /* We don't expect a timeout for a ~584 year wait */
154             default:
155                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
156                 ret = WINED3D_EVENT_QUERY_ERROR;
157         }
158     }
159     else if (context->gl_info->supported[APPLE_FENCE])
160     {
161         GL_EXTCALL(glFinishFenceAPPLE(query->object.id));
162         checkGLcall("glFinishFenceAPPLE");
163         ret = WINED3D_EVENT_QUERY_OK;
164     }
165     else if (context->gl_info->supported[NV_FENCE])
166     {
167         GL_EXTCALL(glFinishFenceNV(query->object.id));
168         checkGLcall("glFinishFenceNV");
169         ret = WINED3D_EVENT_QUERY_OK;
170     }
171     else
172     {
173         ERR("Event query created without GL support\n");
174         ret = WINED3D_EVENT_QUERY_ERROR;
175     }
176     LEAVE_GL();
177
178     context_release(context);
179     return ret;
180 }
181
182 void wined3d_event_query_issue(struct wined3d_event_query *query, IWineD3DDeviceImpl *device)
183 {
184     const struct wined3d_gl_info *gl_info;
185     struct wined3d_context *context;
186
187     if (query->context)
188     {
189         if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
190         {
191             context_free_event_query(query);
192             context = context_acquire(device, NULL);
193             context_alloc_event_query(context, query);
194         }
195         else
196         {
197             context = context_acquire(device, query->context->current_rt);
198         }
199     }
200     else
201     {
202         context = context_acquire(device, NULL);
203         context_alloc_event_query(context, query);
204     }
205
206     gl_info = context->gl_info;
207
208     ENTER_GL();
209
210     if (gl_info->supported[ARB_SYNC])
211     {
212         if (query->object.sync) GL_EXTCALL(glDeleteSync(query->object.sync));
213         checkGLcall("glDeleteSync");
214         query->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
215         checkGLcall("glFenceSync");
216     }
217     else if (gl_info->supported[APPLE_FENCE])
218     {
219         GL_EXTCALL(glSetFenceAPPLE(query->object.id));
220         checkGLcall("glSetFenceAPPLE");
221     }
222     else if (gl_info->supported[NV_FENCE])
223     {
224         GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
225         checkGLcall("glSetFenceNV");
226     }
227
228     LEAVE_GL();
229
230     context_release(context);
231 }
232
233 static HRESULT  WINAPI IWineD3DQueryImpl_QueryInterface(IWineD3DQuery *iface, REFIID riid, void **object)
234 {
235     TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
236
237     if (IsEqualGUID(riid, &IID_IWineD3DQuery)
238             || IsEqualGUID(riid, &IID_IUnknown))
239     {
240         IUnknown_AddRef(iface);
241         *object = iface;
242         return S_OK;
243     }
244
245     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
246
247     *object = NULL;
248     return E_NOINTERFACE;
249 }
250
251 static ULONG  WINAPI IWineD3DQueryImpl_AddRef(IWineD3DQuery *iface) {
252     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
253     TRACE("(%p) : AddRef increasing from %d\n", This, This->ref);
254     return InterlockedIncrement(&This->ref);
255 }
256
257 static ULONG  WINAPI IWineD3DQueryImpl_Release(IWineD3DQuery *iface) {
258     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
259     ULONG ref;
260     TRACE("(%p) : Releasing from %d\n", This, This->ref);
261     ref = InterlockedDecrement(&This->ref);
262
263     if (!ref)
264     {
265         /* Queries are specific to the GL context that created them. Not
266          * deleting the query will obviously leak it, but that's still better
267          * than potentially deleting a different query with the same id in this
268          * context, and (still) leaking the actual query. */
269         if (This->type == WINED3DQUERYTYPE_EVENT)
270         {
271             struct wined3d_event_query *query = This->extendedData;
272             if (query) wined3d_event_query_destroy(query);
273         }
274         else if (This->type == WINED3DQUERYTYPE_OCCLUSION)
275         {
276             struct wined3d_occlusion_query *query = This->extendedData;
277
278             if (query->context) context_free_occlusion_query(query);
279             HeapFree(GetProcessHeap(), 0, This->extendedData);
280         }
281
282         HeapFree(GetProcessHeap(), 0, This);
283     }
284     return ref;
285 }
286
287 static HRESULT WINAPI IWineD3DOcclusionQueryImpl_GetData(IWineD3DQuery *iface,
288         void *pData, DWORD dwSize, DWORD flags)
289 {
290     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *) iface;
291     struct wined3d_occlusion_query *query = This->extendedData;
292     IWineD3DDeviceImpl *device = This->device;
293     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
294     struct wined3d_context *context;
295     DWORD* data = pData;
296     GLuint available;
297     GLuint samples;
298     HRESULT res;
299
300     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", This, pData, dwSize, flags);
301
302     if (!query->context) This->state = QUERY_CREATED;
303
304     if (This->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 (This->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", This);
322         *data = 1;
323         return S_OK;
324     }
325
326     if (query->context->tid != GetCurrentThreadId())
327     {
328         FIXME("%p Wrong thread, returning 1.\n", This);
329         *data = 1;
330         return S_OK;
331     }
332
333     context = context_acquire(This->device, query->context->current_rt);
334
335     ENTER_GL();
336
337     GL_EXTCALL(glGetQueryObjectuivARB(query->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
338     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
339     TRACE("(%p) : available %d.\n", This, available);
340
341     if (available)
342     {
343         if (data)
344         {
345             GL_EXTCALL(glGetQueryObjectuivARB(query->id, GL_QUERY_RESULT_ARB, &samples));
346             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
347             TRACE("(%p) : Returning %d samples.\n", This, samples);
348             *data = samples;
349         }
350         res = S_OK;
351     }
352     else
353     {
354         res = S_FALSE;
355     }
356
357     LEAVE_GL();
358
359     context_release(context);
360
361     return res;
362 }
363
364 static HRESULT WINAPI IWineD3DEventQueryImpl_GetData(IWineD3DQuery *iface,
365         void *pData, DWORD dwSize, DWORD flags)
366 {
367     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *) iface;
368     struct wined3d_event_query *query = This->extendedData;
369     BOOL *data = pData;
370     enum wined3d_event_query_result ret;
371
372     TRACE("(%p) : type D3DQUERY_EVENT, pData %p, dwSize %#x, flags %#x.\n", This, pData, dwSize, flags);
373
374     if (!pData || !dwSize) return S_OK;
375     if (!query)
376     {
377         WARN("(%p): Event query not supported by GL, reporting GPU idle\n", This);
378         *data = TRUE;
379         return S_OK;
380     }
381
382     ret = wined3d_event_query_test(query, This->device);
383     switch(ret)
384     {
385         case WINED3D_EVENT_QUERY_OK:
386         case WINED3D_EVENT_QUERY_NOT_STARTED:
387             *data = TRUE;
388             break;
389
390         case WINED3D_EVENT_QUERY_WAITING:
391             *data = FALSE;
392             break;
393
394         case WINED3D_EVENT_QUERY_WRONG_THREAD:
395             FIXME("(%p) Wrong thread, reporting GPU idle.\n", This);
396             *data = TRUE;
397             break;
398
399         case WINED3D_EVENT_QUERY_ERROR:
400             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
401             return WINED3DERR_INVALIDCALL;
402     }
403
404     return S_OK;
405 }
406
407 static DWORD  WINAPI IWineD3DEventQueryImpl_GetDataSize(IWineD3DQuery* iface){
408     TRACE("(%p) : type D3DQUERY_EVENT\n", iface);
409
410     return sizeof(BOOL);
411 }
412
413 static DWORD  WINAPI IWineD3DOcclusionQueryImpl_GetDataSize(IWineD3DQuery* iface){
414     TRACE("(%p) : type D3DQUERY_OCCLUSION\n", iface);
415
416     return sizeof(DWORD);
417 }
418
419 static WINED3DQUERYTYPE  WINAPI IWineD3DQueryImpl_GetType(IWineD3DQuery* iface){
420     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
421     return This->type;
422 }
423
424 static HRESULT WINAPI IWineD3DEventQueryImpl_Issue(IWineD3DQuery* iface, DWORD flags)
425 {
426     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
427
428     TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", This, flags);
429     if (flags & WINED3DISSUE_END)
430     {
431         struct wined3d_event_query *query = This->extendedData;
432
433         /* Faked event query support */
434         if (!query) return WINED3D_OK;
435
436         wined3d_event_query_issue(query, This->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         This->state = QUERY_BUILDING;
446     else
447         This->state = QUERY_SIGNALLED;
448
449     return WINED3D_OK;
450 }
451
452 static HRESULT WINAPI IWineD3DOcclusionQueryImpl_Issue(IWineD3DQuery *iface, DWORD flags)
453 {
454     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
455     IWineD3DDeviceImpl *device = This->device;
456     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
457
458     if (gl_info->supported[ARB_OCCLUSION_QUERY])
459     {
460         struct wined3d_occlusion_query *query = This->extendedData;
461         struct wined3d_context *context;
462
463         /* This is allowed according to msdn and our tests. Reset the query and restart */
464         if (flags & WINED3DISSUE_BEGIN)
465         {
466             if (This->state == QUERY_BUILDING)
467             {
468                 if (query->context->tid != GetCurrentThreadId())
469                 {
470                     FIXME("Wrong thread, can't restart query.\n");
471
472                     context_free_occlusion_query(query);
473                     context = context_acquire(This->device, NULL);
474                     context_alloc_occlusion_query(context, query);
475                 }
476                 else
477                 {
478                     context = context_acquire(This->device, query->context->current_rt);
479
480                     ENTER_GL();
481                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
482                     checkGLcall("glEndQuery()");
483                     LEAVE_GL();
484                 }
485             }
486             else
487             {
488                 if (query->context) context_free_occlusion_query(query);
489                 context = context_acquire(This->device, NULL);
490                 context_alloc_occlusion_query(context, query);
491             }
492
493             ENTER_GL();
494             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, query->id));
495             checkGLcall("glBeginQuery()");
496             LEAVE_GL();
497
498             context_release(context);
499         }
500         if (flags & WINED3DISSUE_END)
501         {
502             /* Msdn says _END on a non-building occlusion query returns an error, but
503              * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
504              * generating an error
505              */
506             if (This->state == QUERY_BUILDING)
507             {
508                 if (query->context->tid != GetCurrentThreadId())
509                 {
510                     FIXME("Wrong thread, can't end query.\n");
511                 }
512                 else
513                 {
514                     context = context_acquire(This->device, query->context->current_rt);
515
516                     ENTER_GL();
517                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
518                     checkGLcall("glEndQuery()");
519                     LEAVE_GL();
520
521                     context_release(context);
522                 }
523             }
524         }
525     } else {
526         FIXME("(%p) : Occlusion queries not supported\n", This);
527     }
528
529     if (flags & WINED3DISSUE_BEGIN)
530         This->state = QUERY_BUILDING;
531     else
532         This->state = QUERY_SIGNALLED;
533
534     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
535 }
536
537 static const IWineD3DQueryVtbl IWineD3DEventQuery_Vtbl =
538 {
539     /*** IUnknown methods ***/
540     IWineD3DQueryImpl_QueryInterface,
541     IWineD3DQueryImpl_AddRef,
542     IWineD3DQueryImpl_Release,
543     /*** IWineD3Dquery methods ***/
544     IWineD3DEventQueryImpl_GetData,
545     IWineD3DEventQueryImpl_GetDataSize,
546     IWineD3DQueryImpl_GetType,
547     IWineD3DEventQueryImpl_Issue
548 };
549
550 static const IWineD3DQueryVtbl IWineD3DOcclusionQuery_Vtbl =
551 {
552     /*** IUnknown methods ***/
553     IWineD3DQueryImpl_QueryInterface,
554     IWineD3DQueryImpl_AddRef,
555     IWineD3DQueryImpl_Release,
556     /*** IWineD3Dquery methods ***/
557     IWineD3DOcclusionQueryImpl_GetData,
558     IWineD3DOcclusionQueryImpl_GetDataSize,
559     IWineD3DQueryImpl_GetType,
560     IWineD3DOcclusionQueryImpl_Issue
561 };
562
563 HRESULT query_init(IWineD3DQueryImpl *query, IWineD3DDeviceImpl *device, WINED3DQUERYTYPE type)
564 {
565     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
566
567     switch (type)
568     {
569         case WINED3DQUERYTYPE_OCCLUSION:
570             TRACE("Occlusion query.\n");
571             if (!gl_info->supported[ARB_OCCLUSION_QUERY])
572             {
573                 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
574                 return WINED3DERR_NOTAVAILABLE;
575             }
576             query->lpVtbl = &IWineD3DOcclusionQuery_Vtbl;
577             query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
578             if (!query->extendedData)
579             {
580                 ERR("Failed to allocate occlusion query extended data.\n");
581                 return E_OUTOFMEMORY;
582             }
583             ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
584             break;
585
586         case WINED3DQUERYTYPE_EVENT:
587             TRACE("Event query.\n");
588             if (!wined3d_event_query_supported(gl_info))
589             {
590                 /* Half-Life 2 needs this query. It does not render the main
591                  * menu correctly otherwise. Pretend to support it, faking
592                  * this query does not do much harm except potentially
593                  * lowering performance. */
594                 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
595             }
596             query->lpVtbl = &IWineD3DEventQuery_Vtbl;
597             query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
598             if (!query->extendedData)
599             {
600                 ERR("Failed to allocate event query memory.\n");
601                 return E_OUTOFMEMORY;
602             }
603             break;
604
605         case WINED3DQUERYTYPE_VCACHE:
606         case WINED3DQUERYTYPE_RESOURCEMANAGER:
607         case WINED3DQUERYTYPE_VERTEXSTATS:
608         case WINED3DQUERYTYPE_TIMESTAMP:
609         case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
610         case WINED3DQUERYTYPE_TIMESTAMPFREQ:
611         case WINED3DQUERYTYPE_PIPELINETIMINGS:
612         case WINED3DQUERYTYPE_INTERFACETIMINGS:
613         case WINED3DQUERYTYPE_VERTEXTIMINGS:
614         case WINED3DQUERYTYPE_PIXELTIMINGS:
615         case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
616         case WINED3DQUERYTYPE_CACHEUTILIZATION:
617         default:
618             FIXME("Unhandled query type %#x.\n", type);
619             return WINED3DERR_NOTAVAILABLE;
620     }
621
622     query->type = type;
623     query->state = QUERY_CREATED;
624     query->device = device;
625     query->ref = 1;
626
627     return WINED3D_OK;
628 }