wined3d: A more detailed occlusion query test and fixes.
diff --git a/dlls/d3d9/tests/query.c b/dlls/d3d9/tests/query.c
index 9f2b5b3..452991d 100644
--- a/dlls/d3d9/tests/query.c
+++ b/dlls/d3d9/tests/query.c
@@ -135,6 +135,117 @@
if(pDevice) IDirect3DDevice9_Release(pDevice);
}
+static void test_occlusion_query_states(IDirect3D9 *pD3d, HWND hwnd)
+{
+
+ HRESULT hr;
+
+ IDirect3DDevice9 *pDevice = NULL;
+ D3DPRESENT_PARAMETERS d3dpp;
+ D3DDISPLAYMODE d3ddm;
+ IDirect3DQuery9 *pQuery = NULL;
+ BYTE *data = NULL;
+ float point[3] = {0.0, 0.0, 0.0};
+ unsigned int count = 0;
+
+ IDirect3D9_GetAdapterDisplayMode( pD3d, D3DADAPTER_DEFAULT, &d3ddm );
+ ZeroMemory( &d3dpp, sizeof(d3dpp) );
+ d3dpp.Windowed = TRUE;
+ d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3dpp.BackBufferFormat = d3ddm.Format;
+
+ hr = IDirect3D9_CreateDevice( pD3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice );
+ ok(SUCCEEDED(hr) || hr == D3DERR_NOTAVAILABLE, "Failed to create IDirect3D9Device (%s)\n", DXGetErrorString9(hr));
+ if (FAILED(hr))
+ {
+ skip("Failed to create a d3d device\n");
+ goto cleanup;
+ }
+
+ hr = IDirect3DDevice9_CreateQuery(pDevice, D3DQUERYTYPE_OCCLUSION, &pQuery);
+ ok(hr == D3D_OK || D3DERR_NOTAVAILABLE,
+ "IDirect3DDevice9_CreateQuery returned unexpected return value %s\n", DXGetErrorString9(hr));
+ if(!pQuery) {
+ skip("Occlusion queries not supported\n");
+ goto cleanup;
+ }
+
+ data = HeapAlloc(GetProcessHeap(), 0, IDirect3DQuery9_GetDataSize(pQuery));
+
+ hr = IDirect3DQuery9_GetData(pQuery, NULL, 0, D3DGETDATA_FLUSH);
+ ok(hr == S_OK, "IDirect3DQuery9_GetData(NULL) on a new query returned %s\n",
+ DXGetErrorString9(hr));
+ hr = IDirect3DQuery9_GetData(pQuery, data, IDirect3DQuery9_GetDataSize(pQuery), D3DGETDATA_FLUSH);
+ ok(hr == S_OK, "IDirect3DQuery9_GetData on a new query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_END);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DISSUE_END) on a new not yet started query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_BEGIN);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DISSUE_BEGIN) on a new not yet started query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_BEGIN);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DQUERY_BEGIN) on a started query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = IDirect3DQuery9_GetData(pQuery, NULL, 0, D3DGETDATA_FLUSH);
+ ok(hr == S_FALSE, "IDirect3DQuery9_GetData(NULL) on a started query returned %s\n",
+ DXGetErrorString9(hr));
+ hr = IDirect3DQuery9_GetData(pQuery, data, IDirect3DQuery9_GetDataSize(pQuery), D3DGETDATA_FLUSH);
+ ok(hr == S_FALSE, "IDirect3DQuery9_GetData on a started query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = IDirect3DDevice9_SetFVF(pDevice, D3DFVF_XYZ);
+ ok(hr == D3D_OK, "IDirect3DDevice9_SetFVF returned %s\n", DXGetErrorString9(hr));
+ hr = IDirect3DDevice9_BeginScene(pDevice);
+ ok(hr == D3D_OK, "IDirect3DDevice9_BeginScene returned %s\n", DXGetErrorString9(hr));
+ if(SUCCEEDED(hr)) {
+ hr = IDirect3DDevice9_DrawPrimitiveUP(pDevice, D3DPT_POINTLIST, 1, point, 3 * sizeof(float));
+ ok(hr == D3D_OK, "IDirect3DDevice9_DrawPrimitiveUP returned %s\n", DXGetErrorString9(hr));
+ hr = IDirect3DDevice9_EndScene(pDevice);
+ ok(hr == D3D_OK, "IDirect3DDevice9_EndScene returned %s\n", DXGetErrorString9(hr));
+ }
+
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_END);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DISSUE_END) on a started query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = S_FALSE;
+ while(hr == S_FALSE && count < 500) {
+ hr = IDirect3DQuery9_GetData(pQuery, NULL, 0, D3DGETDATA_FLUSH);
+ ok(hr == S_OK || hr == S_FALSE, "IDirect3DQuery9_GetData on a ended query returned %s\n",
+ DXGetErrorString9(hr));
+ count++;
+ if(hr == S_FALSE) Sleep(10);
+ }
+ ok(hr == S_OK, "Occlusion query did not finish\n");
+
+ hr = IDirect3DQuery9_GetData(pQuery, data, IDirect3DQuery9_GetDataSize(pQuery), D3DGETDATA_FLUSH);
+ ok(hr == S_OK, "IDirect3DQuery9_GetData on a ended query returned %s\n",
+ DXGetErrorString9(hr));
+ hr = IDirect3DQuery9_GetData(pQuery, data, IDirect3DQuery9_GetDataSize(pQuery), D3DGETDATA_FLUSH);
+ ok(hr == S_OK, "IDirect3DQuery9_GetData a 2nd time on a ended query returned %s\n",
+ DXGetErrorString9(hr));
+
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_BEGIN);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DISSUE_BEGIN) on a new not yet started query returned %s\n",
+ DXGetErrorString9(hr));
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_END);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DISSUE_END) on a started query returned %s\n",
+ DXGetErrorString9(hr));
+ hr = IDirect3DQuery9_Issue(pQuery, D3DISSUE_END);
+ ok(hr == D3D_OK, "IDirect3DQuery9_Issue(D3DISSUE_END) on a ended query returned %s\n",
+ DXGetErrorString9(hr));
+
+ cleanup:
+ HeapFree(GetProcessHeap(), 0, data);
+ if(pDevice) IDirect3DDevice9_Release(pDevice);
+}
+
START_TEST(query)
{
HMODULE d3d9_handle = LoadLibraryA( "d3d9.dll" );
@@ -166,6 +277,7 @@
}
test_query_support(pD3d, hwnd);
+ test_occlusion_query_states(pD3d, hwnd);
DestroyWindow(hwnd);
IDirect3D9_Release(pD3d);
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index e5f1c96..622d892 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -1259,6 +1259,7 @@
D3DCREATEOBJECTINSTANCE(object, Query)
object->type = Type;
+ object->state = QUERY_CREATED;
/* allocated the 'extended' data based on the type of query requested */
switch(Type){
case WINED3DQUERYTYPE_OCCLUSION:
diff --git a/dlls/wined3d/query.c b/dlls/wined3d/query.c
index 7c91f2e..55c4d00 100644
--- a/dlls/wined3d/query.c
+++ b/dlls/wined3d/query.c
@@ -111,13 +111,6 @@
TRACE("(%p) : type %#x, pData %p, dwSize %#x, dwGetDataFlags %#x\n", This, This->type, pData, dwSize, dwGetDataFlags);
- if(dwSize == 0){
- /*you can use this method to poll the resource for the query status*/
- /*We return success(S_OK) if we support a feature, and faikure(S_FALSE) if we don't, just return success and fluff it for now*/
- return S_OK;
- }else{
- }
-
switch (This->type){
case WINED3DQUERYTYPE_VCACHE:
@@ -125,6 +118,7 @@
WINED3DDEVINFO_VCACHE *data = (WINED3DDEVINFO_VCACHE *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_VCACHE\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->Pattern = WINEMAKEFOURCC('C','A','C','H');
data->OptMethod = 0; /*0 get longest strips, 1 optimize vertex cache*/
data->CacheSize = 0; /*cache size, only required if OptMethod == 1*/
@@ -137,6 +131,7 @@
WINED3DDEVINFO_RESOURCEMANAGER *data = (WINED3DDEVINFO_RESOURCEMANAGER *)pData;
int i;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_RESOURCEMANAGER\n", This);
+ if(pData == NULL || dwSize == 0) break;
for(i = 0; i < WINED3DRTYPECOUNT; i++){
/*I'm setting the default values to 1 so as to reduce the risk of a div/0 in the caller*/
/* isTextureResident could be used to get some of this infomration */
@@ -159,6 +154,7 @@
{
WINED3DDEVINFO_VERTEXSTATS *data = (WINED3DDEVINFO_VERTEXSTATS *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_VERTEXSTATS\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->NumRenderedTriangles = 1;
data->NumExtraClippingTriangles = 1;
@@ -168,7 +164,9 @@
{
BOOL* data = pData;
WineD3DContext *ctx = ((WineQueryEventData *)This->extendedData)->ctx;
- if(ctx != This->wineD3DDevice->activeContext || ctx->tid != GetCurrentThreadId()) {
+ if(pData == NULL || dwSize == 0) {
+ break;
+ } if(ctx != This->wineD3DDevice->activeContext || ctx->tid != GetCurrentThreadId()) {
/* See comment in IWineD3DQuery::Issue, event query codeblock */
WARN("Query context not active, reporting GPU idle\n");
*data = TRUE;
@@ -187,7 +185,17 @@
case WINED3DQUERYTYPE_OCCLUSION:
{
DWORD* data = pData;
- if (GL_SUPPORT(ARB_OCCLUSION_QUERY) &&
+
+ if(This->state == QUERY_CREATED) {
+ /* D3D allows GetData on a new query, opengl doesn't. So just invent the data outselves */
+ TRACE("Query wasn't yet started, returning S_OK\n");
+ res = S_OK;
+ if(data) *data = 0;
+ } else if(This->state == QUERY_BUILDING) {
+ /* Msdn says this returns an error, but our tests show that S_FALSE is returned */
+ TRACE("Query is building, returning S_FALSE\n");
+ res = S_FALSE;
+ } else if (GL_SUPPORT(ARB_OCCLUSION_QUERY) &&
((WineQueryOcclusionData *)This->extendedData)->ctx == This->wineD3DDevice->activeContext &&
This->wineD3DDevice->activeContext->tid == GetCurrentThreadId()) {
GLuint available;
@@ -199,10 +207,12 @@
TRACE("(%p) : available %d.\n", This, available);
if (available) {
- GL_EXTCALL(glGetQueryObjectuivARB(queryId, GL_QUERY_RESULT_ARB, &samples));
- checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)\n");
- TRACE("(%p) : Returning %d samples.\n", This, samples);
- *data = samples;
+ if(data) {
+ GL_EXTCALL(glGetQueryObjectuivARB(queryId, GL_QUERY_RESULT_ARB, &samples));
+ checkGLcall("glGetQueryObjectuivARB(GL_QUERY_RESULT)\n");
+ TRACE("(%p) : Returning %d samples.\n", This, samples);
+ *data = samples;
+ }
res = S_OK;
} else {
res = S_FALSE;
@@ -218,6 +228,7 @@
{
UINT64* data = pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_TIMESTAMP\n", This);
+ if(pData == NULL || dwSize == 0) break;
*data = 1; /*Don't know what this is supposed to be*/
}
break;
@@ -225,6 +236,7 @@
{
BOOL* data = pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_TIMESTAMPDISJOINT\n", This);
+ if(pData == NULL || dwSize == 0) break;
*data = FALSE; /*Don't know what this is supposed to be*/
}
break;
@@ -232,6 +244,7 @@
{
UINT64* data = pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_TIMESTAMPFREQ\n", This);
+ if(pData == NULL || dwSize == 0) break;
*data = 1; /*Don't know what this is supposed to be*/
}
break;
@@ -239,6 +252,7 @@
{
WINED3DDEVINFO_PIPELINETIMINGS *data = (WINED3DDEVINFO_PIPELINETIMINGS *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_PIPELINETIMINGS\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->VertexProcessingTimePercent = 1.0f;
data->PixelProcessingTimePercent = 1.0f;
@@ -251,6 +265,7 @@
WINED3DDEVINFO_INTERFACETIMINGS *data = (WINED3DDEVINFO_INTERFACETIMINGS *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_INTERFACETIMINGS\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->WaitingForGPUToUseApplicationResourceTimePercent = 1.0f;
data->WaitingForGPUToAcceptMoreCommandsTimePercent = 1.0f;
data->WaitingForGPUToStayWithinLatencyTimePercent = 1.0f;
@@ -264,6 +279,7 @@
WINED3DDEVINFO_STAGETIMINGS *data = (WINED3DDEVINFO_STAGETIMINGS *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_VERTEXTIMINGS\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->MemoryProcessingPercent = 50.0f;
data->ComputationProcessingPercent = 50.0f;
@@ -274,6 +290,7 @@
WINED3DDEVINFO_STAGETIMINGS *data = (WINED3DDEVINFO_STAGETIMINGS *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_PIXELTIMINGS\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->MemoryProcessingPercent = 50.0f;
data->ComputationProcessingPercent = 50.0f;
}
@@ -283,6 +300,7 @@
WINED3DDEVINFO_BANDWIDTHTIMINGS *data = (WINED3DDEVINFO_BANDWIDTHTIMINGS *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_BANDWIDTHTIMINGS\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->MaxBandwidthUtilized = 1.0f;
data->FrontEndUploadMemoryUtilizedPercent = 1.0f;
data->VertexRateUtilizedPercent = 1.0f;
@@ -295,6 +313,7 @@
WINED3DDEVINFO_CACHEUTILIZATION *data = (WINED3DDEVINFO_CACHEUTILIZATION *)pData;
FIXME("(%p): Unimplemented query WINED3DQUERYTYPE_CACHEUTILIZATION\n", This);
+ if(pData == NULL || dwSize == 0) break;
data->TextureCacheHitRate = 1.0f;
data->PostTransformVertexCacheHitRate = 1.0f;
}
@@ -387,13 +406,25 @@
if(ctx != This->wineD3DDevice->activeContext || ctx->tid != GetCurrentThreadId()) {
WARN("Not the owning context, can't start query\n");
} else {
+ /* This is allowed according to msdn and our tests. Reset the query and restart */
if (dwIssueFlags & WINED3DISSUE_BEGIN) {
+ if(This->state == QUERY_BUILDING) {
+ GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
+ checkGLcall("glEndQuery()");
+ }
+
GL_EXTCALL(glBeginQueryARB(GL_SAMPLES_PASSED_ARB, ((WineQueryOcclusionData *)This->extendedData)->queryId));
checkGLcall("glBeginQuery()");
}
if (dwIssueFlags & WINED3DISSUE_END) {
- GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
- checkGLcall("glEndQuery()");
+ /* Msdn says _END on a non-building occlusion query returns an error, but
+ * our tests show that it returns OK. But OpenGL doesn't like it, so avoid
+ * generating an error
+ */
+ if(This->state == QUERY_BUILDING) {
+ GL_EXTCALL(glEndQueryARB(GL_SAMPLES_PASSED_ARB));
+ checkGLcall("glEndQuery()");
+ }
}
}
} else {
@@ -432,6 +463,12 @@
break;
}
+ if(dwIssueFlags & WINED3DISSUE_BEGIN) {
+ This->state = QUERY_BUILDING;
+ } else {
+ This->state = QUERY_SIGNALLED;
+ }
+
return WINED3D_OK; /* can be WINED3DERR_INVALIDCALL. */
}
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 86a6bb8..6806bfe 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1443,6 +1443,15 @@
extern const IWineD3DStateBlockVtbl IWineD3DStateBlock_Vtbl;
+/* Direct3D terminology with little modifications. We do not have an issued state
+ * because only the driver knows about it, but we have a created state because d3d
+ * allows GetData on a created issue, but opengl doesn't
+ */
+enum query_state {
+ QUERY_CREATED,
+ QUERY_SIGNALLED,
+ QUERY_BUILDING
+};
/*****************************************************************************
* IWineD3DQueryImpl implementation structure (extends IUnknown)
*/
@@ -1460,6 +1469,7 @@
#endif
/* IWineD3DQuery fields */
+ enum query_state state;
WINED3DQUERYTYPE type;
/* TODO: Think about using a IUnknown instead of a void* */
void *extendedData;