diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 477c4e523..a433b2f60 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -1908,6 +1908,10 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar extern void DSound_PrintStats(); //TODO: move into plugin class usage. DSound_PrintStats(); } + else if (wParam == VK_F2) + { + g_UseFixedFunctionVertexShader = !g_UseFixedFunctionVertexShader; + } else if (wParam == VK_F3) { g_bClipCursor = !g_bClipCursor; @@ -2885,8 +2889,6 @@ void GetMultiSampleScaleRaw(float& xScale, float& yScale) { // Titles can render pre-transformed vertices to screen space (using XYZRHW vertex position data or otherwise) // so we need to know the space they are in to interpret it correctly void GetScreenScaleFactors(float& scaleX, float& scaleY) { - extern bool g_Xbox_VertexShader_IsPassthrough; - // Example: // NFS HP2 renders in-game at 640*480 // The title uses MSAA, which increases the rendertarget size, but leaves the screen scale unaffected @@ -2916,7 +2918,7 @@ void GetScreenScaleFactors(float& scaleX, float& scaleY) { // - Antialias sample (background gradient) // Vertex program passthrough equivalent (title does apply backbuffer scale): // - NFS:HP2 (car speed and other in-game UI elements) - if (!g_Xbox_VertexShader_IsPassthrough) { + if (g_Xbox_VertexShaderMode != VertexShaderMode::Passthrough) { scaleX *= g_Xbox_BackbufferScaleX; scaleY *= g_Xbox_BackbufferScaleY; } @@ -4169,8 +4171,6 @@ void GetXboxViewportOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) void CxbxUpdateHostViewPortOffsetAndScaleConstants() { - extern bool g_Xbox_VertexShader_IsPassthrough; - float vScaleOffset[2][4]; // 0 - scale 1 - offset GetXboxViewportOffsetAndScale(vScaleOffset[1], vScaleOffset[0]); @@ -4196,7 +4196,7 @@ void CxbxUpdateHostViewPortOffsetAndScaleConstants() // Passthrough should range 0 to 1, instead of 0 to zbuffer depth // Test case: DoA3 character select - float zOutputScale = g_Xbox_VertexShader_IsPassthrough ? 1 : g_ZScale; + float zOutputScale = g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough ? 1 : g_ZScale; float screenspaceScale[4] = { xboxScreenspaceWidth / 2, -xboxScreenspaceHeight / 2, zOutputScale, 1 }; float screenspaceOffset[4] = { xboxScreenspaceWidth / 2 + aaOffsetX, xboxScreenspaceHeight / 2 + aaOffsetY, 0, 0 }; @@ -7343,7 +7343,7 @@ void CxbxUpdateHostTextureScaling() // Texcoord index. Just the texture stage unless fixed function mode int texCoordIndex = stage; - if (g_Xbox_VertexShader_IsFixedFunction) { + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) { // Get TEXCOORDINDEX for the current texture stage's state // Stores both the texture stage index and information for generating coordinates // See D3DTSS_TEXCOORDINDEX @@ -7434,7 +7434,7 @@ void CxbxUpdateHostViewport() { aaScaleX *= g_RenderTargetUpscaleFactor; aaScaleY *= g_RenderTargetUpscaleFactor; - if (g_Xbox_VertexShader_IsFixedFunction) { + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) { // Set viewport D3DVIEWPORT hostViewport = g_Xbox_Viewport; hostViewport.X *= aaScaleX; @@ -7495,6 +7495,11 @@ void CxbxUpdateNativeD3DResources() CxbxUpdateHostVertexShaderConstants(); CxbxUpdateHostViewport(); + + // Update fixed function vertex shader state + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction && g_UseFixedFunctionVertexShader) { + UpdateFixedFunctionVertexShaderState(); + } // NOTE: Order is important here // Some Texture States depend on RenderState values (Point Sprites) diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index e67fdf6ca..23ed872d7 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -844,7 +844,7 @@ void CxbxImpl_End() // Arrange for g_InlineVertexBuffer_AttributeFormat to be returned in CxbxGetVertexDeclaration, // so that our above composed declaration will be used for the next draw : g_InlineVertexBuffer_DeclarationOverride = true; - // Note, that g_Xbox_VertexShader_IsFixedFunction should be left untouched, + // Note, that g_Xbox_VertexShaderMode should be left untouched, // because except for the declaration override, the Xbox shader (either FVF // or a program, or even passthrough shaders) should still be in effect! diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index db62e5df5..90e92412a 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -58,9 +58,9 @@ extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp xbox::X_STREAMINPUT g_Xbox_SetVertexShaderInput_Data[X_VSH_MAX_STREAMS] = { 0 }; // Active when g_Xbox_SetVertexShaderInput_Count > 0 xbox::X_VERTEXATTRIBUTEFORMAT g_Xbox_SetVertexShaderInput_Attributes = { 0 }; // Read by GetXboxVertexAttributes when g_Xbox_SetVertexShaderInput_Count > 0 -// Variables set by [D3DDevice|CxbxImpl]_SetVertexShader() and [D3DDevice|CxbxImpl]_SelectVertexShader() : - bool g_Xbox_VertexShader_IsFixedFunction = true; - bool g_Xbox_VertexShader_IsPassthrough = false; +VertexShaderMode g_Xbox_VertexShaderMode = VertexShaderMode::FixedFunction; +bool g_UseFixedFunctionVertexShader = true; + xbox::dword_xt g_Xbox_VertexShader_Handle = 0; #ifdef CXBX_USE_GLOBAL_VERTEXSHADER_POINTER // TODO : Would this be more accurate / simpler? xbox::X_D3DVertexShader *g_Xbox_VertexShader_Ptr = nullptr; @@ -1092,7 +1092,9 @@ VertexDeclarationKey GetXboxVertexAttributesKey(xbox::X_VERTEXATTRIBUTEFORMAT* p auto attributeHash = ComputeHash((void*)pXboxVertexAttributeFormat, sizeof(xbox::X_VERTEXATTRIBUTEFORMAT)); // For now, we use different declarations depending on if the fixed function pipeline // is in use, even if the attributes are the same - return attributeHash ^ (VertexDeclarationKey)g_Xbox_VertexShader_IsFixedFunction; + return g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction + ? attributeHash + : attributeHash ^ 1; } std::unordered_map g_CxbxVertexDeclarations; @@ -1139,18 +1141,27 @@ void CxbxUpdateHostVertexShader() LOG_INIT; // Allows use of DEBUG_D3DRESULT - if (g_Xbox_VertexShader_IsFixedFunction) { - HRESULT hRet = g_pD3DDevice->SetVertexShader(nullptr); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); - // TODO : Once available, start using host Fixed Function HLSL shader - // instead of using deprecated host fixed function (by setting a null - // vertex shader). - // As for the required host vertex declaration : - // CxbxUpdateHostVertexDeclaration already been - // called, which sets host vertex declaration based on the - // declaration that XboxVertexShaderFromFVF generated. + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) { + IDirect3DVertexShader* fixedFunctionShader = nullptr; + HRESULT hRet; + + if (g_UseFixedFunctionVertexShader) { + static IDirect3DVertexShader* ffHlsl = nullptr; + if (ffHlsl == nullptr) { + ID3DBlob* pBlob = nullptr; + EmuCompileFixedFunction(&pBlob); + if (pBlob) { + hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl); + if (FAILED(hRet)) CxbxKrnlCleanup("Failed to create fixed-function shader"); + } + } + fixedFunctionShader = ffHlsl; + } + + hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader); + if (FAILED(hRet)) CxbxKrnlCleanup("Failed to set fixed-function shader"); } - else if (g_Xbox_VertexShader_IsPassthrough && g_bUsePassthroughHLSL) { + else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) { if (passthroughshader == nullptr) { ID3DBlob* pBlob = nullptr; EmuCompileXboxPassthrough(&pBlob); @@ -1264,7 +1275,7 @@ CxbxVertexDeclaration* CxbxGetVertexDeclaration() // Convert Xbox vertex attributes towards host Direct3D 9 vertex element D3DVERTEXELEMENT* pRecompiledVertexElements = EmuRecompileVshDeclaration( pXboxVertexAttributeFormat, - g_Xbox_VertexShader_IsFixedFunction, + g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction, pCxbxVertexDeclaration); // Create the vertex declaration @@ -1354,6 +1365,8 @@ void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address) // Either way, the given address slot is selected as the start of the current vertex shader program g_Xbox_VertexShader_FunctionSlots_StartAddress = Address; + g_Xbox_VertexShaderMode = VertexShaderMode::ShaderProgram; + if (Handle) { if (!VshHandleIsVertexShader(Handle)) LOG_TEST_CASE("Non-zero handle must be a VertexShader!"); @@ -1362,8 +1375,6 @@ void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address) g_Xbox_VertexShader_Ptr = VshHandleToXboxVertexShader(Handle); #endif g_Xbox_VertexShader_Handle = Handle; - g_Xbox_VertexShader_IsFixedFunction = false; - g_Xbox_VertexShader_IsPassthrough = false; } } @@ -1450,7 +1461,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle) HRESULT hRet = D3D_OK; xbox::X_D3DVertexShader* pXboxVertexShader = CxbxGetXboxVertexShaderForHandle(Handle); - g_Xbox_VertexShader_IsPassthrough = false; if ((pXboxVertexShader->Flags & g_X_VERTEXSHADER_FLAG_VALID_MASK) != pXboxVertexShader->Flags) { LOG_TEST_CASE("Unknown vertex shader flag"); @@ -1470,8 +1480,8 @@ void CxbxImpl_SetVertexShader(DWORD Handle) LOG_TEST_CASE("g_Xbox_VertexShader_FunctionSlots_StartAddress != 0"); bHackCallSelectAgain = true; } - if (g_Xbox_VertexShader_IsFixedFunction != false) { - LOG_TEST_CASE("g_Xbox_VertexShader_IsFixedFunction != false"); + if (g_Xbox_VertexShaderMode != VertexShaderMode::ShaderProgram) { + LOG_TEST_CASE("Not in shader program mode after SetVertexShader trampoline"); bHackCallSelectAgain = true; } @@ -1480,7 +1490,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle) // _SelectVertexShader isn't applied; // 'solve' that by calling it here instead. CxbxImpl_SelectVertexShader(Handle, 0); - g_Xbox_VertexShader_IsFixedFunction = false; } #endif } else { @@ -1511,12 +1520,11 @@ void CxbxImpl_SetVertexShader(DWORD Handle) // Switch to passthrough program, if so required if (pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_PASSTHROUGH) { CxbxSetVertexShaderPassthroughProgram(); - g_Xbox_VertexShader_IsFixedFunction = false; - g_Xbox_VertexShader_IsPassthrough = true; + g_Xbox_VertexShaderMode = VertexShaderMode::Passthrough; } else { // Test-case : Many XDK samples, Crazy taxi 3 //LOG_TEST_CASE("Other or no vertex shader flags"); - g_Xbox_VertexShader_IsFixedFunction = true; + g_Xbox_VertexShaderMode = VertexShaderMode::FixedFunction; } } } diff --git a/src/core/hle/D3D8/XbVertexShader.h b/src/core/hle/D3D8/XbVertexShader.h index 29a5ade50..5b82c28a3 100644 --- a/src/core/hle/D3D8/XbVertexShader.h +++ b/src/core/hle/D3D8/XbVertexShader.h @@ -80,6 +80,17 @@ typedef struct _CxbxVertexDeclaration } CxbxVertexDeclaration; +enum class VertexShaderMode { + FixedFunction, + // When titles use Xbox fixed function with pre-transformed vertices + // it actually uses a special "passthrough" shader program + Passthrough, + ShaderProgram +}; + +extern VertexShaderMode g_Xbox_VertexShaderMode; +extern bool g_UseFixedFunctionVertexShader; + // Intermediate vertex shader structures enum VSH_OREG_NAME { @@ -217,4 +228,5 @@ extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox:: extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount); extern void CxbxImpl_DeleteVertexShader(DWORD Handle); extern void CxbxVertexShaderSetFlags(); +extern HRESULT SetVertexShader(IDirect3DVertexShader* pShader); #endif