wined3d: Fix a silly typo.
[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 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 == NULL)
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     if (ref == 0) {
263         /* Queries are specific to the GL context that created them. Not
264          * deleting the query will obviously leak it, but that's still better
265          * than potentially deleting a different query with the same id in this
266          * context, and (still) leaking the actual query. */
267         if (This->type == WINED3DQUERYTYPE_EVENT)
268         {
269             struct wined3d_event_query *query = This->extendedData;
270             if (query) wined3d_event_query_destroy(query);
271         }
272         else if (This->type == WINED3DQUERYTYPE_OCCLUSION)
273         {
274             struct wined3d_occlusion_query *query = This->extendedData;
275
276             if (query->context) context_free_occlusion_query(query);
277             HeapFree(GetProcessHeap(), 0, This->extendedData);
278         }
279
280         HeapFree(GetProcessHeap(), 0, This);
281     }
282     return ref;
283 }
284
285 static HRESULT  WINAPI IWineD3DOcclusionQueryImpl_GetData(IWineD3DQuery* iface, void* pData, DWORD dwSize, DWORD dwGetDataFlags) {
286     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *) iface;
287     struct wined3d_occlusion_query *query = This->extendedData;
288     IWineD3DDeviceImpl *device = This->device;
289     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
290     struct wined3d_context *context;
291     DWORD* data = pData;
292     GLuint available;
293     GLuint samples;
294     HRESULT res;
295
296     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This, pData, dwSize, dwGetDataFlags);
297
298     if (!query->context) This->state = QUERY_CREATED;
299
300     if (This->state == QUERY_CREATED)
301     {
302         /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
303         TRACE("Query wasn't yet started, returning S_OK\n");
304         if(data) *data = 0;
305         return S_OK;
306     }
307
308     if (This->state == QUERY_BUILDING)
309     {
310         /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
311         TRACE("Query is building, returning S_FALSE\n");
312         return S_FALSE;
313     }
314
315     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
316     {
317         WARN("(%p) : Occlusion queries not supported. Returning 1.\n", This);
318         *data = 1;
319         return S_OK;
320     }
321
322     if (query->context->tid != GetCurrentThreadId())
323     {
324         FIXME("%p Wrong thread, returning 1.\n", This);
325         *data = 1;
326         return S_OK;
327     }
328
329     context = context_acquire(This->device, query->context->current_rt);
330
331     ENTER_GL();
332
333     GL_EXTCALL(glGetQueryObjectuivARB(query->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
334     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
335     TRACE("(%p) : available %d.\n", This, available);
336
337     if (available)
338     {
339         if (data)
340         {
341             GL_EXTCALL(glGetQueryObjectuivARB(query->id, GL_QUERY_RESULT_ARB, &samples));
342             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
343             TRACE("(%p) : Returning %d samples.\n", This, samples);
344             *data = samples;
345         }
346         res = S_OK;
347     }
348     else
349     {
350         res = S_FALSE;
351     }
352
353     LEAVE_GL();
354
355     context_release(context);
356
357     return res;
358 }
359
360 static HRESULT  WINAPI IWineD3DEventQueryImpl_GetData(IWineD3DQuery* iface, void* pData, DWORD dwSize, DWORD dwGetDataFlags) {
361     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *) iface;
362     struct wined3d_event_query *query = This->extendedData;
363     BOOL *data = pData;
364     enum wined3d_event_query_result ret;
365
366     TRACE("(%p) : type D3DQUERY_EVENT, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This, pData, dwSize, dwGetDataFlags);
367
368     if (!pData || !dwSize) return S_OK;
369     if (!query)
370     {
371         WARN("(%p): Event query not supported by GL, reporting GPU idle\n", This);
372         *data = TRUE;
373         return S_OK;
374     }
375
376     ret = wined3d_event_query_test(query, This->device);
377     switch(ret)
378     {
379         case WINED3D_EVENT_QUERY_OK:
380         case WINED3D_EVENT_QUERY_NOT_STARTED:
381             *data = TRUE;
382             break;
383
384         case WINED3D_EVENT_QUERY_WAITING:
385             *data = FALSE;
386             break;
387
388         case WINED3D_EVENT_QUERY_WRONG_THREAD:
389             FIXME("(%p) Wrong thread, reporting GPU idle.\n", This);
390             *data = TRUE;
391             break;
392
393         case WINED3D_EVENT_QUERY_ERROR:
394             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
395             return WINED3DERR_INVALIDCALL;
396     }
397
398     return S_OK;
399 }
400
401 static DWORD  WINAPI IWineD3DEventQueryImpl_GetDataSize(IWineD3DQuery* iface){
402     TRACE("(%p) : type D3DQUERY_EVENT\n", iface);
403
404     return sizeof(BOOL);
405 }
406
407 static DWORD  WINAPI IWineD3DOcclusionQueryImpl_GetDataSize(IWineD3DQuery* iface){
408     TRACE("(%p) : type D3DQUERY_OCCLUSION\n", iface);
409
410     return sizeof(DWORD);
411 }
412
413 static WINED3DQUERYTYPE  WINAPI IWineD3DQueryImpl_GetType(IWineD3DQuery* iface){
414     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
415     return This->type;
416 }
417
418 static HRESULT  WINAPI IWineD3DEventQueryImpl_Issue(IWineD3DQuery* iface,  DWORD dwIssueFlags) {
419     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
420
421     TRACE("(%p) : dwIssueFlags %#x, type D3DQUERY_EVENT\n", This, dwIssueFlags);
422     if (dwIssueFlags & WINED3DISSUE_END)
423     {
424         struct wined3d_event_query *query = This->extendedData;
425
426         /* Faked event query support */
427         if (!query) return WINED3D_OK;
428
429         wined3d_event_query_issue(query, This->device);
430     }
431     else if(dwIssueFlags & WINED3DISSUE_BEGIN)
432     {
433         /* Started implicitly at device creation */
434         ERR("Event query issued with START flag - what to do?\n");
435     }
436
437     if(dwIssueFlags & WINED3DISSUE_BEGIN) {
438         This->state = QUERY_BUILDING;
439     } else {
440         This->state = QUERY_SIGNALLED;
441     }
442
443     return WINED3D_OK;
444 }
445
446 static HRESULT  WINAPI IWineD3DOcclusionQueryImpl_Issue(IWineD3DQuery* iface,  DWORD dwIssueFlags) {
447     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
448     IWineD3DDeviceImpl *device = This->device;
449     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
450
451     if (gl_info->supported[ARB_OCCLUSION_QUERY])
452     {
453         struct wined3d_occlusion_query *query = This->extendedData;
454         struct wined3d_context *context;
455
456         /* This is allowed according to msdn and our tests. Reset the query and restart */
457         if (dwIssueFlags & WINED3DISSUE_BEGIN)
458         {
459             if (This->state == QUERY_BUILDING)
460             {
461                 if (query->context->tid != GetCurrentThreadId())
462                 {
463                     FIXME("Wrong thread, can't restart query.\n");
464
465                     context_free_occlusion_query(query);
466                     context = context_acquire(This->device, NULL);
467                     context_alloc_occlusion_query(context, query);
468                 }
469                 else
470                 {
471                     context = context_acquire(This->device, query->context->current_rt);
472
473                     ENTER_GL();
474                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
475                     checkGLcall("glEndQuery()");
476                     LEAVE_GL();
477                 }
478             }
479             else
480             {
481                 if (query->context) context_free_occlusion_query(query);
482                 context = context_acquire(This->device, NULL);
483                 context_alloc_occlusion_query(context, query);
484             }
485
486             ENTER_GL();
487             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, query->id));
488             checkGLcall("glBeginQuery()");
489             LEAVE_GL();
490
491             context_release(context);
492         }
493         if (dwIssueFlags & WINED3DISSUE_END) {
494             /* Msdn says _END on a non-building occlusion query returns an error, but
495              * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
496              * generating an error
497              */
498             if (This->state == QUERY_BUILDING)
499             {
500                 if (query->context->tid != GetCurrentThreadId())
501                 {
502                     FIXME("Wrong thread, can't end query.\n");
503                 }
504                 else
505                 {
506                     context = context_acquire(This->device, query->context->current_rt);
507
508                     ENTER_GL();
509                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
510                     checkGLcall("glEndQuery()");
511                     LEAVE_GL();
512
513                     context_release(context);
514                 }
515             }
516         }
517     } else {
518         FIXME("(%p) : Occlusion queries not supported\n", This);
519     }
520
521     if(dwIssueFlags & WINED3DISSUE_BEGIN) {
522         This->state = QUERY_BUILDING;
523     } else {
524         This->state = QUERY_SIGNALLED;
525     }
526     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
527 }
528
529 static const IWineD3DQueryVtbl IWineD3DEventQuery_Vtbl =
530 {
531     /*** IUnknown methods ***/
532     IWineD3DQueryImpl_QueryInterface,
533     IWineD3DQueryImpl_AddRef,
534     IWineD3DQueryImpl_Release,
535     /*** IWineD3Dquery methods ***/
536     IWineD3DEventQueryImpl_GetData,
537     IWineD3DEventQueryImpl_GetDataSize,
538     IWineD3DQueryImpl_GetType,
539     IWineD3DEventQueryImpl_Issue
540 };
541
542 static const IWineD3DQueryVtbl IWineD3DOcclusionQuery_Vtbl =
543 {
544     /*** IUnknown methods ***/
545     IWineD3DQueryImpl_QueryInterface,
546     IWineD3DQueryImpl_AddRef,
547     IWineD3DQueryImpl_Release,
548     /*** IWineD3Dquery methods ***/
549     IWineD3DOcclusionQueryImpl_GetData,
550     IWineD3DOcclusionQueryImpl_GetDataSize,
551     IWineD3DQueryImpl_GetType,
552     IWineD3DOcclusionQueryImpl_Issue
553 };
554
555 HRESULT query_init(IWineD3DQueryImpl *query, IWineD3DDeviceImpl *device, WINED3DQUERYTYPE type)
556 {
557     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
558
559     switch (type)
560     {
561         case WINED3DQUERYTYPE_OCCLUSION:
562             TRACE("Occlusion query.\n");
563             if (!gl_info->supported[ARB_OCCLUSION_QUERY])
564             {
565                 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
566                 return WINED3DERR_NOTAVAILABLE;
567             }
568             query->lpVtbl = &IWineD3DOcclusionQuery_Vtbl;
569             query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
570             if (!query->extendedData)
571             {
572                 ERR("Failed to allocate occlusion query extended data.\n");
573                 return E_OUTOFMEMORY;
574             }
575             ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
576             break;
577
578         case WINED3DQUERYTYPE_EVENT:
579             TRACE("Event query.\n");
580             if (!wined3d_event_query_supported(gl_info))
581             {
582                 /* Half-Life 2 needs this query. It does not render the main
583                  * menu correctly otherwise. Pretend to support it, faking
584                  * this query does not do much harm except potentially
585                  * lowering performance. */
586                 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
587             }
588             query->lpVtbl = &IWineD3DEventQuery_Vtbl;
589             query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
590             if (!query->extendedData)
591             {
592                 ERR("Failed to allocate event query memory.\n");
593                 return E_OUTOFMEMORY;
594             }
595             break;
596
597         case WINED3DQUERYTYPE_VCACHE:
598         case WINED3DQUERYTYPE_RESOURCEMANAGER:
599         case WINED3DQUERYTYPE_VERTEXSTATS:
600         case WINED3DQUERYTYPE_TIMESTAMP:
601         case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
602         case WINED3DQUERYTYPE_TIMESTAMPFREQ:
603         case WINED3DQUERYTYPE_PIPELINETIMINGS:
604         case WINED3DQUERYTYPE_INTERFACETIMINGS:
605         case WINED3DQUERYTYPE_VERTEXTIMINGS:
606         case WINED3DQUERYTYPE_PIXELTIMINGS:
607         case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
608         case WINED3DQUERYTYPE_CACHEUTILIZATION:
609         default:
610             FIXME("Unhandled query type %#x.\n", type);
611             return WINED3DERR_NOTAVAILABLE;
612     }
613
614     query->type = type;
615     query->state = QUERY_CREATED;
616     query->device = device;
617     query->ref = 1;
618
619     return WINED3D_OK;
620 }