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