wined3d: Test more thoroughly for post-pixelshader blending support, try on more...
[wine] / dlls / wined3d / query.c
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 "wined3d_private.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
26
27 BOOL wined3d_event_query_supported(const struct wined3d_gl_info *gl_info)
28 {
29     return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE];
30 }
31
32 void wined3d_event_query_destroy(struct wined3d_event_query *query)
33 {
34     if (query->context) context_free_event_query(query);
35     HeapFree(GetProcessHeap(), 0, query);
36 }
37
38 static enum wined3d_event_query_result wined3d_event_query_test(const struct wined3d_event_query *query,
39         const struct wined3d_device *device)
40 {
41     struct wined3d_context *context;
42     const struct wined3d_gl_info *gl_info;
43     enum wined3d_event_query_result ret;
44     BOOL fence_result;
45
46     TRACE("(%p) : device %p\n", query, device);
47
48     if (!query->context)
49     {
50         TRACE("Query not started\n");
51         return WINED3D_EVENT_QUERY_NOT_STARTED;
52     }
53
54     if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
55     {
56         WARN("Event query tested from wrong thread\n");
57         return WINED3D_EVENT_QUERY_WRONG_THREAD;
58     }
59
60     context = context_acquire(device, query->context->current_rt);
61     gl_info = context->gl_info;
62
63     ENTER_GL();
64
65     if (gl_info->supported[ARB_SYNC])
66     {
67         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, 0));
68         checkGLcall("glClientWaitSync");
69
70         switch (gl_ret)
71         {
72             case GL_ALREADY_SIGNALED:
73             case GL_CONDITION_SATISFIED:
74                 ret = WINED3D_EVENT_QUERY_OK;
75                 break;
76
77             case GL_TIMEOUT_EXPIRED:
78                 ret = WINED3D_EVENT_QUERY_WAITING;
79                 break;
80
81             case GL_WAIT_FAILED:
82             default:
83                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
84                 ret = WINED3D_EVENT_QUERY_ERROR;
85         }
86     }
87     else if (gl_info->supported[APPLE_FENCE])
88     {
89         fence_result = GL_EXTCALL(glTestFenceAPPLE(query->object.id));
90         checkGLcall("glTestFenceAPPLE");
91         if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
92         else ret = WINED3D_EVENT_QUERY_WAITING;
93     }
94     else if (gl_info->supported[NV_FENCE])
95     {
96         fence_result = GL_EXTCALL(glTestFenceNV(query->object.id));
97         checkGLcall("glTestFenceNV");
98         if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
99         else ret = WINED3D_EVENT_QUERY_WAITING;
100     }
101     else
102     {
103         ERR("Event query created despite lack of GL support\n");
104         ret = WINED3D_EVENT_QUERY_ERROR;
105     }
106
107     LEAVE_GL();
108
109     context_release(context);
110     return ret;
111 }
112
113 enum wined3d_event_query_result wined3d_event_query_finish(const struct wined3d_event_query *query,
114         const struct wined3d_device *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, const struct wined3d_device *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 ULONG CDECL wined3d_query_incref(struct wined3d_query *query)
234 {
235     ULONG refcount = InterlockedIncrement(&query->ref);
236
237     TRACE("%p increasing refcount to %u.\n", query, refcount);
238
239     return refcount;
240 }
241
242 ULONG CDECL wined3d_query_decref(struct wined3d_query *query)
243 {
244     ULONG refcount = InterlockedDecrement(&query->ref);
245
246     TRACE("%p decreasing refcount to %u.\n", query, refcount);
247
248     if (!refcount)
249     {
250         /* Queries are specific to the GL context that created them. Not
251          * deleting the query will obviously leak it, but that's still better
252          * than potentially deleting a different query with the same id in this
253          * context, and (still) leaking the actual query. */
254         if (query->type == WINED3DQUERYTYPE_EVENT)
255         {
256             struct wined3d_event_query *event_query = query->extendedData;
257             if (event_query) wined3d_event_query_destroy(event_query);
258         }
259         else if (query->type == WINED3DQUERYTYPE_OCCLUSION)
260         {
261             struct wined3d_occlusion_query *oq = query->extendedData;
262
263             if (oq->context) context_free_occlusion_query(oq);
264             HeapFree(GetProcessHeap(), 0, query->extendedData);
265         }
266
267         HeapFree(GetProcessHeap(), 0, query);
268     }
269
270     return refcount;
271 }
272
273 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
274         void *data, UINT data_size, DWORD flags)
275 {
276     TRACE("query %p, data %p, data_size %u, flags %#x.\n",
277             query, data, data_size, flags);
278
279     return query->query_ops->query_get_data(query, data, data_size, flags);
280 }
281
282 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query)
283 {
284     TRACE("query %p.\n", query);
285
286     return query->data_size;
287 }
288
289 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
290 {
291     TRACE("query %p, flags %#x.\n", query, flags);
292
293     return query->query_ops->query_issue(query, flags);
294 }
295
296 static HRESULT wined3d_occlusion_query_ops_get_data(struct wined3d_query *query,
297         void *pData, DWORD dwSize, DWORD flags)
298 {
299     struct wined3d_occlusion_query *oq = query->extendedData;
300     struct wined3d_device *device = query->device;
301     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
302     struct wined3d_context *context;
303     DWORD* data = pData;
304     GLuint available;
305     GLuint samples;
306     HRESULT res;
307
308     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
309
310     if (!oq->context)
311         query->state = QUERY_CREATED;
312
313     if (query->state == QUERY_CREATED)
314     {
315         /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
316         TRACE("Query wasn't yet started, returning S_OK\n");
317         if(data) *data = 0;
318         return S_OK;
319     }
320
321     if (query->state == QUERY_BUILDING)
322     {
323         /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
324         TRACE("Query is building, returning S_FALSE\n");
325         return S_FALSE;
326     }
327
328     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
329     {
330         WARN("%p Occlusion queries not supported. Returning 1.\n", query);
331         *data = 1;
332         return S_OK;
333     }
334
335     if (oq->context->tid != GetCurrentThreadId())
336     {
337         FIXME("%p Wrong thread, returning 1.\n", query);
338         *data = 1;
339         return S_OK;
340     }
341
342     context = context_acquire(query->device, oq->context->current_rt);
343
344     ENTER_GL();
345
346     GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
347     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
348     TRACE("available %#x.\n", available);
349
350     if (available)
351     {
352         if (data)
353         {
354             GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_ARB, &samples));
355             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
356             TRACE("Returning %d samples.\n", samples);
357             *data = samples;
358         }
359         res = S_OK;
360     }
361     else
362     {
363         res = S_FALSE;
364     }
365
366     LEAVE_GL();
367
368     context_release(context);
369
370     return res;
371 }
372
373 static HRESULT wined3d_event_query_ops_get_data(struct wined3d_query *query,
374         void *pData, DWORD dwSize, DWORD flags)
375 {
376     struct wined3d_event_query *event_query = query->extendedData;
377     BOOL *data = pData;
378     enum wined3d_event_query_result ret;
379
380     TRACE("query %p, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
381
382     if (!pData || !dwSize) return S_OK;
383     if (!event_query)
384     {
385         WARN("Event query not supported by GL, reporting GPU idle.\n");
386         *data = TRUE;
387         return S_OK;
388     }
389
390     ret = wined3d_event_query_test(event_query, query->device);
391     switch(ret)
392     {
393         case WINED3D_EVENT_QUERY_OK:
394         case WINED3D_EVENT_QUERY_NOT_STARTED:
395             *data = TRUE;
396             break;
397
398         case WINED3D_EVENT_QUERY_WAITING:
399             *data = FALSE;
400             break;
401
402         case WINED3D_EVENT_QUERY_WRONG_THREAD:
403             FIXME("(%p) Wrong thread, reporting GPU idle.\n", query);
404             *data = TRUE;
405             break;
406
407         case WINED3D_EVENT_QUERY_ERROR:
408             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
409             return WINED3DERR_INVALIDCALL;
410     }
411
412     return S_OK;
413 }
414
415 WINED3DQUERYTYPE CDECL wined3d_query_get_type(const struct wined3d_query *query)
416 {
417     TRACE("query %p.\n", query);
418
419     return query->type;
420 }
421
422 static HRESULT wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
423 {
424     TRACE("query %p, flags %#x.\n", query, flags);
425
426     TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", query, flags);
427     if (flags & WINED3DISSUE_END)
428     {
429         struct wined3d_event_query *event_query = query->extendedData;
430
431         /* Faked event query support */
432         if (!event_query) return WINED3D_OK;
433
434         wined3d_event_query_issue(event_query, query->device);
435     }
436     else if (flags & WINED3DISSUE_BEGIN)
437     {
438         /* Started implicitly at device creation */
439         ERR("Event query issued with START flag - what to do?\n");
440     }
441
442     if (flags & WINED3DISSUE_BEGIN)
443         query->state = QUERY_BUILDING;
444     else
445         query->state = QUERY_SIGNALLED;
446
447     return WINED3D_OK;
448 }
449
450 static HRESULT wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
451 {
452     struct wined3d_device *device = query->device;
453     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
454
455     TRACE("query %p, flags %#x.\n", query, flags);
456
457     if (gl_info->supported[ARB_OCCLUSION_QUERY])
458     {
459         struct wined3d_occlusion_query *oq = query->extendedData;
460         struct wined3d_context *context;
461
462         /* This is allowed according to msdn and our tests. Reset the query and restart */
463         if (flags & WINED3DISSUE_BEGIN)
464         {
465             if (query->state == QUERY_BUILDING)
466             {
467                 if (oq->context->tid != GetCurrentThreadId())
468                 {
469                     FIXME("Wrong thread, can't restart query.\n");
470
471                     context_free_occlusion_query(oq);
472                     context = context_acquire(query->device, NULL);
473                     context_alloc_occlusion_query(context, oq);
474                 }
475                 else
476                 {
477                     context = context_acquire(query->device, oq->context->current_rt);
478
479                     ENTER_GL();
480                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
481                     checkGLcall("glEndQuery()");
482                     LEAVE_GL();
483                 }
484             }
485             else
486             {
487                 if (oq->context) context_free_occlusion_query(oq);
488                 context = context_acquire(query->device, NULL);
489                 context_alloc_occlusion_query(context, oq);
490             }
491
492             ENTER_GL();
493             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, oq->id));
494             checkGLcall("glBeginQuery()");
495             LEAVE_GL();
496
497             context_release(context);
498         }
499         if (flags & WINED3DISSUE_END)
500         {
501             /* Msdn says _END on a non-building occlusion query returns an error, but
502              * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
503              * generating an error
504              */
505             if (query->state == QUERY_BUILDING)
506             {
507                 if (oq->context->tid != GetCurrentThreadId())
508                 {
509                     FIXME("Wrong thread, can't end query.\n");
510                 }
511                 else
512                 {
513                     context = context_acquire(query->device, oq->context->current_rt);
514
515                     ENTER_GL();
516                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
517                     checkGLcall("glEndQuery()");
518                     LEAVE_GL();
519
520                     context_release(context);
521                 }
522             }
523         }
524     }
525     else
526     {
527         FIXME("%p Occlusion queries not supported.\n", query);
528     }
529
530     if (flags & WINED3DISSUE_BEGIN)
531         query->state = QUERY_BUILDING;
532     else
533         query->state = QUERY_SIGNALLED;
534
535     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
536 }
537
538 static const struct wined3d_query_ops event_query_ops =
539 {
540     wined3d_event_query_ops_get_data,
541     wined3d_event_query_ops_issue,
542 };
543
544 static const struct wined3d_query_ops occlusion_query_ops =
545 {
546     wined3d_occlusion_query_ops_get_data,
547     wined3d_occlusion_query_ops_issue,
548 };
549
550 static HRESULT query_init(struct wined3d_query *query, struct wined3d_device *device, WINED3DQUERYTYPE type)
551 {
552     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
553
554     switch (type)
555     {
556         case WINED3DQUERYTYPE_OCCLUSION:
557             TRACE("Occlusion query.\n");
558             if (!gl_info->supported[ARB_OCCLUSION_QUERY])
559             {
560                 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
561                 return WINED3DERR_NOTAVAILABLE;
562             }
563             query->query_ops = &occlusion_query_ops;
564             query->data_size = sizeof(DWORD);
565             query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
566             if (!query->extendedData)
567             {
568                 ERR("Failed to allocate occlusion query extended data.\n");
569                 return E_OUTOFMEMORY;
570             }
571             ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
572             break;
573
574         case WINED3DQUERYTYPE_EVENT:
575             TRACE("Event query.\n");
576             if (!wined3d_event_query_supported(gl_info))
577             {
578                 /* Half-Life 2 needs this query. It does not render the main
579                  * menu correctly otherwise. Pretend to support it, faking
580                  * this query does not do much harm except potentially
581                  * lowering performance. */
582                 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
583             }
584             query->query_ops = &event_query_ops;
585             query->data_size = sizeof(BOOL);
586             query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
587             if (!query->extendedData)
588             {
589                 ERR("Failed to allocate event query memory.\n");
590                 return E_OUTOFMEMORY;
591             }
592             break;
593
594         case WINED3DQUERYTYPE_VCACHE:
595         case WINED3DQUERYTYPE_RESOURCEMANAGER:
596         case WINED3DQUERYTYPE_VERTEXSTATS:
597         case WINED3DQUERYTYPE_TIMESTAMP:
598         case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
599         case WINED3DQUERYTYPE_TIMESTAMPFREQ:
600         case WINED3DQUERYTYPE_PIPELINETIMINGS:
601         case WINED3DQUERYTYPE_INTERFACETIMINGS:
602         case WINED3DQUERYTYPE_VERTEXTIMINGS:
603         case WINED3DQUERYTYPE_PIXELTIMINGS:
604         case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
605         case WINED3DQUERYTYPE_CACHEUTILIZATION:
606         default:
607             FIXME("Unhandled query type %#x.\n", type);
608             return WINED3DERR_NOTAVAILABLE;
609     }
610
611     query->type = type;
612     query->state = QUERY_CREATED;
613     query->device = device;
614     query->ref = 1;
615
616     return WINED3D_OK;
617 }
618
619 HRESULT CDECL wined3d_query_create(struct wined3d_device *device,
620         WINED3DQUERYTYPE type, struct wined3d_query **query)
621 {
622     struct wined3d_query *object;
623     HRESULT hr;
624
625     TRACE("device %p, type %#x, query %p.\n", device, type, query);
626
627     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
628     if (!object)
629     {
630         ERR("Failed to allocate query memory.\n");
631         return E_OUTOFMEMORY;
632     }
633
634     hr = query_init(object, device, type);
635     if (FAILED(hr))
636     {
637         WARN("Failed to initialize query, hr %#x.\n", hr);
638         HeapFree(GetProcessHeap(), 0, object);
639         return hr;
640     }
641
642     TRACE("Created query %p.\n", object);
643     *query = object;
644
645     return WINED3D_OK;
646 }