winex11: Move wglShareLists to the internal OpenGL extension functions.
[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         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, ~(GLuint64)0));
145         checkGLcall("glClientWaitSync");
146
147         switch (gl_ret)
148         {
149             case GL_ALREADY_SIGNALED:
150             case GL_CONDITION_SATISFIED:
151                 ret = WINED3D_EVENT_QUERY_OK;
152                 break;
153
154                 /* We don't expect a timeout for a ~584 year wait */
155             default:
156                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
157                 ret = WINED3D_EVENT_QUERY_ERROR;
158         }
159     }
160     else if (context->gl_info->supported[APPLE_FENCE])
161     {
162         GL_EXTCALL(glFinishFenceAPPLE(query->object.id));
163         checkGLcall("glFinishFenceAPPLE");
164         ret = WINED3D_EVENT_QUERY_OK;
165     }
166     else if (context->gl_info->supported[NV_FENCE])
167     {
168         GL_EXTCALL(glFinishFenceNV(query->object.id));
169         checkGLcall("glFinishFenceNV");
170         ret = WINED3D_EVENT_QUERY_OK;
171     }
172     else
173     {
174         ERR("Event query created without GL support\n");
175         ret = WINED3D_EVENT_QUERY_ERROR;
176     }
177     LEAVE_GL();
178
179     context_release(context);
180     return ret;
181 }
182
183 void wined3d_event_query_issue(struct wined3d_event_query *query, const struct wined3d_device *device)
184 {
185     const struct wined3d_gl_info *gl_info;
186     struct wined3d_context *context;
187
188     if (query->context)
189     {
190         if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
191         {
192             context_free_event_query(query);
193             context = context_acquire(device, NULL);
194             context_alloc_event_query(context, query);
195         }
196         else
197         {
198             context = context_acquire(device, query->context->current_rt);
199         }
200     }
201     else
202     {
203         context = context_acquire(device, NULL);
204         context_alloc_event_query(context, query);
205     }
206
207     gl_info = context->gl_info;
208
209     ENTER_GL();
210
211     if (gl_info->supported[ARB_SYNC])
212     {
213         if (query->object.sync) GL_EXTCALL(glDeleteSync(query->object.sync));
214         checkGLcall("glDeleteSync");
215         query->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
216         checkGLcall("glFenceSync");
217     }
218     else if (gl_info->supported[APPLE_FENCE])
219     {
220         GL_EXTCALL(glSetFenceAPPLE(query->object.id));
221         checkGLcall("glSetFenceAPPLE");
222     }
223     else if (gl_info->supported[NV_FENCE])
224     {
225         GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
226         checkGLcall("glSetFenceNV");
227     }
228
229     LEAVE_GL();
230
231     context_release(context);
232 }
233
234 ULONG CDECL wined3d_query_incref(struct wined3d_query *query)
235 {
236     ULONG refcount = InterlockedIncrement(&query->ref);
237
238     TRACE("%p increasing refcount to %u.\n", query, refcount);
239
240     return refcount;
241 }
242
243 ULONG CDECL wined3d_query_decref(struct wined3d_query *query)
244 {
245     ULONG refcount = InterlockedDecrement(&query->ref);
246
247     TRACE("%p decreasing refcount to %u.\n", query, refcount);
248
249     if (!refcount)
250     {
251         /* Queries are specific to the GL context that created them. Not
252          * deleting the query will obviously leak it, but that's still better
253          * than potentially deleting a different query with the same id in this
254          * context, and (still) leaking the actual query. */
255         if (query->type == WINED3D_QUERY_TYPE_EVENT)
256         {
257             struct wined3d_event_query *event_query = query->extendedData;
258             if (event_query) wined3d_event_query_destroy(event_query);
259         }
260         else if (query->type == WINED3D_QUERY_TYPE_OCCLUSION)
261         {
262             struct wined3d_occlusion_query *oq = query->extendedData;
263
264             if (oq->context) context_free_occlusion_query(oq);
265             HeapFree(GetProcessHeap(), 0, query->extendedData);
266         }
267
268         HeapFree(GetProcessHeap(), 0, query);
269     }
270
271     return refcount;
272 }
273
274 HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
275         void *data, UINT data_size, DWORD flags)
276 {
277     TRACE("query %p, data %p, data_size %u, flags %#x.\n",
278             query, data, data_size, flags);
279
280     return query->query_ops->query_get_data(query, data, data_size, flags);
281 }
282
283 UINT CDECL wined3d_query_get_data_size(const struct wined3d_query *query)
284 {
285     TRACE("query %p.\n", query);
286
287     return query->data_size;
288 }
289
290 HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
291 {
292     TRACE("query %p, flags %#x.\n", query, flags);
293
294     return query->query_ops->query_issue(query, flags);
295 }
296
297 static HRESULT wined3d_occlusion_query_ops_get_data(struct wined3d_query *query,
298         void *pData, DWORD dwSize, DWORD flags)
299 {
300     struct wined3d_occlusion_query *oq = query->extendedData;
301     struct wined3d_device *device = query->device;
302     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
303     struct wined3d_context *context;
304     DWORD* data = pData;
305     GLuint available;
306     GLuint samples;
307     HRESULT res;
308
309     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
310
311     if (!oq->context)
312         query->state = QUERY_CREATED;
313
314     if (query->state == QUERY_CREATED)
315     {
316         /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
317         TRACE("Query wasn't yet started, returning S_OK\n");
318         if(data) *data = 0;
319         return S_OK;
320     }
321
322     if (query->state == QUERY_BUILDING)
323     {
324         /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
325         TRACE("Query is building, returning S_FALSE\n");
326         return S_FALSE;
327     }
328
329     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
330     {
331         WARN("%p Occlusion queries not supported. Returning 1.\n", query);
332         *data = 1;
333         return S_OK;
334     }
335
336     if (oq->context->tid != GetCurrentThreadId())
337     {
338         FIXME("%p Wrong thread, returning 1.\n", query);
339         *data = 1;
340         return S_OK;
341     }
342
343     context = context_acquire(query->device, oq->context->current_rt);
344
345     ENTER_GL();
346
347     GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
348     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
349     TRACE("available %#x.\n", available);
350
351     if (available)
352     {
353         if (data)
354         {
355             GL_EXTCALL(glGetQueryObjectuivARB(oq->id, GL_QUERY_RESULT_ARB, &samples));
356             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
357             TRACE("Returning %d samples.\n", samples);
358             *data = samples;
359         }
360         res = S_OK;
361     }
362     else
363     {
364         res = S_FALSE;
365     }
366
367     LEAVE_GL();
368
369     context_release(context);
370
371     return res;
372 }
373
374 static HRESULT wined3d_event_query_ops_get_data(struct wined3d_query *query,
375         void *pData, DWORD dwSize, DWORD flags)
376 {
377     struct wined3d_event_query *event_query = query->extendedData;
378     BOOL *data = pData;
379     enum wined3d_event_query_result ret;
380
381     TRACE("query %p, pData %p, dwSize %#x, flags %#x.\n", query, pData, dwSize, flags);
382
383     if (!pData || !dwSize) return S_OK;
384     if (!event_query)
385     {
386         WARN("Event query not supported by GL, reporting GPU idle.\n");
387         *data = TRUE;
388         return S_OK;
389     }
390
391     ret = wined3d_event_query_test(event_query, query->device);
392     switch(ret)
393     {
394         case WINED3D_EVENT_QUERY_OK:
395         case WINED3D_EVENT_QUERY_NOT_STARTED:
396             *data = TRUE;
397             break;
398
399         case WINED3D_EVENT_QUERY_WAITING:
400             *data = FALSE;
401             break;
402
403         case WINED3D_EVENT_QUERY_WRONG_THREAD:
404             FIXME("(%p) Wrong thread, reporting GPU idle.\n", query);
405             *data = TRUE;
406             break;
407
408         case WINED3D_EVENT_QUERY_ERROR:
409             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
410             return WINED3DERR_INVALIDCALL;
411     }
412
413     return S_OK;
414 }
415
416 enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query *query)
417 {
418     TRACE("query %p.\n", query);
419
420     return query->type;
421 }
422
423 static HRESULT wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
424 {
425     TRACE("query %p, flags %#x.\n", query, flags);
426
427     TRACE("(%p) : flags %#x, type D3DQUERY_EVENT\n", query, flags);
428     if (flags & WINED3DISSUE_END)
429     {
430         struct wined3d_event_query *event_query = query->extendedData;
431
432         /* Faked event query support */
433         if (!event_query) return WINED3D_OK;
434
435         wined3d_event_query_issue(event_query, query->device);
436     }
437     else if (flags & WINED3DISSUE_BEGIN)
438     {
439         /* Started implicitly at device creation */
440         ERR("Event query issued with START flag - what to do?\n");
441     }
442
443     if (flags & WINED3DISSUE_BEGIN)
444         query->state = QUERY_BUILDING;
445     else
446         query->state = QUERY_SIGNALLED;
447
448     return WINED3D_OK;
449 }
450
451 static HRESULT wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
452 {
453     struct wined3d_device *device = query->device;
454     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
455
456     TRACE("query %p, flags %#x.\n", query, flags);
457
458     if (gl_info->supported[ARB_OCCLUSION_QUERY])
459     {
460         struct wined3d_occlusion_query *oq = query->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 (query->state == QUERY_BUILDING)
467             {
468                 if (oq->context->tid != GetCurrentThreadId())
469                 {
470                     FIXME("Wrong thread, can't restart query.\n");
471
472                     context_free_occlusion_query(oq);
473                     context = context_acquire(query->device, NULL);
474                     context_alloc_occlusion_query(context, oq);
475                 }
476                 else
477                 {
478                     context = context_acquire(query->device, oq->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 (oq->context) context_free_occlusion_query(oq);
489                 context = context_acquire(query->device, NULL);
490                 context_alloc_occlusion_query(context, oq);
491             }
492
493             ENTER_GL();
494             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, oq->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 (query->state == QUERY_BUILDING)
507             {
508                 if (oq->context->tid != GetCurrentThreadId())
509                 {
510                     FIXME("Wrong thread, can't end query.\n");
511                 }
512                 else
513                 {
514                     context = context_acquire(query->device, oq->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     }
526     else
527     {
528         FIXME("%p Occlusion queries not supported.\n", query);
529     }
530
531     if (flags & WINED3DISSUE_BEGIN)
532         query->state = QUERY_BUILDING;
533     else
534         query->state = QUERY_SIGNALLED;
535
536     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
537 }
538
539 static const struct wined3d_query_ops event_query_ops =
540 {
541     wined3d_event_query_ops_get_data,
542     wined3d_event_query_ops_issue,
543 };
544
545 static const struct wined3d_query_ops occlusion_query_ops =
546 {
547     wined3d_occlusion_query_ops_get_data,
548     wined3d_occlusion_query_ops_issue,
549 };
550
551 static HRESULT query_init(struct wined3d_query *query, struct wined3d_device *device, enum wined3d_query_type type)
552 {
553     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
554
555     switch (type)
556     {
557         case WINED3D_QUERY_TYPE_OCCLUSION:
558             TRACE("Occlusion query.\n");
559             if (!gl_info->supported[ARB_OCCLUSION_QUERY])
560             {
561                 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
562                 return WINED3DERR_NOTAVAILABLE;
563             }
564             query->query_ops = &occlusion_query_ops;
565             query->data_size = sizeof(DWORD);
566             query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
567             if (!query->extendedData)
568             {
569                 ERR("Failed to allocate occlusion query extended data.\n");
570                 return E_OUTOFMEMORY;
571             }
572             ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
573             break;
574
575         case WINED3D_QUERY_TYPE_EVENT:
576             TRACE("Event query.\n");
577             if (!wined3d_event_query_supported(gl_info))
578             {
579                 /* Half-Life 2 needs this query. It does not render the main
580                  * menu correctly otherwise. Pretend to support it, faking
581                  * this query does not do much harm except potentially
582                  * lowering performance. */
583                 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
584             }
585             query->query_ops = &event_query_ops;
586             query->data_size = sizeof(BOOL);
587             query->extendedData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_event_query));
588             if (!query->extendedData)
589             {
590                 ERR("Failed to allocate event query memory.\n");
591                 return E_OUTOFMEMORY;
592             }
593             break;
594
595         case WINED3D_QUERY_TYPE_VCACHE:
596         case WINED3D_QUERY_TYPE_RESOURCE_MANAGER:
597         case WINED3D_QUERY_TYPE_VERTEX_STATS:
598         case WINED3D_QUERY_TYPE_TIMESTAMP:
599         case WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT:
600         case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ:
601         case WINED3D_QUERY_TYPE_PIPELINE_TIMINGS:
602         case WINED3D_QUERY_TYPE_INTERFACE_TIMINGS:
603         case WINED3D_QUERY_TYPE_VERTEX_TIMINGS:
604         case WINED3D_QUERY_TYPE_PIXEL_TIMINGS:
605         case WINED3D_QUERY_TYPE_BANDWIDTH_TIMINGS:
606         case WINED3D_QUERY_TYPE_CACHE_UTILIZATION:
607         default:
608             FIXME("Unhandled query type %#x.\n", type);
609             return WINED3DERR_NOTAVAILABLE;
610     }
611
612     query->type = type;
613     query->state = QUERY_CREATED;
614     query->device = device;
615     query->ref = 1;
616
617     return WINED3D_OK;
618 }
619
620 HRESULT CDECL wined3d_query_create(struct wined3d_device *device,
621         enum wined3d_query_type type, struct wined3d_query **query)
622 {
623     struct wined3d_query *object;
624     HRESULT hr;
625
626     TRACE("device %p, type %#x, query %p.\n", device, type, query);
627
628     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
629     if (!object)
630     {
631         ERR("Failed to allocate query memory.\n");
632         return E_OUTOFMEMORY;
633     }
634
635     hr = query_init(object, device, type);
636     if (FAILED(hr))
637     {
638         WARN("Failed to initialize query, hr %#x.\n", hr);
639         HeapFree(GetProcessHeap(), 0, object);
640         return hr;
641     }
642
643     TRACE("Created query %p.\n", object);
644     *query = object;
645
646     return WINED3D_OK;
647 }