From 78ebe72bfadd9daf1c0836a89f1ab775ca2287ab Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 31 Jan 2025 19:01:02 +0000 Subject: [PATCH] [d3d8] Add shadow perspective divide hack for Splinter Cell --- src/d3d8/d3d8_device.cpp | 28 ++++++++++++++++++++++++++++ src/d3d8/d3d8_device.h | 4 ++++ src/d3d8/d3d8_options.h | 14 ++++++++++---- src/util/config/config.cpp | 1 + 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp index 0a5ab0f0ddf..4e724ea945c 100644 --- a/src/d3d8/d3d8_device.cpp +++ b/src/d3d8/d3d8_device.cpp @@ -1370,6 +1370,27 @@ namespace dxvk { D3D8Texture2D* tex = static_cast(pTexture); + // Splinter Cell: Force perspective divide when a shadow map is bound to slot 0 + if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Stage == 0)) { + if (tex) { + D3DSURFACE_DESC surf; + tex->GetLevelDesc(0, &surf); + if (isDepthStencilFormat(surf.Format)) { + // If we bound a depth texture to stage 0 then we need to set the projected flag for stage 0 and 1 + // Stage 1 is a non-depth light cookie texture but still requires perspective divide to work + GetD3D9()->SetTextureStageState(0, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED); + GetD3D9()->SetTextureStageState(1, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED); + m_shadowPerspectiveDivide = true; + } else if (m_shadowPerspectiveDivide) { + // Non-depth texture bound. Game will reset the transform flags to 0 on its own + m_shadowPerspectiveDivide = false; + } + } else if (m_shadowPerspectiveDivide) { + // Texture unbound. Game will reset the transform flags to 0 on its own + m_shadowPerspectiveDivide = false; + } + } + if (unlikely(m_textures[Stage] == tex)) return D3D_OK; @@ -1403,6 +1424,13 @@ namespace dxvk { DWORD Value) { d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); + if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Type == D3DTSS_TEXTURETRANSFORMFLAGS)) { + // Splinter Cell: Ignore requests to change texture transform flags + // to 0 while shadow mapping perspective divide mode is enabled + if (m_shadowPerspectiveDivide && (Stage == 0 || Stage == 1)) + return D3D_OK; + } + StateChange(); if (stateType != -1u) { // if the type has been remapped to a sampler state type: diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h index 61ce40b8d13..5c898732a31 100644 --- a/src/d3d8/d3d8_device.h +++ b/src/d3d8/d3d8_device.h @@ -393,6 +393,8 @@ namespace dxvk { m_backBuffers.resize(m_presentParams.BackBufferCount); m_autoDepthStencil = nullptr; + + m_shadowPerspectiveDivide = false; } inline void RecreateBackBuffersAndAutoDepthStencil() { @@ -432,6 +434,8 @@ namespace dxvk { // Controls fixed-function exclusive mode (no PS support) bool m_isFixedFunctionOnly = false; + bool m_shadowPerspectiveDivide = false; + D3D8StateBlock* m_recorder = nullptr; DWORD m_recorderToken = 0; DWORD m_token = 0; diff --git a/src/d3d8/d3d8_options.h b/src/d3d8/d3d8_options.h index ead1b572d30..17604d41a43 100644 --- a/src/d3d8/d3d8_options.h +++ b/src/d3d8/d3d8_options.h @@ -42,13 +42,19 @@ namespace dxvk { /// it was brought in line with standard D3D9 behavior. bool forceLegacyDiscard = false; + /// Splinter Cell expects shadow map texture coordinates to be perspective divided + /// even though D3DTTFF_PROJECTED is never set for any texture coordinates. This flag + /// forces that flag for the necessary stages when a depth texture is bound to slot 0 + bool shadowPerspectiveDivide = false; + D3D8Options() {} D3D8Options(const Config& config) { - auto forceVsDeclStr = config.getOption("d3d8.forceVsDecl", ""); - batching = config.getOption ("d3d8.batching", batching); - placeP8InScratch = config.getOption ("d3d8.placeP8InScratch", placeP8InScratch); - forceLegacyDiscard = config.getOption ("d3d8.forceLegacyDiscard", forceLegacyDiscard); + auto forceVsDeclStr = config.getOption("d3d8.forceVsDecl", ""); + batching = config.getOption ("d3d8.batching", batching); + placeP8InScratch = config.getOption ("d3d8.placeP8InScratch", placeP8InScratch); + forceLegacyDiscard = config.getOption ("d3d8.forceLegacyDiscard", forceLegacyDiscard); + shadowPerspectiveDivide = config.getOption ("d3d8.shadowPerspectiveDivide", shadowPerspectiveDivide); parseVsDecl(forceVsDeclStr); } diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index e1acf7d4ac3..43cf4104293 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1192,6 +1192,7 @@ namespace dxvk { * Fixes shadow buffers and alt-tab */ { R"(\\splintercell\.exe$)", {{ { "d3d8.scaleDref", "24" }, + { "d3d8.shadowPerspectiveDivide", "True" }, { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, }};