diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 8f12aae9a..d05a9f3d0 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -301,6 +301,7 @@ g_EmuCDPD = {0}; XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture_4__LTCG_eax_pTexture, (xbox::dword_xt) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture_4, (xbox::X_D3DBaseTexture*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShader, (xbox::dword_xt) ); \ + XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShader_0, () ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShaderInput, (xbox::dword_xt, xbox::uint_xt, xbox::X_STREAMINPUT*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetViewport, (CONST xbox::X_D3DVIEWPORT8*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform, (D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \ @@ -3060,6 +3061,8 @@ void Direct3D_CreateDevice_Start xbox::X_D3DPRESENT_PARAMETERS *pPresentationParameters ) { + CxbxVertexShaderSetFlags(); + if (!XboxRenderStates.Init()) { CxbxKrnlCleanup("Failed to init XboxRenderStates"); } @@ -4301,8 +4304,6 @@ void CxbxImpl_SetViewPort(xbox::X_D3DVIEWPORT8* pViewport) HostViewPort.Y = static_cast(HostViewPort.Y * g_Xbox_MultiSampleYScale); HostViewPort.Width = static_cast(HostViewPort.Width * g_Xbox_MultiSampleXScale); HostViewPort.Height = static_cast(HostViewPort.Height * g_Xbox_MultiSampleYScale); - HostViewPort.X = static_cast(HostViewPort.X * g_Xbox_MultiSampleXScale); - HostViewPort.Y = static_cast(HostViewPort.Y * g_Xbox_MultiSampleYScale); // Since Width and Height are DWORD, adding GetMultiSampleOffset 0.0f or 0.5f makes no sense HRESULT hRet = g_pD3DDevice->SetViewport(&HostViewPort); @@ -6604,22 +6605,51 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVertexShader) { LOG_FUNC_ONE_ARG(Handle); - if (XB_TRMP(D3DDevice_SetVertexShader)) - XB_TRMP(D3DDevice_SetVertexShader)(Handle); + // This trampoline leads to calling D3DDevice_LoadVertexShader and D3DDevice_SelectVertexShader + // Please raise the alarm if this is ever not the case + XB_TRMP(D3DDevice_SetVertexShader)(Handle); CxbxImpl_SetVertexShader(Handle); } +// Overload for logging +static void D3DDevice_SetVertexShader_0 +( + xbox::dword_xt Handle +) +{ + LOG_FUNC_ONE_ARG(Handle); +} + // This uses a custom calling convention where Handle is passed in EBX // Test-case: NASCAR Heat 2002 -xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVertexShader_0)() +__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVertexShader_0)() { dword_xt Handle; - __asm mov Handle, ebx + __asm { + push ebp + mov ebp, esp + sub esp, __LOCAL_SIZE + mov Handle, ebx + } - LOG_FUNC_ONE_ARG(Handle); + // Log + D3DDevice_SetVertexShader_0(Handle); + + // This trampoline leads to calling D3DDevice_LoadVertexShader and D3DDevice_SelectVertexShader + // Please raise the alarm if this is ever not the case + __asm { + mov ebx, Handle + call XB_TRMP(D3DDevice_SetVertexShader_0) + } CxbxImpl_SetVertexShader(Handle); + + __asm { + mov esp, ebp + pop ebp + ret + } } // TODO : Move to own file diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index 5b94f4f5d..5bfcc192d 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -1149,14 +1149,16 @@ struct X_D3DVertexShader // X_D3DVertexShader.Flags values : #define X_VERTEXSHADER_FLAG_WRITE (1 << 0) // = 0x0001 // Set for Xbox ShaderType != X_VST_NORMAL #define X_VERTEXSHADER_FLAG_PASSTHROUGH (1 << 1) // = 0x0002 -#define X_VERTEXSHADER_FLAG_UNKNOWN (1 << 2) // = 0x0004 // Test case: Amped #define X_VERTEXSHADER_FLAG_STATE (1 << 3) // = 0x0008 // Set for Xbox ShaderType == X_VST_STATE -#define X_VERTEXSHADER_FLAG_PROGRAM (1 << 4) // = 0x0010 // Set when X_D3DVertexShader was created with assigned function data +#define X_VERTEXSHADER_FLAG_PROGRAM (1 << 4) // = 0x0010 // Set when X_D3DVertexShader was created with assigned function data; introduced after XDK 3948; don't use directly, use g_X_VERTEXSHADER_FLAG_PROGRAM instead #define X_VERTEXSHADER_FLAG_HASDIFFUSE (1 << 10) // = 0x0400 Corresponds to X_D3DUSAGE_PERSISTENTDIFFUSE #define X_VERTEXSHADER_FLAG_HASSPECULAR (1 << 11) // = 0x0800 Corresponds to X_D3DUSAGE_PERSISTENTSPECULAR #define X_VERTEXSHADER_FLAG_HASBACKDIFFUSE (1 << 12) // = 0x1000 Corresponds to X_D3DUSAGE_PERSISTENTBACKDIFFUSE #define X_VERTEXSHADER_FLAG_HASBACKSPECULAR (1 << 13) // = 0x2000 Corresponds to X_D3DUSAGE_PERSISTENTBACKSPECULAR +// X_D3DVertexShader3948.Flags values, only those which differ from the above : +#define X_VERTEXSHADER3948_FLAG_PROGRAM (1 << 2) // = 0x0004 // Test case: Amped, NASCAR Heat 2002; don't use directly, use g_X_VERTEXSHADER_FLAG_PROGRAM instead + // vertex shader input registers for fixed function vertex shader // Name Register number D3DFVF diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index 506422feb..f8d54d697 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -77,6 +77,23 @@ extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp static xbox::X_D3DVertexShader g_Xbox_VertexShader_ForFVF = {}; +static uint32_t g_X_VERTEXSHADER_FLAG_PROGRAM; // X_VERTEXSHADER_FLAG_PROGRAM flag varies per XDK, so it is set on runtime +static uint32_t g_X_VERTEXSHADER_FLAG_VALID_MASK; // For a test case + +void CxbxVertexShaderSetFlags() +{ + // Set an appropriate X_VERTEXSHADER_FLAG_PROGRAM version and mask off the "wrong" one + // to allow the test case to spot it + if (g_LibVersion_D3D8 <= 3948) { + g_X_VERTEXSHADER_FLAG_PROGRAM = X_VERTEXSHADER3948_FLAG_PROGRAM; + g_X_VERTEXSHADER_FLAG_VALID_MASK = ~X_VERTEXSHADER_FLAG_PROGRAM; + } + else { + g_X_VERTEXSHADER_FLAG_PROGRAM = X_VERTEXSHADER_FLAG_PROGRAM; + g_X_VERTEXSHADER_FLAG_VALID_MASK = ~X_VERTEXSHADER3948_FLAG_PROGRAM; + } +} + // Converts an Xbox FVF shader handle to X_D3DVertexShader // Note : Temporary, until we reliably locate the Xbox internal state for this // See D3DXDeclaratorFromFVF docs https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxdeclaratorfromfvf @@ -272,7 +289,7 @@ xbox::X_D3DVertexShader* GetXboxVertexShader() return pXboxVertexShader; } -static bool UseXboxD3DVertexShaderTypeForVersion3948(xbox::X_D3DVertexShader* pXboxVertexShader) +static bool UseXboxD3DVertexShaderTypeForVersion3948(const xbox::X_D3DVertexShader* pXboxVertexShader) { // Don't check XDK version for our internal FVF vertex shader // because g_Xbox_VertexShader_ForFVF is an internal variable @@ -287,7 +304,7 @@ static bool UseXboxD3DVertexShaderTypeForVersion3948(xbox::X_D3DVertexShader* pX static xbox::X_VERTEXATTRIBUTEFORMAT* CxbxGetVertexShaderAttributes(xbox::X_D3DVertexShader* pXboxVertexShader) { if (UseXboxD3DVertexShaderTypeForVersion3948(pXboxVertexShader)) { - auto pXboxVertexShader3948 = (xbox::X_D3DVertexShader3948*)pXboxVertexShader; + auto pXboxVertexShader3948 = reinterpret_cast(pXboxVertexShader); return &(pXboxVertexShader3948->VertexAttribute); } @@ -297,7 +314,7 @@ static xbox::X_VERTEXATTRIBUTEFORMAT* CxbxGetVertexShaderAttributes(xbox::X_D3DV static DWORD* CxbxGetVertexShaderTokens(xbox::X_D3DVertexShader* pXboxVertexShader, DWORD* pNrTokens) { if (UseXboxD3DVertexShaderTypeForVersion3948(pXboxVertexShader)) { - auto pXboxVertexShader3948 = (xbox::X_D3DVertexShader3948*)pXboxVertexShader; + auto pXboxVertexShader3948 = reinterpret_cast(pXboxVertexShader); *pNrTokens = pXboxVertexShader3948->ProgramAndConstantsDwords; return &pXboxVertexShader3948->ProgramAndConstants[0]; } @@ -1408,7 +1425,12 @@ void CxbxImpl_SetVertexShader(DWORD Handle) xbox::X_D3DVertexShader* pXboxVertexShader = CxbxGetXboxVertexShaderForHandle(Handle); g_Xbox_VertexShader_IsPassthrough = false; - if (pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_PROGRAM) { + + if ((pXboxVertexShader->Flags & g_X_VERTEXSHADER_FLAG_VALID_MASK) != pXboxVertexShader->Flags) { + LOG_TEST_CASE("Unknown vertex shader flag"); + } + + if (pXboxVertexShader->Flags & g_X_VERTEXSHADER_FLAG_PROGRAM) { // Global variable set from CxbxVertexShaderSetFlags #if 0 // Since the D3DDevice_SetVertexShader patch already called it's trampoline, these calls have already been executed : CxbxImpl_LoadVertexShader(Handle, 0); CxbxImpl_SelectVertexShader(Handle, 0); @@ -1465,9 +1487,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle) CxbxSetVertexShaderPassthroughProgram(); g_Xbox_VertexShader_IsFixedFunction = false; g_Xbox_VertexShader_IsPassthrough = true; - } else if (pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_UNKNOWN) { - // Test-case : Amped - LOG_TEST_CASE("unknown vertex shader flag (4)"); } else { // Test-case : Many XDK samples, Crazy taxi 3 //LOG_TEST_CASE("Other or no vertex shader flags"); diff --git a/src/core/hle/D3D8/XbVertexShader.h b/src/core/hle/D3D8/XbVertexShader.h index 5db13d815..228e94b8f 100644 --- a/src/core/hle/D3D8/XbVertexShader.h +++ b/src/core/hle/D3D8/XbVertexShader.h @@ -213,5 +213,6 @@ extern void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address); extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox::X_STREAMINPUT* pStreamInputs); extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount); extern void CxbxImpl_DeleteVertexShader(DWORD Handle); +extern void CxbxVertexShaderSetFlags(); #endif