game: Fix compat with iGPUs, add fix for stuck DLSS option with blinker

This commit is contained in:
Jade Macho 2025-07-09 22:40:43 +02:00
parent 64690df48b
commit ec59c46440
Signed by: 0x0ade
GPG key ID: E1960710FE4FBEEF
5 changed files with 162 additions and 21 deletions

View file

@ -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;
}

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<Authors>0x0ade</Authors>
<Company></Company>
<Version>0.4.2.0</Version>
<Version>0.4.2.1</Version>
<Description></Description>
<Copyright></Copyright>
<PackageProjectUrl></PackageProjectUrl>

View file

@ -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<ImmediateBindCSSRVs> _immediateBindCSSRVsHook;
private Hook<CreateTexture2D> _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<SDX11.DeviceContext>((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<SDX11.ShaderResourceView>((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<SDX11.ShaderResourceView>((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;
}

View file

@ -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;

View file

@ -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<ResolutionScalingMode>())
{
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
}
}