shlwapi: Only skip 'localhost' in file URLs in UrlCanonicalize.
[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 #define GLINFO_LOCATION (*gl_info)
29
30 static HRESULT wined3d_event_query_init(const struct wined3d_gl_info *gl_info, struct wined3d_event_query **query)
31 {
32     struct wined3d_event_query *ret;
33     *query = NULL;
34     if (!gl_info->supported[ARB_SYNC] && !gl_info->supported[NV_FENCE]
35         && !gl_info->supported[APPLE_FENCE]) return E_NOTIMPL;
36
37     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
38     if (!ret)
39     {
40         ERR("Failed to allocate a wined3d event query structure.\n");
41         return E_OUTOFMEMORY;
42     }
43     ret->context = NULL;
44     *query = ret;
45     return WINED3D_OK;
46 }
47
48 static void wined3d_event_query_destroy(struct wined3d_event_query *query)
49 {
50     if (query->context) context_free_event_query(query);
51     HeapFree(GetProcessHeap(), 0, query);
52 }
53
54 static enum wined3d_event_query_result wined3d_event_query_test(struct wined3d_event_query *query, IWineD3DDeviceImpl *device)
55 {
56     struct wined3d_context *context;
57     const struct wined3d_gl_info *gl_info;
58     enum wined3d_event_query_result ret;
59     BOOL fence_result;
60
61     TRACE("(%p) : device %p\n", query, device);
62
63     if (query->context == NULL)
64     {
65         TRACE("Query not started\n");
66         return WINED3D_EVENT_QUERY_NOT_STARTED;
67     }
68
69     if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
70     {
71         WARN("Event query tested from wrong thread\n");
72         return WINED3D_EVENT_QUERY_WRONG_THREAD;
73     }
74
75     context = context_acquire(device, query->context->current_rt, CTXUSAGE_RESOURCELOAD);
76     gl_info = context->gl_info;
77
78     ENTER_GL();
79
80     if (gl_info->supported[ARB_SYNC])
81     {
82         GLenum gl_ret = GL_EXTCALL(glClientWaitSync(query->object.sync, 0, 0));
83         checkGLcall("glClientWaitSync");
84
85         switch (gl_ret)
86         {
87             case GL_ALREADY_SIGNALED:
88             case GL_CONDITION_SATISFIED:
89                 ret = WINED3D_EVENT_QUERY_OK;
90                 break;
91
92             case GL_TIMEOUT_EXPIRED:
93                 ret = WINED3D_EVENT_QUERY_WAITING;
94                 break;
95
96             case GL_WAIT_FAILED:
97             default:
98                 ERR("glClientWaitSync returned %#x.\n", gl_ret);
99                 ret = WINED3D_EVENT_QUERY_ERROR;
100         }
101     }
102     else if (gl_info->supported[APPLE_FENCE])
103     {
104         fence_result = GL_EXTCALL(glTestFenceAPPLE(query->object.id));
105         checkGLcall("glTestFenceAPPLE");
106         if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
107         else ret = WINED3D_EVENT_QUERY_WAITING;
108     }
109     else if (gl_info->supported[NV_FENCE])
110     {
111         fence_result = GL_EXTCALL(glTestFenceNV(query->object.id));
112         checkGLcall("glTestFenceNV");
113         if (fence_result) ret = WINED3D_EVENT_QUERY_OK;
114         else ret = WINED3D_EVENT_QUERY_WAITING;
115     }
116     else
117     {
118         ERR("Event query created despite lack of GL support\n");
119         ret = WINED3D_EVENT_QUERY_ERROR;
120     }
121
122     LEAVE_GL();
123
124     context_release(context);
125     return ret;
126 }
127
128 static void wined3d_event_query_issue(struct wined3d_event_query *query, IWineD3DDeviceImpl *device)
129 {
130     const struct wined3d_gl_info *gl_info;
131     struct wined3d_context *context;
132
133     if (query->context)
134     {
135         if (!query->context->gl_info->supported[ARB_SYNC] && query->context->tid != GetCurrentThreadId())
136         {
137             context_free_event_query(query);
138             context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
139             context_alloc_event_query(context, query);
140         }
141         else
142         {
143             context = context_acquire(device, query->context->current_rt, CTXUSAGE_RESOURCELOAD);
144         }
145     }
146     else
147     {
148         context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
149         context_alloc_event_query(context, query);
150     }
151
152     gl_info = context->gl_info;
153
154     ENTER_GL();
155
156     if (gl_info->supported[ARB_SYNC])
157     {
158         if (query->object.sync) GL_EXTCALL(glDeleteSync(query->object.sync));
159         checkGLcall("glDeleteSync");
160         query->object.sync = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
161         checkGLcall("glFenceSync");
162     }
163     else if (gl_info->supported[APPLE_FENCE])
164     {
165         GL_EXTCALL(glSetFenceAPPLE(query->object.id));
166         checkGLcall("glSetFenceAPPLE");
167     }
168     else if (gl_info->supported[NV_FENCE])
169     {
170         GL_EXTCALL(glSetFenceNV(query->object.id, GL_ALL_COMPLETED_NV));
171         checkGLcall("glSetFenceNV");
172     }
173
174     LEAVE_GL();
175
176     context_release(context);
177 }
178
179 /*
180  * Occlusion Queries:
181  * http://www.gris.uni-tuebingen.de/~bartz/Publications/paper/hww98.pdf
182  * http://oss.sgi.com/projects/ogl-sample/registry/ARB/occlusion_query.txt
183  */
184
185 /* *******************************************
186    IWineD3DQuery IUnknown parts follow
187    ******************************************* */
188 static HRESULT  WINAPI IWineD3DQueryImpl_QueryInterface(IWineD3DQuery *iface, REFIID riid, LPVOID *ppobj)
189 {
190     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
191     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
192     if (IsEqualGUID(riid, &IID_IUnknown)
193         || IsEqualGUID(riid, &IID_IWineD3DBase)
194         || IsEqualGUID(riid, &IID_IWineD3DQuery)) {
195         IUnknown_AddRef(iface);
196         *ppobj = This;
197         return S_OK;
198     }
199     *ppobj = NULL;
200     return E_NOINTERFACE;
201 }
202
203 static ULONG  WINAPI IWineD3DQueryImpl_AddRef(IWineD3DQuery *iface) {
204     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
205     TRACE("(%p) : AddRef increasing from %d\n", This, This->ref);
206     return InterlockedIncrement(&This->ref);
207 }
208
209 static ULONG  WINAPI IWineD3DQueryImpl_Release(IWineD3DQuery *iface) {
210     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
211     ULONG ref;
212     TRACE("(%p) : Releasing from %d\n", This, This->ref);
213     ref = InterlockedDecrement(&This->ref);
214     if (ref == 0) {
215         /* Queries are specific to the GL context that created them. Not
216          * deleting the query will obviously leak it, but that's still better
217          * than potentially deleting a different query with the same id in this
218          * context, and (still) leaking the actual query. */
219         if (This->type == WINED3DQUERYTYPE_EVENT)
220         {
221             struct wined3d_event_query *query = This->extendedData;
222             if (query) wined3d_event_query_destroy(query);
223         }
224         else if (This->type == WINED3DQUERYTYPE_OCCLUSION)
225         {
226             struct wined3d_occlusion_query *query = This->extendedData;
227
228             if (query->context) context_free_occlusion_query(query);
229             HeapFree(GetProcessHeap(), 0, This->extendedData);
230         }
231
232         HeapFree(GetProcessHeap(), 0, This);
233     }
234     return ref;
235 }
236
237 /* *******************************************
238    IWineD3DQuery IWineD3DQuery parts follow
239    ******************************************* */
240 static HRESULT WINAPI IWineD3DQueryImpl_GetParent(IWineD3DQuery *iface, IUnknown **parent)
241 {
242     TRACE("iface %p, parent %p.\n", iface, parent);
243
244     *parent = (IUnknown *)parent;
245     IUnknown_AddRef(*parent);
246
247     TRACE("Returning %p.\n", *parent);
248
249     return WINED3D_OK;
250 }
251
252 static HRESULT  WINAPI IWineD3DOcclusionQueryImpl_GetData(IWineD3DQuery* iface, void* pData, DWORD dwSize, DWORD dwGetDataFlags) {
253     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *) iface;
254     struct wined3d_occlusion_query *query = This->extendedData;
255     IWineD3DDeviceImpl *device = This->device;
256     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
257     struct wined3d_context *context;
258     DWORD* data = pData;
259     GLuint available;
260     GLuint samples;
261     HRESULT res;
262
263     TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This, pData, dwSize, dwGetDataFlags);
264
265     if (!query->context) This->state = QUERY_CREATED;
266
267     if (This->state == QUERY_CREATED)
268     {
269         /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
270         TRACE("Query wasn't yet started, returning S_OK\n");
271         if(data) *data = 0;
272         return S_OK;
273     }
274
275     if (This->state == QUERY_BUILDING)
276     {
277         /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
278         TRACE("Query is building, returning S_FALSE\n");
279         return S_FALSE;
280     }
281
282     if (!gl_info->supported[ARB_OCCLUSION_QUERY])
283     {
284         WARN("(%p) : Occlusion queries not supported. Returning 1.\n", This);
285         *data = 1;
286         return S_OK;
287     }
288
289     if (query->context->tid != GetCurrentThreadId())
290     {
291         FIXME("%p Wrong thread, returning 1.\n", This);
292         *data = 1;
293         return S_OK;
294     }
295
296     context = context_acquire(This->device, query->context->current_rt, CTXUSAGE_RESOURCELOAD);
297
298     ENTER_GL();
299
300     GL_EXTCALL(glGetQueryObjectuivARB(query->id, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
301     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
302     TRACE("(%p) : available %d.\n", This, available);
303
304     if (available)
305     {
306         if (data)
307         {
308             GL_EXTCALL(glGetQueryObjectuivARB(query->id, GL_QUERY_RESULT_ARB, &samples));
309             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
310             TRACE("(%p) : Returning %d samples.\n", This, samples);
311             *data = samples;
312         }
313         res = S_OK;
314     }
315     else
316     {
317         res = S_FALSE;
318     }
319
320     LEAVE_GL();
321
322     context_release(context);
323
324     return res;
325 }
326
327 static HRESULT  WINAPI IWineD3DEventQueryImpl_GetData(IWineD3DQuery* iface, void* pData, DWORD dwSize, DWORD dwGetDataFlags) {
328     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *) iface;
329     struct wined3d_event_query *query = This->extendedData;
330     BOOL *data = pData;
331     enum wined3d_event_query_result ret;
332
333     TRACE("(%p) : type D3DQUERY_EVENT, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This, pData, dwSize, dwGetDataFlags);
334
335     if (!pData || !dwSize) return S_OK;
336     if (!query)
337     {
338         WARN("(%p): Event query not supported by GL, reporting GPU idle\n", This);
339         *data = TRUE;
340         return S_OK;
341     }
342
343     ret = wined3d_event_query_test(query, This->device);
344     switch(ret)
345     {
346         case WINED3D_EVENT_QUERY_OK:
347         case WINED3D_EVENT_QUERY_NOT_STARTED:
348             *data = TRUE;
349             break;
350
351         case WINED3D_EVENT_QUERY_WAITING:
352             *data = FALSE;
353             break;
354
355         case WINED3D_EVENT_QUERY_WRONG_THREAD:
356             FIXME("(%p) Wrong thread, reporting GPU idle.\n", This);
357             *data = TRUE;
358             break;
359
360         case WINED3D_EVENT_QUERY_ERROR:
361             ERR("The GL event query failed, returning D3DERR_INVALIDCALL\n");
362             return WINED3DERR_INVALIDCALL;
363     }
364
365     return S_OK;
366 }
367
368 static DWORD  WINAPI IWineD3DEventQueryImpl_GetDataSize(IWineD3DQuery* iface){
369     TRACE("(%p) : type D3DQUERY_EVENT\n", iface);
370
371     return sizeof(BOOL);
372 }
373
374 static DWORD  WINAPI IWineD3DOcclusionQueryImpl_GetDataSize(IWineD3DQuery* iface){
375     TRACE("(%p) : type D3DQUERY_OCCLUSION\n", iface);
376
377     return sizeof(DWORD);
378 }
379
380 static WINED3DQUERYTYPE  WINAPI IWineD3DQueryImpl_GetType(IWineD3DQuery* iface){
381     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
382     return This->type;
383 }
384
385 static HRESULT  WINAPI IWineD3DEventQueryImpl_Issue(IWineD3DQuery* iface,  DWORD dwIssueFlags) {
386     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
387
388     TRACE("(%p) : dwIssueFlags %#x, type D3DQUERY_EVENT\n", This, dwIssueFlags);
389     if (dwIssueFlags & WINED3DISSUE_END)
390     {
391         struct wined3d_event_query *query = This->extendedData;
392
393         /* Faked event query support */
394         if (!query) return WINED3D_OK;
395
396         wined3d_event_query_issue(query, This->device);
397     }
398     else if(dwIssueFlags & WINED3DISSUE_BEGIN)
399     {
400         /* Started implicitly at device creation */
401         ERR("Event query issued with START flag - what to do?\n");
402     }
403
404     if(dwIssueFlags & WINED3DISSUE_BEGIN) {
405         This->state = QUERY_BUILDING;
406     } else {
407         This->state = QUERY_SIGNALLED;
408     }
409
410     return WINED3D_OK;
411 }
412
413 static HRESULT  WINAPI IWineD3DOcclusionQueryImpl_Issue(IWineD3DQuery* iface,  DWORD dwIssueFlags) {
414     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
415     IWineD3DDeviceImpl *device = This->device;
416     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
417
418     if (gl_info->supported[ARB_OCCLUSION_QUERY])
419     {
420         struct wined3d_occlusion_query *query = This->extendedData;
421         struct wined3d_context *context;
422
423         /* This is allowed according to msdn and our tests. Reset the query and restart */
424         if (dwIssueFlags & WINED3DISSUE_BEGIN)
425         {
426             if (This->state == QUERY_BUILDING)
427             {
428                 if (query->context->tid != GetCurrentThreadId())
429                 {
430                     FIXME("Wrong thread, can't restart query.\n");
431
432                     context_free_occlusion_query(query);
433                     context = context_acquire(This->device, NULL, CTXUSAGE_RESOURCELOAD);
434                     context_alloc_occlusion_query(context, query);
435                 }
436                 else
437                 {
438                     context = context_acquire(This->device, query->context->current_rt, CTXUSAGE_RESOURCELOAD);
439
440                     ENTER_GL();
441                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
442                     checkGLcall("glEndQuery()");
443                     LEAVE_GL();
444                 }
445             }
446             else
447             {
448                 if (query->context) context_free_occlusion_query(query);
449                 context = context_acquire(This->device, NULL, CTXUSAGE_RESOURCELOAD);
450                 context_alloc_occlusion_query(context, query);
451             }
452
453             ENTER_GL();
454             GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, query->id));
455             checkGLcall("glBeginQuery()");
456             LEAVE_GL();
457
458             context_release(context);
459         }
460         if (dwIssueFlags & WINED3DISSUE_END) {
461             /* Msdn says _END on a non-building occlusion query returns an error, but
462              * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
463              * generating an error
464              */
465             if (This->state == QUERY_BUILDING)
466             {
467                 if (query->context->tid != GetCurrentThreadId())
468                 {
469                     FIXME("Wrong thread, can't end query.\n");
470                 }
471                 else
472                 {
473                     context = context_acquire(This->device, query->context->current_rt, CTXUSAGE_RESOURCELOAD);
474
475                     ENTER_GL();
476                     GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
477                     checkGLcall("glEndQuery()");
478                     LEAVE_GL();
479
480                     context_release(context);
481                 }
482             }
483         }
484     } else {
485         FIXME("(%p) : Occlusion queries not supported\n", This);
486     }
487
488     if(dwIssueFlags & WINED3DISSUE_BEGIN) {
489         This->state = QUERY_BUILDING;
490     } else {
491         This->state = QUERY_SIGNALLED;
492     }
493     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
494 }
495
496 static const IWineD3DQueryVtbl IWineD3DEventQuery_Vtbl =
497 {
498     /*** IUnknown methods ***/
499     IWineD3DQueryImpl_QueryInterface,
500     IWineD3DQueryImpl_AddRef,
501     IWineD3DQueryImpl_Release,
502     /*** IWineD3Dquery methods ***/
503     IWineD3DQueryImpl_GetParent,
504     IWineD3DEventQueryImpl_GetData,
505     IWineD3DEventQueryImpl_GetDataSize,
506     IWineD3DQueryImpl_GetType,
507     IWineD3DEventQueryImpl_Issue
508 };
509
510 static const IWineD3DQueryVtbl IWineD3DOcclusionQuery_Vtbl =
511 {
512     /*** IUnknown methods ***/
513     IWineD3DQueryImpl_QueryInterface,
514     IWineD3DQueryImpl_AddRef,
515     IWineD3DQueryImpl_Release,
516     /*** IWineD3Dquery methods ***/
517     IWineD3DQueryImpl_GetParent,
518     IWineD3DOcclusionQueryImpl_GetData,
519     IWineD3DOcclusionQueryImpl_GetDataSize,
520     IWineD3DQueryImpl_GetType,
521     IWineD3DOcclusionQueryImpl_Issue
522 };
523
524 HRESULT query_init(IWineD3DQueryImpl *query, IWineD3DDeviceImpl *device,
525         WINED3DQUERYTYPE type, IUnknown *parent)
526 {
527     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
528     HRESULT hr;
529
530     switch (type)
531     {
532         case WINED3DQUERYTYPE_OCCLUSION:
533             TRACE("Occlusion query.\n");
534             if (!gl_info->supported[ARB_OCCLUSION_QUERY])
535             {
536                 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
537                 return WINED3DERR_NOTAVAILABLE;
538             }
539             query->lpVtbl = &IWineD3DOcclusionQuery_Vtbl;
540             query->extendedData = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query));
541             if (!query->extendedData)
542             {
543                 ERR("Failed to allocate occlusion query extended data.\n");
544                 return E_OUTOFMEMORY;
545             }
546             ((struct wined3d_occlusion_query *)query->extendedData)->context = NULL;
547             break;
548
549         case WINED3DQUERYTYPE_EVENT:
550             TRACE("Event query.\n");
551             query->lpVtbl = &IWineD3DEventQuery_Vtbl;
552             hr = wined3d_event_query_init(gl_info, (struct wined3d_event_query **) &query->extendedData);
553             if (hr == E_NOTIMPL)
554             {
555                 /* Half-Life 2 needs this query. It does not render the main
556                  * menu correctly otherwise. Pretend to support it, faking
557                  * this query does not do much harm except potentially
558                  * lowering performance. */
559                 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
560             }
561             else if(FAILED(hr))
562             {
563                 return hr;
564             }
565             break;
566
567         case WINED3DQUERYTYPE_VCACHE:
568         case WINED3DQUERYTYPE_RESOURCEMANAGER:
569         case WINED3DQUERYTYPE_VERTEXSTATS:
570         case WINED3DQUERYTYPE_TIMESTAMP:
571         case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
572         case WINED3DQUERYTYPE_TIMESTAMPFREQ:
573         case WINED3DQUERYTYPE_PIPELINETIMINGS:
574         case WINED3DQUERYTYPE_INTERFACETIMINGS:
575         case WINED3DQUERYTYPE_VERTEXTIMINGS:
576         case WINED3DQUERYTYPE_PIXELTIMINGS:
577         case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
578         case WINED3DQUERYTYPE_CACHEUTILIZATION:
579         default:
580             FIXME("Unhandled query type %#x.\n", type);
581             return WINED3DERR_NOTAVAILABLE;
582     }
583
584     query->type = type;
585     query->state = QUERY_CREATED;
586     query->device = device;
587     query->parent = parent;
588     query->ref = 1;
589
590     return WINED3D_OK;
591 }