oleaut32: Add a test for loading/saving an empty picture.
[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 "wine/port.h"
24 #include "wined3d_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
27
28 BOOL wined3d_event_query_supported(const struct wined3d_gl_info *gl_info)
29 {
30     return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE];
31 }
32
33 void wined3d_event_query_destroy(struct wined3d_event_query *query)
34 {
35     if (query->context) context_free_event_query(query);
36     HeapFree(GetProcessHeap(), 0, query);
37 }
38
39 static enum wined3d_event_query_result wined3d_event_query_test(const struct wined3d_event_query *query,
40         const struct wined3d_device *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(const struct wined3d_event_query *query,
115         const struct wined3d_device *device)
116 {
117     struct wined3d_context *context;
118     const struct wined3d_gl_info *gl_info;
119     enum wined3d_event_query_result ret;
120
121     TRACE("(%p)\n", query);
122
123     if (!query->context)
124     {
125         TRACE("Query not started\n");
126         return WINED3D_EVENT_QUERY_NOT_STARTED;
127     }
128     gl_info = query->context->gl_info;
129
130     if (query->context->tid != GetCurrentThreadId() && !gl_info->supported[ARB_SYNC])
131     {
132         /* A glFinish does not reliably wait for draws in other contexts. The caller has
133          * to find its own way to cope with the thread switch
134          */
135         WARN("Event query finished from wrong thread\n");
136         return WINED3D_EVENT_QUERY_WRONG_THREAD;
137     }
138
139     context = context_acquire(device, query->context->current_rt);
140
141     ENTER_GL();
142     if (gl_info->supported[ARB_SYNC])
143     {
144         /* Apple seems to be into arbitrary limits, and timeouts larger than
145          * 0xfffffffffffffbff immediately return GL_TIMEOUT_EXPIRED. We don't
146          * really care and can live with waiting a few μs less. (OS X 10.7.4). */
147         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, GL_SYNC_FLUSH_COMMANDS_BIT, ~(GLuint64)0xffff));
148         checkGLcall("glClientWaitSync");
149
150         switch (gl_ret)
151         {
152             case GL_ALREADY_SIGNALED:
153             case GL_CONDITION_SATISFIED:
154                 ret = WINED3D_EVENT_QUERY_OK;
155                 break;
156
157                 /* We don't expect a timeout for a ~584 year wait */
158             default:
159                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
160                 ret = WINED3D_EVENT_QUERY_ERROR;
161         }
162     }
163     else if (context->gl_info->supported[APPLE_FENCE])
164     {
165         GL_EXTCALL(glFinishFenceAPPLE(query->object.id));
166         checkGLcall("glFinishFenceAPPLE");
167         ret = WINED3D_EVENT_QUERY_OK;
168     }
169     else if (context->gl_info->supported[NV_FENCE])
170     {
171         GL_EXTCALL(glFinishFenceNV(query->object.id));
172         checkGLcall("glFinishFenceNV");
173         ret = WINED3D_EVENT_QUERY_OK;
174     }
175     else
176     {
177         ERR("Event query created without GL support\n");
178         ret = WINED3D_EVENT_QUERY_ERROR;
179     }
180     LEAVE_GL();
181
182     context_release(context);
183     return ret;
184 }
185
186 void wined3d_event_query_issue(struct wined3d_event_query *query, const struct wined3d_device *device)
187 {
188     const struct wined3d_gl_info *gl_info;
189     struct wined3d_context *context;
190
191     if (query->context)
192     {
193         if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
194         {
195             context_free_event_query(query);
196             context = context_acquire(device, NULL);
197             context_alloc_event_query(context, query);
198         }
199         else
200         {
201             context = context_acquire(device, query->context->current_rt);
202         }
203     }
204     else
205     {
206         context = context_acquire(device, NULL);
207         context_alloc_event_query(context, query);
208     }
209
210     gl_info = context->gl_info;
211
212     ENTER_GL();
213
214     if (gl_info->supported[ARB_SYNC])
215     {
216         if (query->object.sync) GL_EXTCALL(glDeleteSync(query->object.sync));
217         checkGLcall("glDeleteSync");
218         query->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
219         checkGLcall("glFenceSync");
220     }
221     else if (gl_info->supported[APPLE_FENCE])
222     {
223         GL_EXTCALL(glSetFenceAPPLE(query->object.id));
224         checkGLcall("glSetFenceAPPLE");
225     }
226     else if (gl_info->supported[NV_FENCE])
227     {
228         GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
229         checkGLcall("glSetFenceNV");
230     }
231
232     LEAVE_GL();
233
234     context_release(context);
235 }
236
237 ULONG CDECL wined3d_query_incref(struct wined3d_query *query)
238 {
239     ULONG refcount = InterlockedIncrement(&query->ref);
240
241     TRACE("%p increasing refcount to %u.\n", query, refcount);
242
243     return refcount;
244 }
245
246 ULONG CDECL wined3d_query_decref(struct wined3d_query *query)
247 {
248     ULONG refcount = InterlockedDecrement(&query->ref);
249
250     TRACE("%p decreasing refcount to %u.\n", query, refcount);
251
252     if (!refcount)
253     {
254         /* Queries are specific to the GL context that created them. Not
255          * deleting the query will obviously leak it, but that's still better
256          * than potentially deleting a different query with the same id in this
257          * context, and (still) leaking the actual query. */
258         if (query->type == WINED3D_QUERY_TYPE_EVENT)
259         {
260             struct wined3d_event_query *event_query = query->extendedData;
261             if (event_query) wined3d_event_query_destroy(event_query);
262         }
263         else if (query->type == WINED3D_QUERY_TYPE_OCCLUSION)
264         {
265             struct wined3d_occlusion_query *oq = query->extendedData;
266
267             if (oq->context) context_free_occlusion_query(oq);
268             HeapFree(GetProcessHeap(), 0, query->extendedData);
269         }
270
271         HeapFree(GetProcessHeap(), 0, query);
272     }
273
274     return refcount;
275 }
276
277 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
278         void *data, UINT data_size, DWORD flags)
279 {
280     TRACE("query %p, data %p, data_size %u, flags %#x.\n",
281             query, data, data_size, flags);
282
283     return query->query_ops->query_get_data(query, data, data_size, flags);
284 }
285
286 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query)
287 {
288     TRACE("query %p.\n", query);
289
290     return query->data_size;
291 }
292
293 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
294 {
295     TRACE("query %p, flags %#x.\n", query, flags);
296
297     return query->query_ops->query_issue(query, flags);
298 }
299
300 static HRESULT wined3d_occlusion_query_ops_get_data(struct wined3d_query *query,
301         void *pData, DWORD dwSize, DWORD flags)
302 {
303     struct wined3d_occlusion_query *oq = query->extendedData;
304     struct wined3d_device *device = query->device;
305     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
306     struct wined3d_context *context;
307     DWORD* data = pData;
308     GLuint available;
309     GLuint samples;
310     HRESULT res;
311
312     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
313
314     if (!oq->context)
315         query->state = QUERY_CREATED;
316
317     if (query->state == QUERY_CREATED)
318     {
319         /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
320         TRACE("Query wasn't yet started, returning S_OK\n");
321         if(data) *data = 0;
322         return S_OK;
323     }
324
325     if (query->state == QUERY_BUILDING)
326     {
327         /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
328         TRACE("Query is building, returning S_FALSE\n");
329         return S_FALSE;
330     }
331
332     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
333     {
334         WARN("%p Occlusion queries not supported. Returning 1.\n", query);
335         *data = 1;
336         return S_OK;
337     }
338
339     if (oq->context->tid != GetCurrentThreadId())
340     {
341         FIXME("%p Wrong thread, returning 1.\n", query);
342         *data = 1;
343         return S_OK;
344     }
345
346     context = context_acquire(query->device, oq->context->current_rt);
347
348     ENTER_GL();
349
350     GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
351     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
352     TRACE("available %#x.\n", available);
353
354     if (available)
355     {
356         if (data)
357         {
358             GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_ARB, &samples));
359             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
360             TRACE("Returning %d samples.\n", samples);
361             *data = samples;
362         }
363         res = S_OK;
364     }
365     else
366     {
367         res = S_FALSE;
368     }
369
370     LEAVE_GL();
371
372     context_release(context);
373
374     return res;
375 }
376
377 static HRESULT wined3d_event_query_ops_get_data(struct wined3d_query *query,
378         void *pData, DWORD dwSize, DWORD flags)
379 {
380     struct wined3d_event_query *event_query = query->extendedData;
381     BOOL *data = pData;
382     enum wined3d_event_query_result ret;
383
384     TRACE("query %p, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
385
386     if (!pData || !dwSize) return S_OK;
387     if (!event_query)
388     {
389         WARN("Event query not supported by GL, reporting GPU idle.\n");
390         *data = TRUE;
391         return S_OK;
392     }
393
394     ret = wined3d_event_query_test(event_query, query->device);
395     switch(ret)
396     {
397         case WINED3D_EVENT_QUERY_OK:
398         case WINED3D_EVENT_QUERY_NOT_STARTED:
399             *data = TRUE;
400             break;
401
402         case WINED3D_EVENT_QUERY_WAITING:
403             *data = FALSE;
404             break;
405
406         case WINED3D_EVENT_QUERY_WRONG_THREAD:
407             FIXME("(%p) Wrong thread, reporting GPU idle.\n", query);
408             *data = TRUE;
409             break;
410
411         case WINED3D_EVENT_QUERY_ERROR:
412             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
413             return WINED3DERR_INVALIDCALL;
414     }
415
416     return S_OK;
417 }
418
419 enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query *query)
420 {
421     TRACE("query %p.\n", query);
422
423     return query->type;
424 }
425
426 static HRESULT wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
427 {
428     TRACE("query %p, flags %#x.\n", query, flags);
429
430     TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", query, flags);
431     if (flags & WINED3DISSUE_END)
432     {
433         struct wined3d_event_query *event_query = query->extendedData;
434
435         /* Faked event query support */
436         if (!event_query) return WINED3D_OK;
437
438         wined3d_event_query_issue(event_query, query->device);
439     }
440     else if (flags & WINED3DISSUE_BEGIN)
441     {
442         /* Started implicitly at device creation */
443         ERR("Event query issued with START flag - what to do?\n");
444     }
445
446     if (flags & WINED3DISSUE_BEGIN)
447         query->state = QUERY_BUILDING;
448     else
449         query->state = QUERY_SIGNALLED;
450
451     return WINED3D_OK;
452 }
453
454 static HRESULT wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
455 {
456     struct wined3d_device *device = query->device;
457     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
458
459     TRACE("query %p, flags %#x.\n", query, flags);
460
461     if (gl_info->supported[ARB_OCCLUSION_QUERY])
462     {
463         struct wined3d_occlusion_query *oq = query->extendedData;
464         struct wined3d_context *context;
465
466         /* This is allowed according to msdn and our tests. Reset the query and restart */
467         if (flags & WINED3DISSUE_BEGIN)
468         {
469             if (query->state == QUERY_BUILDING)
470             {
471                 if (oq->context->tid != GetCurrentThreadId())
472                 {
473                     FIXME("Wrong thread, can't restart query.\n");
474
475                     context_free_occlusion_query(oq);
476                     context = context_acquire(query->device, NULL);
477                     context_alloc_occlusion_query(context, oq);
478                 }
479                 else
480                 {
481                     context = context_acquire(query->device, oq->context->current_rt);
482
483                     ENTER_GL();
484                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
485                     checkGLcall("glEndQuery()");
486                     LEAVE_GL();
487                 }
488             }
489             else
490             {
491                 if (oq->context) context_free_occlusion_query(oq);
492                 context = context_acquire(query->device, NULL);
493                 context_alloc_occlusion_query(context, oq);
494             }
495
496             ENTER_GL();
497             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, oq->id));
498             checkGLcall("glBeginQuery()");
499             LEAVE_GL();
500
501             context_release(context);
502         }
503         if (flags & WINED3DISSUE_END)
504         {
505             /* Msdn says _END on a non-building occlusion query returns an error, but
506              * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
507              * generating an error
508              */
509             if (query->state == QUERY_BUILDING)
510             {
511                 if (oq->context->tid != GetCurrentThreadId())
512                 {
513                     FIXME("Wrong thread, can't end query.\n");
514                 }
515                 else
516                 {
517                     context = context_acquire(query->device, oq->context->current_rt);
518
519                     ENTER_GL();
520                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
521                     checkGLcall("glEndQuery()");
522                     LEAVE_GL();
523
524                     context_release(context);
525                 }
526             }
527         }
528     }
529     else
530     {
531         FIXME("%p Occlusion queries not supported.\n", query);
532     }
533
534     if (flags & WINED3DISSUE_BEGIN)
535         query->state = QUERY_BUILDING;
536     else
537         query->state = QUERY_SIGNALLED;
538
539     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
540 }
541
542 static const struct wined3d_query_ops event_query_ops =
543 {
544     wined3d_event_query_ops_get_data,
545     wined3d_event_query_ops_issue,
546 };
547
548 static const struct wined3d_query_ops occlusion_query_ops =
549 {
550     wined3d_occlusion_query_ops_get_data,
551     wined3d_occlusion_query_ops_issue,
552 };
553
554 static HRESULT query_init(struct wined3d_query *query, struct wined3d_device *device, enum wined3d_query_type type)
555 {
556     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
557
558     switch (type)
559     {
560         case WINED3D_QUERY_TYPE_OCCLUSION:
561             TRACE("Occlusion query.\n");
562             if (!gl_info->supported[ARB_OCCLUSION_QUERY])
563             {
564                 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
565                 return WINED3DERR_NOTAVAILABLE;
566             }
567             query->query_ops = &occlusion_query_ops;
568             query->data_size = sizeof(DWORD);
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 WINED3D_QUERY_TYPE_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->query_ops = &event_query_ops;
589             query->data_size = sizeof(BOOL);
590             query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
591             if (!query->extendedData)
592             {
593                 ERR("Failed to allocate event query memory.\n");
594                 return E_OUTOFMEMORY;
595             }
596             break;
597
598         case WINED3D_QUERY_TYPE_VCACHE:
599         case WINED3D_QUERY_TYPE_RESOURCE_MANAGER:
600         case WINED3D_QUERY_TYPE_VERTEX_STATS:
601         case WINED3D_QUERY_TYPE_TIMESTAMP:
602         case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT:
603         case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ:
604         case WINED3D_QUERY_TYPE_PIPELINE_TIMINGS:
605         case WINED3D_QUERY_TYPE_INTERFACE_TIMINGS:
606         case WINED3D_QUERY_TYPE_VERTEX_TIMINGS:
607         case WINED3D_QUERY_TYPE_PIXEL_TIMINGS:
608         case WINED3D_QUERY_TYPE_BANDWIDTH_TIMINGS:
609         case WINED3D_QUERY_TYPE_CACHE_UTILIZATION:
610         default:
611             FIXME("Unhandled query type %#x.\n", type);
612             return WINED3DERR_NOTAVAILABLE;
613     }
614
615     query->type = type;
616     query->state = QUERY_CREATED;
617     query->device = device;
618     query->ref = 1;
619
620     return WINED3D_OK;
621 }
622
623 HRESULT CDECL wined3d_query_create(struct wined3d_device *device,
624         enum wined3d_query_type type, struct wined3d_query **query)
625 {
626     struct wined3d_query *object;
627     HRESULT hr;
628
629     TRACE("device %p, type %#x, query %p.\n", device, type, query);
630
631     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
632     if (!object)
633     {
634         ERR("Failed to allocate query memory.\n");
635         return E_OUTOFMEMORY;
636     }
637
638     hr = query_init(object, device, type);
639     if (FAILED(hr))
640     {
641         WARN("Failed to initialize query, hr %#x.\n", hr);
642         HeapFree(GetProcessHeap(), 0, object);
643         return hr;
644     }
645
646     TRACE("Created query %p.\n", object);
647     *query = object;
648
649     return WINED3D_OK;
650 }