diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 366cdd188..ba1cedb79 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -28,6 +28,7 @@ #include "common\util\hasher.h" #include +#include // prevent name collisions namespace xboxkrnl @@ -114,6 +115,10 @@ static IDirect3DIndexBuffer *g_pClosingLineLoopHostIndexBuffer = nullptr; static IDirect3DIndexBuffer *g_pQuadToTriangleHostIndexBuffer = nullptr; static IDirect3DSurface *g_pDefaultHostDepthBufferSurface = nullptr; +static bool g_bEnableHostQueryVisibilityTest = true; +static std::stack g_HostQueryVisibilityTests; +static std::map g_HostVisibilityTestMap; + // cached Direct3D state variable(s) static size_t g_QuadToTriangleHostIndexBuffer_Size = 0; // = NrOfQuadIndices static INDEX16 *g_pQuadToTriangleIndexData = nullptr; @@ -2448,7 +2453,20 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (callback event)"); } } else { - LOG_TEST_CASE("Can't CreateQuery on host!"); + LOG_TEST_CASE("Can't CreateQuery(D3DQUERYTYPE_EVENT) on host!"); + } + + // Can host driver create occlusion queries? + g_bEnableHostQueryVisibilityTest = false; + if (SUCCEEDED(g_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, nullptr))) { + // Is host GPU query creation enabled? + if (!g_bHack_DisableHostGPUQueries) { + g_bEnableHostQueryVisibilityTest = true; + } else { + LOG_TEST_CASE("Disabled D3DQUERYTYPE_OCCLUSION on host!"); + } + } else { + LOG_TEST_CASE("Can't CreateQuery(D3DQUERYTYPE_OCCLUSION) on host!"); } hRet = g_pD3DDevice->CreateVertexBuffer @@ -3234,11 +3252,30 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_EndPush)(DWORD *pPush) // ****************************************************************** // * patch: D3DDevice_BeginVisibilityTest // ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_BeginVisibilityTest)() +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_BeginVisibilityTest)() { LOG_FUNC(); - LOG_UNIMPLEMENTED(); + if (g_bEnableHostQueryVisibilityTest) { + // Create a D3D occlusion query to handle "visibility test" with + IDirect3DQuery* pHostQueryVisibilityTest = nullptr; + HRESULT hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pHostQueryVisibilityTest); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (visibility test)"); + if (pHostQueryVisibilityTest != nullptr) { + hRet = pHostQueryVisibilityTest->Issue(D3DISSUE_BEGIN); + DEBUG_D3DRESULT(hRet, "g_pHostQueryVisibilityTest->Issue(D3DISSUE_BEGIN)"); + if (SUCCEEDED(hRet)) { + g_HostQueryVisibilityTests.push(pHostQueryVisibilityTest); + } else { + LOG_TEST_CASE("Failed to issue query"); + pHostQueryVisibilityTest->Release(); + } + + pHostQueryVisibilityTest = nullptr; + } + } + + return D3D_OK; } // LTCG specific D3DDevice_EndVisibilityTest function... @@ -3267,7 +3304,30 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_EndVisibilityTest) { LOG_FUNC_ONE_ARG(Index); - LOG_UNIMPLEMENTED(); + if (g_bEnableHostQueryVisibilityTest) { + // Check that the dedicated storage for the given Index isn't in use + if (g_HostVisibilityTestMap[Index] != nullptr) { + return E_OUTOFMEMORY; + } + + if (g_HostQueryVisibilityTests.empty()) { + return 2088; // visibility test incomplete (a prior BeginVisibilityTest call is needed) + } + + IDirect3DQuery* pHostQueryVisibilityTest = g_HostQueryVisibilityTests.top(); + g_HostQueryVisibilityTests.pop(); + assert(pHostQueryVisibilityTest != nullptr); + + HRESULT hRet = pHostQueryVisibilityTest->Issue(D3DISSUE_END); + DEBUG_D3DRESULT(hRet, "g_pHostQueryVisibilityTest->Issue(D3DISSUE_END)"); + if (hRet == D3D_OK) { + // Associate the result of this call with the given Index + g_HostVisibilityTestMap[Index] = pHostQueryVisibilityTest; + } else { + LOG_TEST_CASE("Failed to issue query"); + pHostQueryVisibilityTest->Release(); + } + } return D3D_OK; } @@ -3301,13 +3361,32 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVisibilityTestResult) LOG_FUNC_ARG(pTimeStamp) LOG_FUNC_END; - // TODO: actually emulate this!? + if (g_bEnableHostQueryVisibilityTest) { + IDirect3DQuery* pHostQueryVisibilityTest = g_HostVisibilityTestMap[Index]; + if (pHostQueryVisibilityTest == nullptr) { + return E_OUTOFMEMORY; + } - if(pResult != 0) - *pResult = 640*480; + // In order to prevent an endless loop if the D3D device becomes lost, we pass + // the D3DGETDATA_FLUSH flag. This tells GetData to return D3DERR_DEVICELOST if + // such a situation occurs, and break out of the loop as a result. + // Note: By Cxbx's design, we cannot do drawing within this while loop in order + // to further prevent any other endless loop situations. + while (S_FALSE == pHostQueryVisibilityTest->GetData(pResult, sizeof(DWORD), D3DGETDATA_FLUSH)); - if(pTimeStamp != 0) - *pTimeStamp = 0; + g_HostVisibilityTestMap[Index] = nullptr; + pHostQueryVisibilityTest->Release(); + } else { + // Fallback to old faked result when there's no host occlusion query : + if (pResult != xbnullptr) { + *pResult = 640 * 480; // TODO : Use actual backbuffer dimensions + } + } + + if (pTimeStamp != xbnullptr) { + LOG_TEST_CASE("requested value for pTimeStamp"); + *pTimeStamp = sizeof(DWORD); // TODO : This should be an incrementing GPU-memory based DWORD-aligned memory address + } return D3D_OK; } diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.h b/src/core/hle/D3D8/Direct3D9/Direct3D9.h index 95a5317c2..a4c65d945 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.h +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.h @@ -161,7 +161,7 @@ VOID WINAPI EMUPATCH(D3DDevice_EndPush)(DWORD *pPush); // ****************************************************************** // * patch: D3DDevice_BeginVisibilityTest // ****************************************************************** -VOID WINAPI EMUPATCH(D3DDevice_BeginVisibilityTest)(); +HRESULT WINAPI EMUPATCH(D3DDevice_BeginVisibilityTest)(); // ****************************************************************** // * patch: D3DDevice_EndVisibilityTest