From ec59c46440eb8bfd61fe33e0d1ee02556ccc76dc Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Wed, 9 Jul 2025 22:40:43 +0200 Subject: [PATCH] game: Fix compat with iGPUs, add fix for stuck DLSS option with blinker --- CustomResolution2782/ConfigurationEnums.cs | 41 +++++++- .../CustomResolution2782.csproj | 2 +- CustomResolution2782/GameSizeState.cs | 93 ++++++++++++++++--- CustomResolution2782/Service.cs | 1 + CustomResolution2782/Windows/ConfigWindow.cs | 46 ++++++++- 5 files changed, 162 insertions(+), 21 deletions(-) diff --git a/CustomResolution2782/ConfigurationEnums.cs b/CustomResolution2782/ConfigurationEnums.cs index 27fd782..6a36686 100644 --- a/CustomResolution2782/ConfigurationEnums.cs +++ b/CustomResolution2782/ConfigurationEnums.cs @@ -125,7 +125,29 @@ public enum ResolutionScalingMode public static class ResolutionScalingModeExt { - public static byte ToXIV(this ResolutionScalingMode mode) => mode switch + public static ResolutionScalingMode FromXIVSysConf(uint mode) => mode switch + { + 0 => ResolutionScalingMode.FSR, + 1 => ResolutionScalingMode.DLSS, + _ => ResolutionScalingMode.Linear + }; + + public static uint ToXIVSysConf(this ResolutionScalingMode mode) => mode switch + { + ResolutionScalingMode.FSR => 0, + ResolutionScalingMode.DLSS => 1, + _ => 0 + }; + + public static ResolutionScalingMode FromXIVGFX(byte mode) => mode switch + { + 0 => ResolutionScalingMode.Linear, + 1 => ResolutionScalingMode.FSR, + 2 => ResolutionScalingMode.DLSS, + _ => ResolutionScalingMode.Linear + }; + + public static byte ToXIVGFX(this ResolutionScalingMode mode) => mode switch { ResolutionScalingMode.Point => 0, ResolutionScalingMode.Linear => 0, @@ -152,5 +174,20 @@ public static class ResolutionScalingModeExt _ => null }; - public static bool IsUnsupported(this ResolutionScalingMode mode) => mode == ResolutionScalingMode.DLSS; + public static bool IsBroken(this ResolutionScalingMode mode) => mode switch + { +#if !DEBUG + ResolutionScalingMode.DLSS => true, +#endif + _ => false + }; + + public static unsafe bool IsSupported(this ResolutionScalingMode mode) => mode switch + { + ResolutionScalingMode.DLSS => PostEffectManagerEx.Instance()->DLSS != null, + _ => true, + }; + + public static ResolutionScalingMode ToSupported(this ResolutionScalingMode mode) => + !mode.IsSupported() ? ResolutionScalingMode.FSR : mode; } diff --git a/CustomResolution2782/CustomResolution2782.csproj b/CustomResolution2782/CustomResolution2782.csproj index c2d108b..7b7fdcd 100644 --- a/CustomResolution2782/CustomResolution2782.csproj +++ b/CustomResolution2782/CustomResolution2782.csproj @@ -3,7 +3,7 @@ 0x0ade - 0.4.2.0 + 0.4.2.1 diff --git a/CustomResolution2782/GameSizeState.cs b/CustomResolution2782/GameSizeState.cs index 5687eae..e266af1 100644 --- a/CustomResolution2782/GameSizeState.cs +++ b/CustomResolution2782/GameSizeState.cs @@ -6,6 +6,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Framework; using FloppyUtils; using SharpDX; using System; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using TerraFX.Interop.DirectX; @@ -28,10 +29,13 @@ public unsafe class GameSizeState : IDisposable private Hook _immediateBindCSSRVsHook; private Hook _createTexture2DHook; + private PostEffectManagerEx* _postEffectManager; + private SDX11.DeviceContext _d3dctx; private SDX11.Device _d3ddev; private SDX11.SamplerState _samplerMips; private SDX11.SamplerState _samplerPoint; + private SDX11.SamplerState _samplerLinear; private bool _wasEnabled = false; private object _renderLock = new(); @@ -40,8 +44,14 @@ public unsafe class GameSizeState : IDisposable private uint _drawCount; + private ResolutionScalingMode? _setConfigGraphicsRezoType; + public GameSizeState() { + var pfx = PostEffectManagerEx.Instance(); + Service.PluginLog.Debug($"PostEffectManager: 0x{(long) (nint) pfx:X16}"); + Service.PluginLog.Info($"DLSS: {(pfx->DLSS != null ? "Available" : "Unavailable")}"); + var dev = (DeviceEx*) Device.Instance(); _d3dctx = CppObject.FromPointer((nint) dev->D3D11DeviceContext); _d3ddev = _d3dctx.Device; @@ -58,6 +68,9 @@ public unsafe class GameSizeState : IDisposable samplerDesc.MaximumLod = 0; _samplerPoint = new(_d3ddev, samplerDesc); + samplerDesc.Filter = SDX11.Filter.MinMagLinearMipPoint; + _samplerLinear = new(_d3ddev, samplerDesc); + /* Writes DynamicResolutionTargetHeight to size[1] near the end, * but if in (gpose || main menu), only scale < 1?? */ @@ -213,6 +226,7 @@ public unsafe class GameSizeState : IDisposable _createTexture2DHook.Dispose(); lock (_renderLock) { + _samplerLinear.Dispose(); _samplerPoint.Dispose(); _samplerMips.Dispose(); } @@ -220,15 +234,26 @@ public unsafe class GameSizeState : IDisposable Service.Framework.RunOnFrameworkThread(Update); } + public void SetConfigGraphicsRezoType(ResolutionScalingMode mode) + { + _setConfigGraphicsRezoType = mode; + } + public void Update() { _dbg.Clear(); ref var cfg = ref Service.Config._.Game; + if (_setConfigGraphicsRezoType is { } setType) + { + _setConfigGraphicsRezoType = null; + Service.GameConfig.System.Set(SystemConfigOption.GraphicsRezoUpscaleType.ToString(), setType.ToXIVSysConf()); + } + ConfigDynRezo = Service.GameConfig.System.GetUInt(SystemConfigOption.DynamicRezoType.ToString()) != 0U; // GameConfig starts with FSR at 0; GraphicsConfig starts with FSR at 1 - ConfigGraphicsRezoType = (ResolutionScalingMode) (Service.GameConfig.System.GetUInt(SystemConfigOption.GraphicsRezoUpscaleType.ToString()) + 1); + ConfigGraphicsRezoType = ResolutionScalingModeExt.FromXIVSysConf(Service.GameConfig.System.GetUInt(SystemConfigOption.GraphicsRezoUpscaleType.ToString())); ConfigGraphicsRezoScale = Service.GameConfig.System.GetUInt(SystemConfigOption.GraphicsRezoScale.ToString()) / 100f; var dev = (DeviceEx*) Device.Instance(); @@ -239,9 +264,6 @@ public unsafe class GameSizeState : IDisposable { _dbg.Append(@$"DR !{gfx->DynamicRezoEnable} +{gfx->DynamicRezoEnableBeyond1} _{gfx->DynamicRezoEnableCutScene} ?{gfx->DynamicRezoEnableUnkx47} ?{gfx->DynamicRezoEnableUnkx48} GR x{gfx->GraphicsRezoScale} ?{gfx->GraphicsRezoUnk1} _{gfx->GraphicsRezoUpscaleType} ?{gfx->GraphicsRezoUnk2} -DEV 0x{(long) (nint) dev:X16} -GFX 0x{(long) (nint) gfx:X16} -RTM 0x{(long) (nint) rtm:X16} RTM {rtm->Resolution_Width} x {rtm->Resolution_Height} RTM H {rtm->DynamicResolutionActualTargetHeight} {rtm->DynamicResolutionTargetHeight} {rtm->DynamicResolutionMaximumHeight} {rtm->DynamicResolutionMinimumHeight} RTM S {rtm->GraphicsRezoScalePrev} {rtm->GraphicsRezoScaleGlassX} {rtm->GraphicsRezoScaleGlassY} {rtm->GraphicsRezoScaleGlassX} {rtm->GraphicsRezoScaleGlassY} @@ -258,12 +280,12 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr gfx->DynamicRezoEnable = (byte) (ConfigDynRezo || enabled ? 1 : 0); if (enabled) { - gfx->GraphicsRezoUpscaleType = cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode.ToXIV() : ResolutionScalingMode.Linear.ToXIV(); + gfx->GraphicsRezoUpscaleType = cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode.ToXIVGFX() : ResolutionScalingMode.Linear.ToXIVGFX(); rtm->DynamicResolutionMinimumHeight = rtm->DynamicResolutionMaximumHeight; } else { - gfx->GraphicsRezoUpscaleType = (byte) ConfigGraphicsRezoType; + gfx->GraphicsRezoUpscaleType = ConfigGraphicsRezoType.ToSupported().ToXIVGFX(); } _wasEnabled = enabled; } @@ -277,9 +299,10 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr { if (!unloading) { + Service.PluginLog.Debug($"Regenerating dirty RTM - locking ImmediateContextDX11.ProcessCommands"); lock (_renderLock) { - Service.PluginLog.Debug($"Regenerating dirty RTM - locking ImmediateContextDX11.ProcessCommands"); + Service.PluginLog.Debug($"Regenerating dirty RTM - locked ImmediateContextDX11.ProcessCommands"); dev->RequestResolutionChange = 1; RTMDestroyAfterResizeDetour(); RTMRegenAfterResizeDetour(); @@ -341,6 +364,8 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr private void RTMApplyScalingDetour(RenderTargetManagerEx* rtm, uint* size, byte unk1) { + Debug.Assert(rtm == RenderTargetManager.Instance()); + ref var cfg = ref Service.Config._.Game; if (Service.Plugin.Unloading || !cfg.IsEnabled) { @@ -452,6 +477,8 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data ) { + Debug.Assert(im == Device.Instance()->ImmediateContext); + var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance(); bool called = false; @@ -463,22 +490,29 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr Orig(); + SDX11.SamplerState? sampler = null; + if (Service.Config._.Game.IsEnabled && Service.Config._.ResolutionScalingMode == ResolutionScalingMode.Point) { - stage.SetSampler(0, _samplerPoint); - if (!isPS) - { - _d3dctx.PixelShader.SetSampler(0, _samplerPoint); - } + sampler = _samplerPoint; } else if (tex->MipLevel != 1) { - _d3dctx.PixelShader.SetSampler(0, _samplerMips); + sampler = _samplerMips; + _d3dctx.GenerateMips(CppObject.FromPointer((nint) tex->D3D11ShaderResourceView)); + } + else if (Service.Config._.Game.IsEnabled && Service.Config._.ResolutionScalingMode == ResolutionScalingMode.Linear) + { + sampler = _samplerLinear; + } + + if (sampler is not null) + { + stage.SetSampler(0, sampler); if (!isPS) { - _d3dctx.PixelShader.SetSampler(0, _samplerMips); + _d3dctx.PixelShader.SetSampler(0, sampler); } - _d3dctx.GenerateMips(CppObject.FromPointer((nint) tex->D3D11ShaderResourceView)); } } @@ -647,3 +681,32 @@ public unsafe struct ImmediateSRV [FieldOffset(0x14)] public uint Unkx14; } + +// There's currently no effort to C#-ify the PostEffectManager findings in CS. +[StructLayout(LayoutKind.Explicit)] +public unsafe struct PostEffectManagerEx +{ + private static PostEffectManagerEx* _instance; + + public static PostEffectManagerEx* Instance() + { + if (Service.SigScanner is null) + { + return null; + } + + var code = Service.SigScanner.ScanText("48 83 EC ?? 48 8B 05 ?? ?? ?? ?? 49 8B F9 49 8B F0 4C 8B F2 48 8B 98 ?? ?? 00 00 48 85 DB"); + var found = Service.SigScanner.ResolveRelativeAddress(code + 4 + 7, *(int*) (code + 4 + 3)); + return _instance = (PostEffectManagerEx*) *(nint*) found; + } + + /* Identifiable by being used for DLSS initialization when the upscale type == 2, + * and containing NVSDK NGX capability param attribs at *+0x158 since 2024? + * The function setting it up is very similar to NVIDIA's sample code, + * but in case of emergency, CTRL+F NVSDK_NGX_D3D11_GetCapabilityParameters (always exported?) + * and the two magic strings "SuperSampling.NeedsUpdatedDriver" and "SuperSampling.Available" + */ + [FieldOffset(0x4220)] + public void* DLSS; +} + diff --git a/CustomResolution2782/Service.cs b/CustomResolution2782/Service.cs index d1f185c..d69d99b 100644 --- a/CustomResolution2782/Service.cs +++ b/CustomResolution2782/Service.cs @@ -1,6 +1,7 @@ using CustomResolution.Hooks; using Dalamud.Game; using Dalamud.Game.Text; +using Dalamud.Interface; using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; diff --git a/CustomResolution2782/Windows/ConfigWindow.cs b/CustomResolution2782/Windows/ConfigWindow.cs index a249a0a..5147a04 100644 --- a/CustomResolution2782/Windows/ConfigWindow.cs +++ b/CustomResolution2782/Windows/ConfigWindow.cs @@ -125,7 +125,22 @@ Changed via /cres"); private void DrawGameTab() { - using var imTab = ImRaii.TabItem($"Gameplay##GameTab"); + var unsupportedSysScale = !Service.GameSize.ConfigGraphicsRezoType.IsSupported(); + + var blinkTime = (DateTime.UtcNow.Ticks % TimeSpan.TicksPerHour) / (float) TimeSpan.TicksPerSecond; + var blink = (blinkTime % 2) < 1; + + if (unsupportedSysScale && blink) + { + ImGui.PushStyleColor(ImGuiCol.Border, 0xFF00FFFF); + } + + using var imTab = ImRaii.TabItem($"Gameplay{(unsupportedSysScale ? " \uE0C0" : "")}##GameTab"); + + if (unsupportedSysScale && blink) + { + ImGui.PopStyleColor(); + } if (ImGui.IsItemHovered()) { @@ -147,6 +162,21 @@ Changed via /gres"); return; } + if (unsupportedSysScale) + { + using var border = ImRaii.PushColor(ImGuiCol.Border, 0xFF00FFFF); + if (ImGui.Button(@$"Your game system configuration is currently stuck on +an unsupported graphics upscaling option: {Service.GameSize.ConfigGraphicsRezoType} + +This can happen after you've replaced your GPU, +or when your graphic driver has encountered problems. + +Click here to reset to FSR and fix the scaling slider.##FixDLSS")) + { + Service.GameSize.SetConfigGraphicsRezoType(ResolutionScalingMode.FSR); + } + } + using (ImRaii.Disabled()) { ImGui.InputInt2("Current size", ref _displayCurrentGameWH[0]); @@ -160,7 +190,7 @@ Changed via /gres"); { foreach (var mode in Enum.GetValues()) { - if (ImGui.Selectable(mode.ToHumanNameString(), _.ResolutionScalingMode == mode, mode.IsUnsupported() ? ImGuiSelectableFlags.Disabled : ImGuiSelectableFlags.None)) + if (ImGui.Selectable(mode.ToHumanNameString(), _.ResolutionScalingMode == mode, mode.IsBroken() ? ImGuiSelectableFlags.Disabled : ImGuiSelectableFlags.None)) { _.ResolutionScalingMode = mode; } @@ -176,11 +206,15 @@ Changed via /gres"); if (Service.DebugConfig.IsDebug) { + if (ImGui.Button("(DEBUG) Set game config to DLSS")) + { + Service.GameSize.SetConfigGraphicsRezoType(ResolutionScalingMode.DLSS); + } + ImGui.Text(Service.GameSize.DebugInfo); } } - private void DrawSizeTabShared(ref ConfigurationV1.SizeConfig size, string name, bool scaleOnly = false) { ImGui.Checkbox("Enabled", ref size.IsEnabled); @@ -308,5 +342,11 @@ in case the resolution changes unexpectedly, or in case the resolution goes too In the absolute worst case, delete the following file to reset your settings: %%AppData%%\XIVLauncher\pluginConfigs\{Service.PluginName}.json"); } + +#if DEBUG + var debug = Service.DebugConfig.IsDebug; + ImGui.Checkbox("DEBUG!", ref debug); + Service.DebugConfig.IsDebug = debug; +#endif } }