wined3d: Get rid of the invymat.
[wine] / dlls / wined3d / query.c
1 /*
2  * IWineD3DQuery implementation
3  *
4  * Copyright 2005 Oliver Stieber
5  *
6  *
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 /*
28  * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/programmingguide/advancedtopics/Queries.asp
29  *
30  * Occlusion Queries:
31  * http://www.gris.uni-tuebingen.de/~bartz/Publications/paper/hww98.pdf
32  * http://oss.sgi.com/projects/ogl-sample/registry/ARB/occlusion_query.txt
33  */
34
35 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
36 #define GLINFO_LOCATION This->wineD3DDevice->adapter->gl_info
37
38 /* *******************************************
39    IWineD3DQuery IUnknown parts follow
40    ******************************************* */
41 static HRESULT  WINAPI IWineD3DQueryImpl_QueryInterface(IWineD3DQuery *iface, REFIID riid, LPVOID *ppobj)
42 {
43     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
44     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
45     if (IsEqualGUID(riid, &IID_IUnknown)
46         || IsEqualGUID(riid, &IID_IWineD3DBase)
47         || IsEqualGUID(riid, &IID_IWineD3DQuery)) {
48         IUnknown_AddRef(iface);
49         *ppobj = This;
50         return S_OK;
51     }
52     *ppobj = NULL;
53     return E_NOINTERFACE;
54 }
55
56 static ULONG  WINAPI IWineD3DQueryImpl_AddRef(IWineD3DQuery *iface) {
57     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
58     TRACE("(%p) : AddRef increasing from %d\n", This, This->ref);
59     return InterlockedIncrement(&This->ref);
60 }
61
62 static ULONG  WINAPI IWineD3DQueryImpl_Release(IWineD3DQuery *iface) {
63     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
64     ULONG ref;
65     TRACE("(%p) : Releasing from %d\n", This, This->ref);
66     ref = InterlockedDecrement(&This->ref);
67     if (ref == 0) {
68         if(This->type == WINED3DQUERYTYPE_EVENT) {
69             if(GL_SUPPORT(APPLE_FENCE)) {
70                 GL_EXTCALL(glDeleteFencesAPPLE(1, &((WineQueryEventData *)(This->extendedData))->fenceId));
71                 checkGLcall("glDeleteFencesAPPLE");
72             } else if(GL_SUPPORT(NV_FENCE)) {
73                 GL_EXTCALL(glDeleteFencesNV(1, &((WineQueryEventData *)(This->extendedData))->fenceId));
74                 checkGLcall("glDeleteFencesNV");
75             }
76         } else if(This->type == WINED3DQUERYTYPE_OCCLUSION && GL_SUPPORT(ARB_OCCLUSION_QUERY)) {
77             GL_EXTCALL(glDeleteQueriesARB(1, &((WineQueryOcclusionData *)(This->extendedData))->queryId));
78             checkGLcall("glDeleteQueriesARB");
79         }
80
81         HeapFree(GetProcessHeap(), 0, This->extendedData);
82         HeapFree(GetProcessHeap(), 0, This);
83     }
84     return ref;
85 }
86
87 /* *******************************************
88    IWineD3DQuery IWineD3DQuery parts follow
89    ******************************************* */
90 static HRESULT  WINAPI IWineD3DQueryImpl_GetParent(IWineD3DQuery *iface, IUnknown** parent){
91     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
92
93     *parent= (IUnknown*) parent;
94     IUnknown_AddRef(*parent);
95     TRACE("(%p) : returning %p\n", This, *parent);
96     return WINED3D_OK;
97 }
98
99 static HRESULT  WINAPI IWineD3DQueryImpl_GetDevice(IWineD3DQuery* iface, IWineD3DDevice **pDevice){
100     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
101     IWineD3DDevice_AddRef((IWineD3DDevice *)This->wineD3DDevice);
102     *pDevice = (IWineD3DDevice *)This->wineD3DDevice;
103     TRACE("(%p) returning %p\n", This, *pDevice);
104     return WINED3D_OK;
105 }
106
107
108 static HRESULT  WINAPI IWineD3DQueryImpl_GetData(IWineD3DQuery* iface, void* pData, DWORD dwSize, DWORD dwGetDataFlags){
109     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
110     HRESULT res = S_OK;
111
112     TRACE("(%p) : type %#x, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This, This->type, pData, dwSize, dwGetDataFlags);
113
114     switch (This->type){
115
116     case WINED3DQUERYTYPE_VCACHE:
117     {
118
119         WINED3DDEVINFO_VCACHE *data = (WINED3DDEVINFO_VCACHE *)pData;
120         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_VCACHE\n", This);
121         if(pData == NULL || dwSize == 0) break;
122         data->Pattern     = WINEMAKEFOURCC('C','A','C','H');
123         data->OptMethod   = 0; /*0 get longest strips, 1 optimize vertex cache*/
124         data->CacheSize   = 0; /*cache size, only required if OptMethod == 1*/
125         data->MagicNumber = 0; /*only required if OptMethod == 1 (used internally)*/
126
127     }
128     break;
129     case WINED3DQUERYTYPE_RESOURCEMANAGER:
130     {
131         WINED3DDEVINFO_RESOURCEMANAGER *data = (WINED3DDEVINFO_RESOURCEMANAGER *)pData;
132         int i;
133         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_RESOURCEMANAGER\n", This);
134         if(pData == NULL || dwSize == 0) break;
135         for(i = 0; i < WINED3DRTYPECOUNT; i++){
136             /*I'm setting the default values to 1 so as to reduce the risk of a div/0 in the caller*/
137             /*  isTextureResident could be used to get some of this infomration  */
138             data->stats[i].bThrashing            = FALSE;
139             data->stats[i].ApproxBytesDownloaded = 1;
140             data->stats[i].NumEvicts             = 1;
141             data->stats[i].NumVidCreates         = 1;
142             data->stats[i].LastPri               = 1;
143             data->stats[i].NumUsed               = 1;
144             data->stats[i].NumUsedInVidMem       = 1;
145             data->stats[i].WorkingSet            = 1;
146             data->stats[i].WorkingSetBytes       = 1;
147             data->stats[i].TotalManaged          = 1;
148             data->stats[i].TotalBytes            = 1;
149         }
150
151     }
152     break;
153     case WINED3DQUERYTYPE_VERTEXSTATS:
154     {
155         WINED3DDEVINFO_VERTEXSTATS *data = (WINED3DDEVINFO_VERTEXSTATS *)pData;
156         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_VERTEXSTATS\n", This);
157         if(pData == NULL || dwSize == 0) break;
158         data->NumRenderedTriangles      = 1;
159         data->NumExtraClippingTriangles = 1;
160
161     }
162     break;
163     case WINED3DQUERYTYPE_EVENT:
164     {
165         BOOL* data = pData;
166         WineD3DContext *ctx = ((WineQueryEventData *)This->extendedData)->ctx;
167         if(pData == NULL || dwSize == 0) {
168             break;
169         } if(ctx != This->wineD3DDevice->activeContext || ctx->tid != GetCurrentThreadId()) {
170             /* See comment in IWineD3DQuery::Issue, event query codeblock */
171             WARN("Query context not active, reporting GPU idle\n");
172             *data = TRUE;
173         } else if(GL_SUPPORT(APPLE_FENCE)) {
174             *data = GL_EXTCALL(glTestFenceAPPLE(((WineQueryEventData *)This->extendedData)->fenceId));
175             checkGLcall("glTestFenceAPPLE");
176         } else if(GL_SUPPORT(NV_FENCE)) {
177             *data = GL_EXTCALL(glTestFenceNV(((WineQueryEventData *)This->extendedData)->fenceId));
178             checkGLcall("glTestFenceNV");
179         } else {
180             WARN("(%p): reporting GPU idle\n", This);
181             *data = TRUE;
182         }
183     }
184     break;
185     case WINED3DQUERYTYPE_OCCLUSION:
186     {
187         DWORD* data = pData;
188
189         if(This->state == QUERY_CREATED) {
190             /* D3D allows GetData on a new query, opengl doesn't. So just invent the data outselves */
191             TRACE("Query wasn't yet started, returning S_OK\n");
192             res = S_OK;
193             if(data) *data = 0;
194         } else if(This->state == QUERY_BUILDING) {
195             /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
196             TRACE("Query is building, returning S_FALSE\n");
197             res = S_FALSE;
198         } else if (GL_SUPPORT(ARB_OCCLUSION_QUERY) &&
199             ((WineQueryOcclusionData *)This->extendedData)->ctx == This->wineD3DDevice->activeContext &&
200             This->wineD3DDevice->activeContext->tid == GetCurrentThreadId()) {
201             GLuint available;
202             GLuint samples;
203             GLuint queryId = ((WineQueryOcclusionData *)This->extendedData)->queryId;
204
205             GL_EXTCALL(glGetQueryObjectuivARB(queryId, GL_QUERY_RESULT_AVAILABLE_ARB, &available));
206             checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)\n");
207             TRACE("(%p) : available %d.\n", This, available);
208
209             if (available) {
210                 if(data) {
211                     GL_EXTCALL(glGetQueryObjectuivARB(queryId, GL_QUERY_RESULT_ARB, &samples));
212                     checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)\n");
213                     TRACE("(%p) : Returning %d samples.\n", This, samples);
214                     *data = samples;
215                 }
216                 res = S_OK;
217             } else {
218                 res = S_FALSE;
219             }
220         } else {
221             WARN("(%p) : Occlusion queries not supported, or wrong context. Returning 1.\n", This);
222             *data = 1;
223             res = S_OK;
224         }
225     }
226     break;
227     case WINED3DQUERYTYPE_TIMESTAMP:
228     {
229         UINT64* data = pData;
230         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_TIMESTAMP\n", This);
231         if(pData == NULL || dwSize == 0) break;
232         *data = 1; /*Don't know what this is supposed to be*/
233     }
234     break;
235     case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
236     {
237         BOOL* data = pData;
238         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_TIMESTAMPDISJOINT\n", This);
239         if(pData == NULL || dwSize == 0) break;
240         *data = FALSE; /*Don't know what this is supposed to be*/
241     }
242     break;
243     case WINED3DQUERYTYPE_TIMESTAMPFREQ:
244     {
245         UINT64* data = pData;
246         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_TIMESTAMPFREQ\n", This);
247         if(pData == NULL || dwSize == 0) break;
248         *data = 1; /*Don't know what this is supposed to be*/
249     }
250     break;
251     case WINED3DQUERYTYPE_PIPELINETIMINGS:
252     {
253         WINED3DDEVINFO_PIPELINETIMINGS *data = (WINED3DDEVINFO_PIPELINETIMINGS *)pData;
254         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_PIPELINETIMINGS\n", This);
255         if(pData == NULL || dwSize == 0) break;
256
257         data->VertexProcessingTimePercent    =   1.0f;
258         data->PixelProcessingTimePercent     =   1.0f;
259         data->OtherGPUProcessingTimePercent  =  97.0f;
260         data->GPUIdleTimePercent             =   1.0f;
261     }
262     break;
263     case WINED3DQUERYTYPE_INTERFACETIMINGS:
264     {
265         WINED3DDEVINFO_INTERFACETIMINGS *data = (WINED3DDEVINFO_INTERFACETIMINGS *)pData;
266         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_INTERFACETIMINGS\n", This);
267
268         if(pData == NULL || dwSize == 0) break;
269         data->WaitingForGPUToUseApplicationResourceTimePercent =   1.0f;
270         data->WaitingForGPUToAcceptMoreCommandsTimePercent     =   1.0f;
271         data->WaitingForGPUToStayWithinLatencyTimePercent      =   1.0f;
272         data->WaitingForGPUExclusiveResourceTimePercent        =   1.0f;
273         data->WaitingForGPUOtherTimePercent                    =  96.0f;
274     }
275
276     break;
277     case WINED3DQUERYTYPE_VERTEXTIMINGS:
278     {
279         WINED3DDEVINFO_STAGETIMINGS *data = (WINED3DDEVINFO_STAGETIMINGS *)pData;
280         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_VERTEXTIMINGS\n", This);
281
282         if(pData == NULL || dwSize == 0) break;
283         data->MemoryProcessingPercent      = 50.0f;
284         data->ComputationProcessingPercent = 50.0f;
285
286     }
287     break;
288     case WINED3DQUERYTYPE_PIXELTIMINGS:
289     {
290         WINED3DDEVINFO_STAGETIMINGS *data = (WINED3DDEVINFO_STAGETIMINGS *)pData;
291         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_PIXELTIMINGS\n", This);
292
293         if(pData == NULL || dwSize == 0) break;
294         data->MemoryProcessingPercent      = 50.0f;
295         data->ComputationProcessingPercent = 50.0f;
296     }
297     break;
298     case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
299     {
300         WINED3DDEVINFO_BANDWIDTHTIMINGS *data = (WINED3DDEVINFO_BANDWIDTHTIMINGS *)pData;
301         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_BANDWIDTHTIMINGS\n", This);
302
303         if(pData == NULL || dwSize == 0) break;
304         data->MaxBandwidthUtilized                =  1.0f;
305         data->FrontEndUploadMemoryUtilizedPercent =  1.0f;
306         data->VertexRateUtilizedPercent           =  1.0f;
307         data->TriangleSetupRateUtilizedPercent    =  1.0f;
308         data->FillRateUtilizedPercent             = 97.0f;
309     }
310     break;
311     case WINED3DQUERYTYPE_CACHEUTILIZATION:
312     {
313         WINED3DDEVINFO_CACHEUTILIZATION *data = (WINED3DDEVINFO_CACHEUTILIZATION *)pData;
314         FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_CACHEUTILIZATION\n", This);
315
316         if(pData == NULL || dwSize == 0) break;
317         data->TextureCacheHitRate             = 1.0f;
318         data->PostTransformVertexCacheHitRate = 1.0f;
319     }
320
321
322     break;
323     default:
324         FIXME("(%p) Unhandled query type %d\n",This , This->type);
325
326     };
327
328     /*dwGetDataFlags = 0 || D3DGETDATA_FLUSH
329     D3DGETDATA_FLUSH may return WINED3DERR_DEVICELOST if the device is lost
330     */
331     return res; /* S_OK if the query data is available*/
332 }
333
334
335 static DWORD  WINAPI IWineD3DQueryImpl_GetDataSize(IWineD3DQuery* iface){
336     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
337     int dataSize = 0;
338     TRACE("(%p) : type %#x\n", This, This->type);
339     switch(This->type){
340     case WINED3DQUERYTYPE_VCACHE:
341         dataSize = sizeof(WINED3DDEVINFO_VCACHE);
342         break;
343     case WINED3DQUERYTYPE_RESOURCEMANAGER:
344         dataSize = sizeof(WINED3DDEVINFO_RESOURCEMANAGER);
345         break;
346     case WINED3DQUERYTYPE_VERTEXSTATS:
347         dataSize = sizeof(WINED3DDEVINFO_VERTEXSTATS);
348         break;
349     case WINED3DQUERYTYPE_EVENT:
350         dataSize = sizeof(BOOL);
351         break;
352     case WINED3DQUERYTYPE_OCCLUSION:
353         dataSize = sizeof(DWORD);
354         break;
355     case WINED3DQUERYTYPE_TIMESTAMP:
356         dataSize = sizeof(UINT64);
357         break;
358     case WINED3DQUERYTYPE_TIMESTAMPDISJOINT:
359         dataSize = sizeof(BOOL);
360         break;
361     case WINED3DQUERYTYPE_TIMESTAMPFREQ:
362         dataSize = sizeof(UINT64);
363         break;
364     case WINED3DQUERYTYPE_PIPELINETIMINGS:
365         dataSize = sizeof(WINED3DDEVINFO_PIPELINETIMINGS);
366         break;
367     case WINED3DQUERYTYPE_INTERFACETIMINGS:
368         dataSize = sizeof(WINED3DDEVINFO_INTERFACETIMINGS);
369         break;
370     case WINED3DQUERYTYPE_VERTEXTIMINGS:
371         dataSize = sizeof(WINED3DDEVINFO_STAGETIMINGS);
372         break;
373     case WINED3DQUERYTYPE_PIXELTIMINGS:
374         dataSize = sizeof(WINED3DDEVINFO_STAGETIMINGS);
375         break;
376     case WINED3DQUERYTYPE_BANDWIDTHTIMINGS:
377         dataSize = sizeof(WINED3DQUERYTYPE_BANDWIDTHTIMINGS);
378         break;
379     case WINED3DQUERYTYPE_CACHEUTILIZATION:
380         dataSize = sizeof(WINED3DDEVINFO_CACHEUTILIZATION);
381         break;
382     default:
383        FIXME("(%p) Unhandled query type %d\n",This , This->type);
384        dataSize = 0;
385     }
386     return dataSize;
387 }
388
389
390 static WINED3DQUERYTYPE  WINAPI IWineD3DQueryImpl_GetType(IWineD3DQuery* iface){
391     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
392     return This->type;
393 }
394
395
396 static HRESULT  WINAPI IWineD3DQueryImpl_Issue(IWineD3DQuery* iface,  DWORD dwIssueFlags){
397     IWineD3DQueryImpl *This = (IWineD3DQueryImpl *)iface;
398
399     TRACE("(%p) : dwIssueFlags %#x, type %#x\n", This, dwIssueFlags, This->type);
400
401     switch (This->type) {
402         case WINED3DQUERYTYPE_OCCLUSION:
403             if (GL_SUPPORT(ARB_OCCLUSION_QUERY)) {
404                 WineD3DContext *ctx = ((WineQueryOcclusionData *)This->extendedData)->ctx;
405
406                 if(ctx != This->wineD3DDevice->activeContext || ctx->tid != GetCurrentThreadId()) {
407                     WARN("Not the owning context, can't start query\n");
408                 } else {
409                     /* This is allowed according to msdn and our tests. Reset the query and restart */
410                     if (dwIssueFlags & WINED3DISSUE_BEGIN) {
411                         if(This->state == QUERY_BUILDING) {
412                             GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
413                             checkGLcall("glEndQuery()");
414                         }
415
416                         GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, ((WineQueryOcclusionData *)This->extendedData)->queryId));
417                         checkGLcall("glBeginQuery()");
418                     }
419                     if (dwIssueFlags & WINED3DISSUE_END) {
420                         /* Msdn says _END on a non-building occlusion query returns an error, but
421                          * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
422                          * generating an error
423                          */
424                         if(This->state == QUERY_BUILDING) {
425                             GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
426                             checkGLcall("glEndQuery()");
427                         }
428                     }
429                 }
430             } else {
431                 FIXME("(%p) : Occlusion queries not supported\n", This);
432             }
433             break;
434
435         case WINED3DQUERYTYPE_EVENT: {
436             if (dwIssueFlags & WINED3DISSUE_END) {
437                 WineD3DContext *ctx = ((WineQueryEventData *)This->extendedData)->ctx;
438                 if(ctx != This->wineD3DDevice->activeContext || ctx->tid != GetCurrentThreadId()) {
439                     /* GL fences can be used only from the context that created them,
440                      * so if a different context is active, don't bother setting the query. The penalty
441                      * of a context switch is most likely higher than the gain of a correct query result
442                      *
443                      * If the query is used from a different thread, don't bother creating a multithread
444                      * context - there's no point in doing that as the query would be unusable anyway
445                      */
446                     WARN("Query context not active\n");
447                 } else if(GL_SUPPORT(APPLE_FENCE)) {
448                     GL_EXTCALL(glSetFenceAPPLE(((WineQueryEventData *)This->extendedData)->fenceId));
449                     checkGLcall("glSetFenceAPPLE");
450                 } else if (GL_SUPPORT(NV_FENCE)) {
451                     GL_EXTCALL(glSetFenceNV(((WineQueryEventData *)This->extendedData)->fenceId, GL_ALL_COMPLETED_NV));
452                     checkGLcall("glSetFenceNV");
453                 }
454             } else if(dwIssueFlags & WINED3DISSUE_BEGIN) {
455                 /* Started implicitly at device creation */
456                 ERR("Event query issued with START flag - what to do?\n");
457             }
458         }
459
460         default:
461             /* The fixme is printed when the app asks for the resulting data */
462             WARN("(%p) : Unhandled query type %#x\n", This, This->type);
463             break;
464     }
465
466     if(dwIssueFlags & WINED3DISSUE_BEGIN) {
467         This->state = QUERY_BUILDING;
468     } else {
469         This->state = QUERY_SIGNALLED;
470     }
471
472     return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL.    */
473 }
474
475
476 /**********************************************************
477  * IWineD3DQuery VTbl follows
478  **********************************************************/
479
480 const IWineD3DQueryVtbl IWineD3DQuery_Vtbl =
481 {
482     /*** IUnknown methods ***/
483     IWineD3DQueryImpl_QueryInterface,
484     IWineD3DQueryImpl_AddRef,
485     IWineD3DQueryImpl_Release,
486      /*** IWineD3Dquery methods ***/
487     IWineD3DQueryImpl_GetParent,
488     IWineD3DQueryImpl_GetDevice,
489     IWineD3DQueryImpl_GetData,
490     IWineD3DQueryImpl_GetDataSize,
491     IWineD3DQueryImpl_GetType,
492     IWineD3DQueryImpl_Issue
493 };