2 * IWineD3DQuery implementation
4 * Copyright 2005 Oliver Stieber
5 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6 * Copyright 2009 Henri Verbeet for CodeWeavers.
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.
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.
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
25 #include "wined3d_private.h"
29 * http://www.gris.uni-tuebingen.de/~bartz/Publications/paper/hww98.pdf
30 * http://oss.sgi.com/projects/ogl-sample/registry/ARB/occlusion_query.txt
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
34 #define GLINFO_LOCATION (*gl_info)
36 /* *******************************************
37 IWineD3DQuery IUnknown parts follow
38 ******************************************* */
39 static HRESULT WINAPI
IWineD3DQueryImpl_QueryInterface(IWineD3DQuery
*iface
, REFIID riid
, LPVOID
*ppobj
)
41 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*)iface
;
42 TRACE("(%p)->(%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
43 if (IsEqualGUID(riid
, &IID_IUnknown
)
44 || IsEqualGUID(riid
, &IID_IWineD3DBase
)
45 || IsEqualGUID(riid
, &IID_IWineD3DQuery
)) {
46 IUnknown_AddRef(iface
);
54 static ULONG WINAPI
IWineD3DQueryImpl_AddRef(IWineD3DQuery
*iface
) {
55 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*)iface
;
56 TRACE("(%p) : AddRef increasing from %d\n", This
, This
->ref
);
57 return InterlockedIncrement(&This
->ref
);
60 static ULONG WINAPI
IWineD3DQueryImpl_Release(IWineD3DQuery
*iface
) {
61 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*)iface
;
63 TRACE("(%p) : Releasing from %d\n", This
, This
->ref
);
64 ref
= InterlockedDecrement(&This
->ref
);
66 /* Queries are specific to the GL context that created them. Not
67 * deleting the query will obviously leak it, but that's still better
68 * than potentially deleting a different query with the same id in this
69 * context, and (still) leaking the actual query. */
70 if (This
->type
== WINED3DQUERYTYPE_EVENT
)
72 struct wined3d_event_query
*query
= This
->extendedData
;
74 if (query
->context
) context_free_event_query(query
);
76 else if (This
->type
== WINED3DQUERYTYPE_OCCLUSION
)
78 struct wined3d_occlusion_query
*query
= This
->extendedData
;
80 if (query
->context
) context_free_occlusion_query(query
);
83 HeapFree(GetProcessHeap(), 0, This
->extendedData
);
84 HeapFree(GetProcessHeap(), 0, This
);
89 /* *******************************************
90 IWineD3DQuery IWineD3DQuery parts follow
91 ******************************************* */
92 static HRESULT WINAPI
IWineD3DQueryImpl_GetParent(IWineD3DQuery
*iface
, IUnknown
**parent
)
94 TRACE("iface %p, parent %p.\n", iface
, parent
);
96 *parent
= (IUnknown
*)parent
;
97 IUnknown_AddRef(*parent
);
99 TRACE("Returning %p.\n", *parent
);
104 static HRESULT WINAPI
IWineD3DOcclusionQueryImpl_GetData(IWineD3DQuery
* iface
, void* pData
, DWORD dwSize
, DWORD dwGetDataFlags
) {
105 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*) iface
;
106 struct wined3d_occlusion_query
*query
= This
->extendedData
;
107 IWineD3DDeviceImpl
*device
= This
->device
;
108 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
109 struct wined3d_context
*context
;
115 TRACE("(%p) : type D3DQUERY_OCCLUSION, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This
, pData
, dwSize
, dwGetDataFlags
);
117 if (!query
->context
) This
->state
= QUERY_CREATED
;
119 if (This
->state
== QUERY_CREATED
)
121 /* D3D allows GetData on a new query, OpenGL doesn't. So just invent the data ourselves */
122 TRACE("Query wasn't yet started, returning S_OK\n");
127 if (This
->state
== QUERY_BUILDING
)
129 /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
130 TRACE("Query is building, returning S_FALSE\n");
134 if (!gl_info
->supported
[ARB_OCCLUSION_QUERY
])
136 WARN("(%p) : Occlusion queries not supported. Returning 1.\n", This
);
141 if (query
->context
->tid
!= GetCurrentThreadId())
143 FIXME("%p Wrong thread, returning 1.\n", This
);
148 context
= context_acquire(This
->device
, query
->context
->current_rt
, CTXUSAGE_RESOURCELOAD
);
152 GL_EXTCALL(glGetQueryObjectuivARB(query
->id
, GL_QUERY_RESULT_AVAILABLE_ARB
, &available
));
153 checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT_AVAILABLE)");
154 TRACE("(%p) : available %d.\n", This
, available
);
160 GL_EXTCALL(glGetQueryObjectuivARB(query
->id
, GL_QUERY_RESULT_ARB
, &samples
));
161 checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)");
162 TRACE("(%p) : Returning %d samples.\n", This
, samples
);
174 context_release(context
);
179 static HRESULT WINAPI
IWineD3DEventQueryImpl_GetData(IWineD3DQuery
* iface
, void* pData
, DWORD dwSize
, DWORD dwGetDataFlags
) {
180 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*) iface
;
181 struct wined3d_event_query
*query
= This
->extendedData
;
182 const struct wined3d_gl_info
*gl_info
;
183 struct wined3d_context
*context
;
186 TRACE("(%p) : type D3DQUERY_EVENT, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This
, pData
, dwSize
, dwGetDataFlags
);
188 if (!pData
|| !dwSize
) return S_OK
;
192 TRACE("Query not started, returning TRUE.\n");
198 if (query
->context
->tid
!= GetCurrentThreadId())
200 /* See comment in IWineD3DQuery::Issue, event query codeblock */
201 FIXME("Wrong thread, reporting GPU idle.\n");
207 context
= context_acquire(This
->device
, query
->context
->current_rt
, CTXUSAGE_RESOURCELOAD
);
208 gl_info
= context
->gl_info
;
212 if (gl_info
->supported
[APPLE_FENCE
])
214 *data
= GL_EXTCALL(glTestFenceAPPLE(query
->id
));
215 checkGLcall("glTestFenceAPPLE");
217 else if (gl_info
->supported
[NV_FENCE
])
219 *data
= GL_EXTCALL(glTestFenceNV(query
->id
));
220 checkGLcall("glTestFenceNV");
224 WARN("(%p): reporting GPU idle\n", This
);
230 context_release(context
);
235 static DWORD WINAPI
IWineD3DEventQueryImpl_GetDataSize(IWineD3DQuery
* iface
){
236 TRACE("(%p) : type D3DQUERY_EVENT\n", iface
);
241 static DWORD WINAPI
IWineD3DOcclusionQueryImpl_GetDataSize(IWineD3DQuery
* iface
){
242 TRACE("(%p) : type D3DQUERY_OCCLUSION\n", iface
);
244 return sizeof(DWORD
);
247 static WINED3DQUERYTYPE WINAPI
IWineD3DQueryImpl_GetType(IWineD3DQuery
* iface
){
248 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*)iface
;
253 static HRESULT WINAPI
IWineD3DEventQueryImpl_Issue(IWineD3DQuery
* iface
, DWORD dwIssueFlags
) {
254 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*)iface
;
256 TRACE("(%p) : dwIssueFlags %#x, type D3DQUERY_EVENT\n", This
, dwIssueFlags
);
257 if (dwIssueFlags
& WINED3DISSUE_END
)
259 struct wined3d_event_query
*query
= This
->extendedData
;
260 const struct wined3d_gl_info
*gl_info
;
261 struct wined3d_context
*context
;
265 if (query
->context
->tid
!= GetCurrentThreadId())
267 context_free_event_query(query
);
268 context
= context_acquire(This
->device
, NULL
, CTXUSAGE_RESOURCELOAD
);
269 context_alloc_event_query(context
, query
);
273 context
= context_acquire(This
->device
, query
->context
->current_rt
, CTXUSAGE_RESOURCELOAD
);
278 context
= context_acquire(This
->device
, NULL
, CTXUSAGE_RESOURCELOAD
);
279 context_alloc_event_query(context
, query
);
282 gl_info
= context
->gl_info
;
286 if (gl_info
->supported
[APPLE_FENCE
])
288 GL_EXTCALL(glSetFenceAPPLE(query
->id
));
289 checkGLcall("glSetFenceAPPLE");
291 else if (gl_info
->supported
[NV_FENCE
])
293 GL_EXTCALL(glSetFenceNV(query
->id
, GL_ALL_COMPLETED_NV
));
294 checkGLcall("glSetFenceNV");
299 context_release(context
);
301 else if(dwIssueFlags
& WINED3DISSUE_BEGIN
)
303 /* Started implicitly at device creation */
304 ERR("Event query issued with START flag - what to do?\n");
307 if(dwIssueFlags
& WINED3DISSUE_BEGIN
) {
308 This
->state
= QUERY_BUILDING
;
310 This
->state
= QUERY_SIGNALLED
;
316 static HRESULT WINAPI
IWineD3DOcclusionQueryImpl_Issue(IWineD3DQuery
* iface
, DWORD dwIssueFlags
) {
317 IWineD3DQueryImpl
*This
= (IWineD3DQueryImpl
*)iface
;
318 IWineD3DDeviceImpl
*device
= This
->device
;
319 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
321 if (gl_info
->supported
[ARB_OCCLUSION_QUERY
])
323 struct wined3d_occlusion_query
*query
= This
->extendedData
;
324 struct wined3d_context
*context
;
326 /* This is allowed according to msdn and our tests. Reset the query and restart */
327 if (dwIssueFlags
& WINED3DISSUE_BEGIN
)
329 if (This
->state
== QUERY_BUILDING
)
331 if (query
->context
->tid
!= GetCurrentThreadId())
333 FIXME("Wrong thread, can't restart query.\n");
335 context_free_occlusion_query(query
);
336 context
= context_acquire(This
->device
, NULL
, CTXUSAGE_RESOURCELOAD
);
337 context_alloc_occlusion_query(context
, query
);
341 context
= context_acquire(This
->device
, query
->context
->current_rt
, CTXUSAGE_RESOURCELOAD
);
344 GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB
));
345 checkGLcall("glEndQuery()");
351 if (query
->context
) context_free_occlusion_query(query
);
352 context
= context_acquire(This
->device
, NULL
, CTXUSAGE_RESOURCELOAD
);
353 context_alloc_occlusion_query(context
, query
);
357 GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB
, query
->id
));
358 checkGLcall("glBeginQuery()");
361 context_release(context
);
363 if (dwIssueFlags
& WINED3DISSUE_END
) {
364 /* Msdn says _END on a non-building occlusion query returns an error, but
365 * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
366 * generating an error
368 if (This
->state
== QUERY_BUILDING
)
370 if (query
->context
->tid
!= GetCurrentThreadId())
372 FIXME("Wrong thread, can't end query.\n");
376 context
= context_acquire(This
->device
, query
->context
->current_rt
, CTXUSAGE_RESOURCELOAD
);
379 GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB
));
380 checkGLcall("glEndQuery()");
383 context_release(context
);
388 FIXME("(%p) : Occlusion queries not supported\n", This
);
391 if(dwIssueFlags
& WINED3DISSUE_BEGIN
) {
392 This
->state
= QUERY_BUILDING
;
394 This
->state
= QUERY_SIGNALLED
;
396 return WINED3D_OK
; /* can be WINED3DERR_INVALIDCALL. */
399 static const IWineD3DQueryVtbl IWineD3DEventQuery_Vtbl
=
401 /*** IUnknown methods ***/
402 IWineD3DQueryImpl_QueryInterface
,
403 IWineD3DQueryImpl_AddRef
,
404 IWineD3DQueryImpl_Release
,
405 /*** IWineD3Dquery methods ***/
406 IWineD3DQueryImpl_GetParent
,
407 IWineD3DEventQueryImpl_GetData
,
408 IWineD3DEventQueryImpl_GetDataSize
,
409 IWineD3DQueryImpl_GetType
,
410 IWineD3DEventQueryImpl_Issue
413 static const IWineD3DQueryVtbl IWineD3DOcclusionQuery_Vtbl
=
415 /*** IUnknown methods ***/
416 IWineD3DQueryImpl_QueryInterface
,
417 IWineD3DQueryImpl_AddRef
,
418 IWineD3DQueryImpl_Release
,
419 /*** IWineD3Dquery methods ***/
420 IWineD3DQueryImpl_GetParent
,
421 IWineD3DOcclusionQueryImpl_GetData
,
422 IWineD3DOcclusionQueryImpl_GetDataSize
,
423 IWineD3DQueryImpl_GetType
,
424 IWineD3DOcclusionQueryImpl_Issue
427 HRESULT
query_init(IWineD3DQueryImpl
*query
, IWineD3DDeviceImpl
*device
,
428 WINED3DQUERYTYPE type
, IUnknown
*parent
)
430 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
434 case WINED3DQUERYTYPE_OCCLUSION
:
435 TRACE("Occlusion query.\n");
436 if (!gl_info
->supported
[ARB_OCCLUSION_QUERY
])
438 WARN("Unsupported in local OpenGL implementation: ARB_OCCLUSION_QUERY.\n");
439 return WINED3DERR_NOTAVAILABLE
;
441 query
->lpVtbl
= &IWineD3DOcclusionQuery_Vtbl
;
442 query
->extendedData
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_occlusion_query
));
443 if (!query
->extendedData
)
445 ERR("Failed to allocate occlusion query extended data.\n");
446 return E_OUTOFMEMORY
;
448 ((struct wined3d_occlusion_query
*)query
->extendedData
)->context
= NULL
;
451 case WINED3DQUERYTYPE_EVENT
:
452 TRACE("Event query.\n");
453 if (!gl_info
->supported
[NV_FENCE
] && !gl_info
->supported
[APPLE_FENCE
])
455 /* Half-Life 2 needs this query. It does not render the main
456 * menu correctly otherwise. Pretend to support it, faking
457 * this query does not do much harm except potentially
458 * lowering performance. */
459 FIXME("Event query: Unimplemented, but pretending to be supported.\n");
461 query
->lpVtbl
= &IWineD3DEventQuery_Vtbl
;
462 query
->extendedData
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_event_query
));
463 if (!query
->extendedData
)
465 ERR("Failed to allocate event query extended data.\n");
466 return E_OUTOFMEMORY
;
468 ((struct wined3d_event_query
*)query
->extendedData
)->context
= NULL
;
471 case WINED3DQUERYTYPE_VCACHE
:
472 case WINED3DQUERYTYPE_RESOURCEMANAGER
:
473 case WINED3DQUERYTYPE_VERTEXSTATS
:
474 case WINED3DQUERYTYPE_TIMESTAMP
:
475 case WINED3DQUERYTYPE_TIMESTAMPDISJOINT
:
476 case WINED3DQUERYTYPE_TIMESTAMPFREQ
:
477 case WINED3DQUERYTYPE_PIPELINETIMINGS
:
478 case WINED3DQUERYTYPE_INTERFACETIMINGS
:
479 case WINED3DQUERYTYPE_VERTEXTIMINGS
:
480 case WINED3DQUERYTYPE_PIXELTIMINGS
:
481 case WINED3DQUERYTYPE_BANDWIDTHTIMINGS
:
482 case WINED3DQUERYTYPE_CACHEUTILIZATION
:
484 FIXME("Unhandled query type %#x.\n", type
);
485 return WINED3DERR_NOTAVAILABLE
;
489 query
->state
= QUERY_CREATED
;
490 query
->device
= device
;
491 query
->parent
= parent
;