Compare commits

...
Sign in to create a new pull request.

5 commits

9 changed files with 462 additions and 95 deletions

View file

@ -117,16 +117,49 @@ public static class MinSizeModeExt
public enum ResolutionScalingMode public enum ResolutionScalingMode
{ {
Fast = 0, Point = 0,
FSR = 1, Linear = 1,
DLSS = 2 FSR = 2,
DLSS = 3
} }
public static class ResolutionScalingModeExt public static class ResolutionScalingModeExt
{ {
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,
ResolutionScalingMode.FSR => 1,
ResolutionScalingMode.DLSS => 2,
_ => 0
};
public static string ToHumanNameString(this ResolutionScalingMode mode) => mode switch public static string ToHumanNameString(this ResolutionScalingMode mode) => mode switch
{ {
ResolutionScalingMode.Fast => "Fast Pixelation", ResolutionScalingMode.Point => "Pixelated",
ResolutionScalingMode.Linear => "Blurry",
ResolutionScalingMode.FSR => "AMD FSR", ResolutionScalingMode.FSR => "AMD FSR",
ResolutionScalingMode.DLSS => "NVIDIA DLSS", ResolutionScalingMode.DLSS => "NVIDIA DLSS",
_ => mode.ToString(), _ => mode.ToString(),
@ -134,11 +167,25 @@ public static class ResolutionScalingModeExt
public static string? ToHumanInfoString(this ResolutionScalingMode mode) => mode switch public static string? ToHumanInfoString(this ResolutionScalingMode mode) => mode switch
{ {
ResolutionScalingMode.Fast => "Lowest quality option which results in blurry pixelation.", ResolutionScalingMode.Point => "Lowest quality option which results in pixelation.",
ResolutionScalingMode.Linear => "Low quality option which results in blur.",
ResolutionScalingMode.FSR => "Upscaling which works on any GPU for low performance overhead.", ResolutionScalingMode.FSR => "Upscaling which works on any GPU for low performance overhead.",
ResolutionScalingMode.DLSS => "DLSS in FFXIV is buggy, even without any plugins.", ResolutionScalingMode.DLSS => "DLSS in FFXIV is buggy, even without any plugins.",
_ => null _ => null
}; };
public static bool IsUnsupported(this ResolutionScalingMode mode) => mode == ResolutionScalingMode.DLSS; public static bool IsBroken(this ResolutionScalingMode mode) => mode switch
{
ResolutionScalingMode.DLSS => !Service.DebugConfig.IsDebug,
_ => 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> <PropertyGroup>
<Authors>0x0ade</Authors> <Authors>0x0ade</Authors>
<Company></Company> <Company></Company>
<Version>0.4.1.0</Version> <Version>0.4.2.1</Version>
<Description></Description> <Description></Description>
<Copyright></Copyright> <Copyright></Copyright>
<PackageProjectUrl></PackageProjectUrl> <PackageProjectUrl></PackageProjectUrl>
@ -19,6 +19,7 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<RootNamespace>CustomResolution</RootNamespace> <RootNamespace>CustomResolution</RootNamespace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
@ -30,7 +31,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="12.0.0" /> <PackageReference Include="DalamudPackager" Version="12.0.0" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.22621.2" />
<Reference Include="FFXIVClientStructs"> <Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath> <HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private> <Private>false</Private>
@ -71,9 +71,11 @@
<HintPath>$(DalamudLibPath)SharpDX.Mathematics.dll</HintPath> <HintPath>$(DalamudLibPath)SharpDX.Mathematics.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" /> <Reference Include="TerraFX.Interop.Windows">
<HintPath>$(DalamudLibPath)TerraFX.Interop.Windows.dll</HintPath>
<Private>false</Private>
</Reference>
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" /> <PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -6,6 +6,7 @@ using FloppyUtils;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
using static TerraFX.Interop.Windows.Windows; using static TerraFX.Interop.Windows.Windows;
@ -421,13 +422,14 @@ public unsafe struct DeviceEx
public ref uint NewHeight => ref _.NewHeight; public ref uint NewHeight => ref _.NewHeight;
// C# doesn't let us use ptr types as generic arguments, oh well. // C# doesn't let us use ptr types as generic arguments, oh well.
public readonly ID3D11DeviceContext* D3D11DeviceContext => (ID3D11DeviceContext*) _.D3D11DeviceContext;
public readonly ImmediateContextEx* ImmediateContext => (ImmediateContextEx*) _.ImmediateContext; public readonly ImmediateContextEx* ImmediateContext => (ImmediateContextEx*) _.ImmediateContext;
} }
// ImmediateContextEx in FFXIVClientStructs is a bare minimum. // ImmediateContextEx in FFXIVClientStructs is a bare minimum.
// https://github.com/aers/FFXIVClientStructs/blob/ef5e9a5997671fb2c9a72cb9d57d841855f62085/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs // https://github.com/aers/FFXIVClientStructs/blob/ef5e9a5997671fb2c9a72cb9d57d841855f62085/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
public struct ImmediateContextEx public unsafe struct ImmediateContextEx
{ {
[FieldOffset(0)] [FieldOffset(0)]
public ImmediateContext _; public ImmediateContext _;

View file

@ -4,10 +4,14 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FloppyUtils; using FloppyUtils;
using SharpDX;
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using TerraFX.Interop.Windows; using TerraFX.Interop.DirectX;
using Device = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device;
using SDX11 = SharpDX.Direct3D11;
namespace CustomResolution; namespace CustomResolution;
@ -17,17 +21,56 @@ public unsafe class GameSizeState : IDisposable
private Hook<RTMApplyScaling> _rtmApplyScalingHook; private Hook<RTMApplyScaling> _rtmApplyScalingHook;
private Hook<RTMDestroyAfterResize> _rtmDestroyAfterResizeHook; private Hook<RTMDestroyAfterResize> _rtmDestroyAfterResizeHook;
private Hook<RTMRegenAfterResize> _rtmRegenAfterResizeHook; private Hook<RTMRegenAfterResize> _rtmRegenAfterResizeHook;
private Hook<ICDX11ProcessCommands> _icdx11ProcessCommandsHook; private Hook<ImmediateProcessCommands> _immediateProcessCommandsHook;
private Hook<DDX11PostTick> _ddx11PostTickHook; private Hook<DDX11PostTick> _ddx11PostTickHook;
private Hook<TaskRenderGraphicsRender> _taskRenderGraphicsRenderHook; private Hook<TaskRenderGraphicsRender> _taskRenderGraphicsRenderHook;
private Hook<PrepareTexture> _prepareTextureHook;
private Hook<ImmediateBindPSSRVs> _immediateBindPSSRVsHook;
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 bool _wasEnabled = false;
private object _renderLock = new(); private object _renderLock = new();
private ForceUpdateRTMState _forceUpdateRTM = ForceUpdateRTMState._0_Idle; private ForceUpdateRTMState _forceUpdateRTM = ForceUpdateRTMState._0_Idle;
private uint _drawCount;
private ResolutionScalingMode? _setConfigGraphicsRezoType;
public GameSizeState() 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;
var samplerDesc = SDX11.SamplerStateDescription.Default();
samplerDesc.Filter = SDX11.Filter.MinMagMipLinear;
samplerDesc.AddressU = SDX11.TextureAddressMode.Mirror;
samplerDesc.AddressV = SDX11.TextureAddressMode.Mirror;
samplerDesc.AddressW = SDX11.TextureAddressMode.Mirror;
_samplerMips = new(_d3ddev, samplerDesc);
samplerDesc.Filter = SDX11.Filter.MinMagMipPoint;
samplerDesc.MinimumLod = 0;
samplerDesc.MaximumLod = 0;
_samplerPoint = new(_d3ddev, samplerDesc);
samplerDesc.Filter = SDX11.Filter.MinMagLinearMipPoint;
_samplerLinear = new(_d3ddev, samplerDesc);
/* Writes DynamicResolutionTargetHeight to size[1] near the end, /* Writes DynamicResolutionTargetHeight to size[1] near the end,
* but if in (gpose || main menu), only scale < 1?? * but if in (gpose || main menu), only scale < 1??
*/ */
@ -37,8 +80,6 @@ public unsafe class GameSizeState : IDisposable
); );
_rtmApplyScalingHook.Enable(); _rtmApplyScalingHook.Enable();
var dev = (DeviceEx*) Device.Instance();
/* Device.RequestResolutionChange and other triggers run some callbacks, namely /* Device.RequestResolutionChange and other triggers run some callbacks, namely
* (at the time of 7.2) 0x40 for RTM destruction and 0x48 for RTM reconstruction, * (at the time of 7.2) 0x40 for RTM destruction and 0x48 for RTM reconstruction,
* registered in RTM init and run in PostTick. * registered in RTM init and run in PostTick.
@ -70,11 +111,11 @@ public unsafe class GameSizeState : IDisposable
* with some other race conditions regarding the fields we modify. * with some other race conditions regarding the fields we modify.
* Let's wrap it in a C# lock and call it a day... * Let's wrap it in a C# lock and call it a day...
*/ */
_icdx11ProcessCommandsHook = Service.GameInteropProvider.HookFromAddress<ICDX11ProcessCommands>( _immediateProcessCommandsHook = Service.GameInteropProvider.HookFromAddress<ImmediateProcessCommands>(
Service.SigScanner.ScanText("48 89 5C 24 10 48 89 6C 24 18 56 57 41 56 48 83 EC 30 48 8B 01 41 8B F0 48 8B EA"), Service.SigScanner.ScanText("48 89 5C 24 10 48 89 6C 24 18 56 57 41 56 48 83 EC 30 48 8B 01 41 8B F0 48 8B EA"),
ICDX11ProcessCommandsDetour ImmediateProcessCommandsDetour
); );
_icdx11ProcessCommandsHook.Enable(); _immediateProcessCommandsHook.Enable();
_ddx11PostTickHook = Service.GameInteropProvider.HookFromAddress<DDX11PostTick>( _ddx11PostTickHook = Service.GameInteropProvider.HookFromAddress<DDX11PostTick>(
Service.SigScanner.ScanText("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 54 41 55 41 56 41 57 B8 30 43 00 00 ?? ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? 00 00 8B 15"), Service.SigScanner.ScanText("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 54 41 55 41 56 41 57 B8 30 43 00 00 ?? ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? 00 00 8B 15"),
@ -100,6 +141,37 @@ public unsafe class GameSizeState : IDisposable
TaskRenderGraphicsRenderDetour TaskRenderGraphicsRenderDetour
); );
_taskRenderGraphicsRenderHook.Enable(); _taskRenderGraphicsRenderHook.Enable();
/* Texture preparation used in a lot of places, but most notable for
* writing to the mip level count of the gameplay texture and also updating the D3D11 ptrs.
*/
_prepareTextureHook = Service.GameInteropProvider.HookFromAddress<PrepareTexture>(
Service.SigScanner.ScanText("40 53 48 83 EC 20 48 8B 02 48 8B D9 48 89 41 38 48 8B 02 48 89 41 40"),
PrepareTextureDetour
);
_prepareTextureHook.Enable();
/* The shader resource view of GameplayTexture gets read by these two.
*/
_immediateBindPSSRVsHook = Service.GameInteropProvider.HookFromAddress<ImmediateBindPSSRVs>(
Service.SigScanner.ScanText("48 8B C4 4C 89 40 18 48 89 48 08 53 56 48 83 EC 68 44 8B 5A 44 48 8D B1 E8 03 00 00 4C 89 68 D8"),
ImmediateBindPSSRVsDetour
);
_immediateBindPSSRVsHook.Enable();
_immediateBindCSSRVsHook = Service.GameInteropProvider.HookFromAddress<ImmediateBindCSSRVs>(
Service.SigScanner.ScanText("48 8B C4 4C 89 40 18 48 89 48 08 53 56 48 83 EC 68 44 8B 5A 44 48 8D B1 E8 13 00 00 4C 89 68 D8"),
ImmediateBindCSSRVsDetour
);
_immediateBindCSSRVsHook.Enable();
/* And because all that isn't bad enough:
* Hook CreateTexture2D to forcibly set the mipmap generation flag.
*/
_createTexture2DHook = Service.GameInteropProvider.HookFromAddress<CreateTexture2D>(
((ID3D11Device*) _d3ddev.NativePointer)->lpVtbl[5],
CreateTexture2DDetour
);
_createTexture2DHook.Enable();
} }
public bool ConfigDynRezo { get; private set; } public bool ConfigDynRezo { get; private set; }
@ -121,7 +193,7 @@ public unsafe class GameSizeState : IDisposable
private delegate void RTMRegenAfterResize(); private delegate void RTMRegenAfterResize();
// [UnmanagedFunctionPointer(CallingConvention.FastCall)] // [UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate void ICDX11ProcessCommands(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count); private delegate void ImmediateProcessCommands(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count);
// [UnmanagedFunctionPointer(CallingConvention.FastCall)] // [UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate void DDX11PostTick(DeviceEx* dev); private delegate void DDX11PostTick(DeviceEx* dev);
@ -129,26 +201,59 @@ public unsafe class GameSizeState : IDisposable
// [UnmanagedFunctionPointer(CallingConvention.FastCall)] // [UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate void TaskRenderGraphicsRender(); private delegate void TaskRenderGraphicsRender();
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate ulong PrepareTexture(Texture* tex, uint* size, byte mips, uint format);
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate void ImmediateBindPSSRVs(ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data);
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
private delegate void ImmediateBindCSSRVs(ImmediateContextEx* im, Shader* cs, ImmediateSRV* data);
private delegate int CreateTexture2D(ID3D11Device* d3ddev, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* initialData, ID3D11Texture2D** tex);
public void Dispose() public void Dispose()
{ {
_rtmApplyScalingHook.Dispose(); _rtmApplyScalingHook.Dispose();
_rtmDestroyAfterResizeHook.Dispose(); _rtmDestroyAfterResizeHook.Dispose();
_rtmRegenAfterResizeHook.Dispose(); _rtmRegenAfterResizeHook.Dispose();
_icdx11ProcessCommandsHook.Dispose(); _immediateProcessCommandsHook.Dispose();
_ddx11PostTickHook.Dispose(); _ddx11PostTickHook.Dispose();
_taskRenderGraphicsRenderHook.Dispose(); _taskRenderGraphicsRenderHook.Dispose();
_prepareTextureHook.Dispose();
_immediateBindPSSRVsHook.Dispose();
_immediateBindCSSRVsHook.Dispose();
_createTexture2DHook.Dispose();
lock (_renderLock)
{
_samplerLinear.Dispose();
_samplerPoint.Dispose();
_samplerMips.Dispose();
}
_d3ddev.Dispose();
Service.Framework.RunOnFrameworkThread(Update); Service.Framework.RunOnFrameworkThread(Update);
} }
public void SetConfigGraphicsRezoType(ResolutionScalingMode mode)
{
_setConfigGraphicsRezoType = mode;
}
public void Update() public void Update()
{ {
_dbg.Clear(); _dbg.Clear();
ref var cfg = ref Service.Config._.Game; 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; ConfigDynRezo = Service.GameConfig.System.GetUInt(SystemConfigOption.DynamicRezoType.ToString()) != 0U;
// GameConfig starts with FSR at 0; GraphicsConfig starts with FSR at 1 // 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; ConfigGraphicsRezoScale = Service.GameConfig.System.GetUInt(SystemConfigOption.GraphicsRezoScale.ToString()) / 100f;
var dev = (DeviceEx*) Device.Instance(); var dev = (DeviceEx*) Device.Instance();
@ -157,14 +262,8 @@ public unsafe class GameSizeState : IDisposable
if (Service.DebugConfig.IsDebug) if (Service.DebugConfig.IsDebug)
{ {
_dbg.Append(@$"RTMApplyScaling 0x{(_rtmApplyScalingHook.IsDisposed ? null : _rtmApplyScalingHook.Address):X16} _dbg.Append(@$"DR !{gfx->DynamicRezoEnable} +{gfx->DynamicRezoEnableBeyond1} _{gfx->DynamicRezoEnableCutScene} ?{gfx->DynamicRezoEnableUnkx47} ?{gfx->DynamicRezoEnableUnkx48}
RTMRegenAfterResize 0x{(_rtmRegenAfterResizeHook.IsDisposed ? null : _rtmRegenAfterResizeHook.Address):X16}
ICDX11ProcessCommands 0x{(_icdx11ProcessCommandsHook.IsDisposed ? null : _icdx11ProcessCommandsHook.Address):X16}
DR !{gfx->DynamicRezoEnable} +{gfx->DynamicRezoEnableBeyond1} _{gfx->DynamicRezoEnableCutScene} ?{gfx->DynamicRezoEnableUnkx47} ?{gfx->DynamicRezoEnableUnkx48}
GR x{gfx->GraphicsRezoScale} ?{gfx->GraphicsRezoUnk1} _{gfx->GraphicsRezoUpscaleType} ?{gfx->GraphicsRezoUnk2} 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 {rtm->Resolution_Width} x {rtm->Resolution_Height}
RTM H {rtm->DynamicResolutionActualTargetHeight} {rtm->DynamicResolutionTargetHeight} {rtm->DynamicResolutionMaximumHeight} {rtm->DynamicResolutionMinimumHeight} RTM H {rtm->DynamicResolutionActualTargetHeight} {rtm->DynamicResolutionTargetHeight} {rtm->DynamicResolutionMaximumHeight} {rtm->DynamicResolutionMinimumHeight}
RTM S {rtm->GraphicsRezoScalePrev} {rtm->GraphicsRezoScaleGlassX} {rtm->GraphicsRezoScaleGlassY} {rtm->GraphicsRezoScaleGlassX} {rtm->GraphicsRezoScaleGlassY} RTM S {rtm->GraphicsRezoScalePrev} {rtm->GraphicsRezoScaleGlassX} {rtm->GraphicsRezoScaleGlassY} {rtm->GraphicsRezoScaleGlassX} {rtm->GraphicsRezoScaleGlassY}
@ -181,12 +280,12 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
gfx->DynamicRezoEnable = (byte) (ConfigDynRezo || enabled ? 1 : 0); gfx->DynamicRezoEnable = (byte) (ConfigDynRezo || enabled ? 1 : 0);
if (enabled) if (enabled)
{ {
gfx->GraphicsRezoUpscaleType = (byte) (cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode : ResolutionScalingMode.Fast); gfx->GraphicsRezoUpscaleType = cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode.ToXIVGFX() : ResolutionScalingMode.Linear.ToXIVGFX();
rtm->DynamicResolutionMinimumHeight = rtm->DynamicResolutionMaximumHeight; rtm->DynamicResolutionMinimumHeight = rtm->DynamicResolutionMaximumHeight;
} }
else else
{ {
gfx->GraphicsRezoUpscaleType = (byte) ConfigGraphicsRezoType; gfx->GraphicsRezoUpscaleType = ConfigGraphicsRezoType.ToSupported().ToXIVGFX();
} }
_wasEnabled = enabled; _wasEnabled = enabled;
} }
@ -194,16 +293,16 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
var scale = MathF.Max(1f, enabled ? cfg.Scale : 1f); var scale = MathF.Max(1f, enabled ? cfg.Scale : 1f);
GetScaledWidthHeight(dev->Width, dev->Height, scale, out var widthS, out var heightS); GetScaledWidthHeight(dev->Width, dev->Height, scale, out var widthS, out var heightS);
// TODO: Figure out a more consistent way to get to the currently allocated size.
// Check if the backing RTs are the expected size. // Check if the backing RTs are the expected size.
var tex = rtm->_.Unk20[0].Value; var tex = rtm->GameplayTextureUnk3;
if (tex->AllocatedWidth != widthS || tex->AllocatedHeight != heightS) if (tex->AllocatedWidth != widthS || tex->AllocatedHeight != heightS)
{ {
if (!unloading) if (!unloading)
{ {
Service.PluginLog.Debug($"Regenerating dirty RTM - locking ImmediateContextDX11.ProcessCommands");
lock (_renderLock) lock (_renderLock)
{ {
Service.PluginLog.Debug($"Regenerating dirty RTM - locking ImmediateContextDX11.ProcessCommands"); Service.PluginLog.Debug($"Regenerating dirty RTM - locked ImmediateContextDX11.ProcessCommands");
dev->RequestResolutionChange = 1; dev->RequestResolutionChange = 1;
RTMDestroyAfterResizeDetour(); RTMDestroyAfterResizeDetour();
RTMRegenAfterResizeDetour(); RTMRegenAfterResizeDetour();
@ -221,7 +320,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
(rtm->Resolution_Width != widthS || rtm->Resolution_Height != heightS)) (rtm->Resolution_Width != widthS || rtm->Resolution_Height != heightS))
{ {
// Can also be forced via dev->RequestResolutionChange, but while cleaner on paper, it trips up ReShade. // Can also be forced via dev->RequestResolutionChange, but while cleaner on paper, it trips up ReShade.
Service.PluginLog.Debug("_forceUpdateRTM -> 1"); Service.PluginLog.Debug($"_forceUpdateRTM -> 1 - expected {widthS} x {heightS}, got {rtm->Resolution_Width} x {rtm->Resolution_Height}");
_forceUpdateRTM = ForceUpdateRTMState._1_FakeInv; _forceUpdateRTM = ForceUpdateRTMState._1_FakeInv;
} }
@ -259,12 +358,14 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
private static void GetScaledWidthHeight(uint width, uint height, float scale, out uint widthS, out uint heightS) private static void GetScaledWidthHeight(uint width, uint height, float scale, out uint widthS, out uint heightS)
{ {
heightS = (uint) MathF.Round(height * scale); heightS = (uint) MathF.Round(height * scale, MidpointRounding.AwayFromZero);
widthS = (width * heightS) / height; widthS = (width * heightS) / height;
} }
private void RTMApplyScalingDetour(RenderTargetManagerEx* rtm, uint* size, byte unk1) private void RTMApplyScalingDetour(RenderTargetManagerEx* rtm, uint* size, byte unk1)
{ {
Debug.Assert(rtm == RenderTargetManager.Instance());
ref var cfg = ref Service.Config._.Game; ref var cfg = ref Service.Config._.Game;
if (Service.Plugin.Unloading || !cfg.IsEnabled) if (Service.Plugin.Unloading || !cfg.IsEnabled)
{ {
@ -274,7 +375,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
var gfx = (GraphicsConfigEx*) GraphicsConfig.Instance(); var gfx = (GraphicsConfigEx*) GraphicsConfig.Instance();
var _DynamicRezoEnableBeyond1 = gfx->DynamicRezoEnableBeyond1; var _DynamicRezoEnableBeyond1 = gfx->DynamicRezoEnableBeyond1;
gfx->DynamicRezoEnableBeyond1 = 1; gfx->DynamicRezoEnableBeyond1 = (byte) (cfg.Scale > 1f ? 1 : 0);
Service.PluginLog.Debug($"Applying scaling, before: {size[0]} {size[1]} {unk1}"); Service.PluginLog.Debug($"Applying scaling, before: {size[0]} {size[1]} {unk1}");
_rtmApplyScalingHook.OriginalDisposeSafe(rtm, size, unk1); _rtmApplyScalingHook.OriginalDisposeSafe(rtm, size, unk1);
@ -295,7 +396,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
Service.PluginLog.Debug($"Destroying RTM resources"); Service.PluginLog.Debug($"Destroying RTM resources");
byte rv = _rtmDestroyAfterResizeHook.OriginalDisposeSafe(); byte rv = _rtmDestroyAfterResizeHook.OriginalDisposeSafe();
Service.PluginLog.Debug($"After: 0x{(long) (nint) rtm->_.Unk20[0].Value->D3D11Texture2D:X16}"); Service.PluginLog.Debug($"After: {rv} 0x{(long) (nint) rtm->GameplayTextureUnk1->D3D11Texture2D:X16} 0x{(long) (nint) rtm->GameplayTextureUnk3->D3D11Texture2D:X16}");
return rv; return rv;
} }
@ -314,14 +415,17 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
using var scale = new ScaleState(); using var scale = new ScaleState();
Service.PluginLog.Debug($"Regenerating RTM resources: {dev->Width} x {dev->Height}"); Service.PluginLog.Debug($"Regenerating RTM resources: {dev->Width} x {dev->Height}");
_rtmRegenAfterResizeHook.OriginalDisposeSafe(); _rtmRegenAfterResizeHook.OriginalDisposeSafe();
Service.PluginLog.Debug($"After: 0x{(long) (nint) rtm->_.Unk20[0].Value->D3D11Texture2D:X16}"); Service.PluginLog.Debug($"After: 0x{(long) (nint) rtm->GameplayTextureUnk1->D3D11Texture2D:X16} 0x{(long) (nint) rtm->GameplayTextureUnk3->D3D11Texture2D:X16}");
} }
private void ICDX11ProcessCommandsDetour(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count) private void ImmediateProcessCommandsDetour(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count)
{ {
_drawCount = 0;
// Service.PluginLog.Debug("--- ImmediateProcessCommands ---");
lock (_renderLock) lock (_renderLock)
{ {
_icdx11ProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count); _immediateProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
} }
} }
@ -346,6 +450,121 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
} }
} }
private ulong PrepareTextureDetour(Texture* tex, uint* size, byte mips, uint format)
{
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
if (tex != rtm->GameplayTextureUnk1 && tex != rtm->GameplayTextureUnk3)
{
return _prepareTextureHook.OriginalDisposeSafe(tex, size, mips, format);
}
ref var cfg = ref Service.Config._;
if (!Service.Plugin.Unloading && cfg.Game.IsEnabled && cfg.Game.Scale > 2f)
{
mips = (byte) MathF.Ceiling(MathF.Log2(cfg.Game.Scale));
}
Service.PluginLog.Debug($"Preparing gameplay texture 0x{(long) (nint) tex:X16} with {mips} mips, currently 0x{(long) (nint) tex->D3D11Texture2D:X16}");
var rv = _prepareTextureHook.OriginalDisposeSafe(tex, size, mips, format);
Service.PluginLog.Debug($"After: 0x{rv:X16} 0x{(long) (nint) tex->D3D11Texture2D:X16} 0x{(long) (nint) tex->D3D11ShaderResourceView:X16}");
return rv;
}
private void ImmediateBindPSCSSRVsDetourCommon(
bool isPS,
SDX11.CommonShaderStage stage,
ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data
)
{
Debug.Assert(im == Device.Instance()->ImmediateContext);
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
bool called = false;
// TODO: Identifying by the shader might be much more reliable long-term.
if (ps->Shader.SamplerCount >= 1 && (data[0].Texture == rtm->GameplayTextureUnk1 || data[0].Texture == rtm->GameplayTextureUnk3))
{
// Service.PluginLog.Debug($"ImmediateBind{(isPS ? "PS" : "CS")}SRVs #{_drawCount} 0x{(long) (nint) ps}");
var tex = data[0].Texture;
Orig();
SDX11.SamplerState? sampler = null;
if (Service.Config._.Game.IsEnabled && Service.Config._.ResolutionScalingMode == ResolutionScalingMode.Point)
{
sampler = _samplerPoint;
}
else if (tex->MipLevel != 1)
{
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, sampler);
}
}
}
Orig();
void Orig()
{
if (called)
{
return;
}
called = true;
_drawCount++;
if (isPS)
{
_immediateBindPSSRVsHook.OriginalDisposeSafe(im, ps, data);
}
else
{
_immediateBindCSSRVsHook.OriginalDisposeSafe(im, (Shader*) ps, data);
}
}
}
private void ImmediateBindPSSRVsDetour(ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data)
{
ImmediateBindPSCSSRVsDetourCommon(true, _d3dctx.PixelShader, im, ps, data);
}
private void ImmediateBindCSSRVsDetour(ImmediateContextEx* im, Shader* cs, ImmediateSRV* data)
{
ImmediateBindPSCSSRVsDetourCommon(false, _d3dctx.ComputeShader, im, (PixelShader*) cs, data);
}
private int CreateTexture2DDetour(ID3D11Device* d3ddev, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* initialData, ID3D11Texture2D** tex)
{
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
if (tex != &rtm->GameplayTextureUnk1->D3D11Texture2D && tex != &rtm->GameplayTextureUnk3->D3D11Texture2D)
{
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
}
if (rtm->GameplayTextureUnk1->MipLevel != 1)
{
desc->MiscFlags |= (uint) D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_GENERATE_MIPS;
}
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
}
private enum ForceUpdateRTMState private enum ForceUpdateRTMState
{ {
_0_Idle, _0_Idle,
@ -384,7 +603,6 @@ public unsafe struct GraphicsConfigEx
[FieldOffset(0)] [FieldOffset(0)]
public GraphicsConfig _; public GraphicsConfig _;
// CRES_CLEAN never
public ref byte DynamicRezoEnable => ref _.DynamicRezoEnable; public ref byte DynamicRezoEnable => ref _.DynamicRezoEnable;
// Set to 0 in main menu and gpose, preventing scaling beyond 1. // Set to 0 in main menu and gpose, preventing scaling beyond 1.
public ref byte DynamicRezoEnableBeyond1 => ref RefPtr.For(ref DynamicRezoEnable).Offs<byte>(0x1).Ref; public ref byte DynamicRezoEnableBeyond1 => ref RefPtr.For(ref DynamicRezoEnable).Offs<byte>(0x1).Ref;
@ -406,6 +624,20 @@ public unsafe struct RenderTargetManagerEx
[FieldOffset(0)] [FieldOffset(0)]
public RenderTargetManager _; public RenderTargetManager _;
// Title screen: Gets blitted FROM after actual gameplay texture. Actual purpose unknown.
// In-game outdoors: Gets blitted to backbuffer.
[FieldOffset(0x68)]
public Texture* GameplayTextureUnk1;
// Gets blitted TO after actual gameplay texture, using Unk1. Actual purpose unknown.
[FieldOffset(0x100)]
public Texture* GameplayTextureUnk2;
// Title screen: Gets blitted to backbuffer.
// In-game outdoors: Totally unknown.
[FieldOffset(0x258)]
public Texture* GameplayTextureUnk3;
[FieldOffset(0x428)] [FieldOffset(0x428)]
public uint Resolution_Width; public uint Resolution_Width;
[FieldOffset(0x428 + 0x4)] [FieldOffset(0x428 + 0x4)]
@ -431,3 +663,50 @@ public unsafe struct RenderTargetManagerEx
[FieldOffset(0x70C + 4 * 4)] [FieldOffset(0x70C + 4 * 4)]
public float GraphicsRezoScaleUnk2; public float GraphicsRezoScaleUnk2;
} }
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
public unsafe struct ImmediateSRV
{
// D3D11ShaderResourceView must be at 0x58 + (GlobalIndex % 3) * 8
// Smells like something swapchain-adjacent, but have yet to analyze anything with != null.
[FieldOffset(0x0)]
public nint Unkx0;
[FieldOffset(0x8)]
public Texture* Texture;
[FieldOffset(0x10)]
public uint Unkx10;
[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,8 +1,8 @@
using Dalamud.Game.ClientState.Keys; using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.Config; using Dalamud.Game.Config;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.System.Framework;
using ImGuiNET; using ImGuiNET;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -17,6 +17,9 @@ public sealed unsafe class Plugin : IDalamudPlugin
private readonly List<Cmd> _cmds; private readonly List<Cmd> _cmds;
private bool _ignoreConfigChanges = false; private bool _ignoreConfigChanges = false;
private int _lagsIndex = 0;
private readonly bool[] _lagsAll = new bool[5];
public Plugin(IDalamudPluginInterface pluginInterface) public Plugin(IDalamudPluginInterface pluginInterface)
{ {
pluginInterface.Create<Service>(); pluginInterface.Create<Service>();
@ -55,6 +58,9 @@ public sealed unsafe class Plugin : IDalamudPlugin
public bool Unloading { get; private set; } = false; public bool Unloading { get; private set; } = false;
public int LagsTotal { get; private set; } = 0;
public bool Lag { get; private set; } = false;
public void Dispose() public void Dispose()
{ {
Unloading = true; Unloading = true;
@ -81,10 +87,25 @@ public sealed unsafe class Plugin : IDalamudPlugin
private void OnFrameworkUpdateForSize(IFramework framework, string type, ref ConfigurationV1.SizeConfig size) private void OnFrameworkUpdateForSize(IFramework framework, string type, ref ConfigurationV1.SizeConfig size)
{ {
if (Service.ClientState.IsLoggedIn && framework.UpdateDelta.TotalSeconds >= 1 && Service.Config._.DisableOnHang && size.IsEnabled) if (Lag && Service.Config._.DisableOnHang && size.IsEnabled)
{ {
Service.PluginLog.Warning($"Disabling because hang: {type}"); Service.PluginLog.Warning($"Disabling because hang: {type}");
if (Service.ClientState.IsLoggedIn)
{
Service.PrintChat($"Lag detected, \"{type}\" custom resolution disabled."); Service.PrintChat($"Lag detected, \"{type}\" custom resolution disabled.");
}
else
{
// An initial duration of 15 might seem absurd, but we're dealing with lag here!
Service.NotificationManager.AddNotification(new Notification()
{
Title = "CustomResolution",
Content = $"Lag detected, \"{type}\" custom resolution disabled.",
Type = NotificationType.Error,
InitialDuration = TimeSpan.FromSeconds(framework.UpdateDelta.TotalSeconds + 15),
Minimized = false
});
}
size.IsEnabled = false; size.IsEnabled = false;
Service.Config.Save(); Service.Config.Save();
} }
@ -115,6 +136,18 @@ public sealed unsafe class Plugin : IDalamudPlugin
private void OnFrameworkUpdate(IFramework framework) private void OnFrameworkUpdate(IFramework framework)
{ {
if (_lagsAll[_lagsIndex])
{
LagsTotal--;
}
if (_lagsAll[_lagsIndex] = framework.UpdateDelta.TotalSeconds > 1.0)
{
LagsTotal++;
}
_lagsIndex = (_lagsIndex + 1) % _lagsAll.Length;
Lag = LagsTotal == _lagsAll.Length;
lock (_disposeLock) lock (_disposeLock)
{ {
OnFrameworkUpdateForSize(framework, "Display", ref Service.Config._.Display); OnFrameworkUpdateForSize(framework, "Display", ref Service.Config._.Display);

View file

@ -1,6 +1,7 @@
using CustomResolution.Hooks; using CustomResolution.Hooks;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.Text; using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.IoC; using Dalamud.IoC;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
@ -57,6 +58,9 @@ public sealed class Service
[PluginService] [PluginService]
public static IGameConfig GameConfig { get; private set; } = null!; public static IGameConfig GameConfig { get; private set; } = null!;
[PluginService]
public static INotificationManager NotificationManager { get; private set; } = null!;
public static void PrintChat(string msg) public static void PrintChat(string msg)
{ {
ChatGui.Print(new XivChatEntry ChatGui.Print(new XivChatEntry

View file

@ -125,7 +125,22 @@ Changed via /cres");
private void DrawGameTab() 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()) if (ImGui.IsItemHovered())
{ {
@ -147,6 +162,21 @@ Changed via /gres");
return; 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.ToHumanNameString()}
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()) using (ImRaii.Disabled())
{ {
ImGui.InputInt2("Current size", ref _displayCurrentGameWH[0]); ImGui.InputInt2("Current size", ref _displayCurrentGameWH[0]);
@ -154,22 +184,13 @@ Changed via /gres");
DrawSizeTabShared(ref _.Game, "Game", scaleOnly: true); DrawSizeTabShared(ref _.Game, "Game", scaleOnly: true);
using (_.Game.Scale > 2f ? ImRaii.PushColor(ImGuiCol.Border, 0xff00ffff) : null)
{
ImGui.InputFloat("Scale", ref _.Game.Scale, 0.01f, 0.1f, "%.3f", ImGuiInputTextFlags.None); ImGui.InputFloat("Scale", ref _.Game.Scale, 0.01f, 0.1f, "%.3f", ImGuiInputTextFlags.None);
}
if (_.Game.Scale > 2f && ImGui.IsItemHovered())
{
ImGui.SetTooltip(@"Scaling larger than 2x currently doesn't improve antialiasing!
This is still being worked on before the v1.0 release.");
}
if (ImGui.BeginCombo("Graphics Upscaling", _.ResolutionScalingMode.ToHumanNameString())) if (ImGui.BeginCombo("Graphics Upscaling", _.ResolutionScalingMode.ToHumanNameString()))
{ {
foreach (var mode in Enum.GetValues<ResolutionScalingMode>()) 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; _.ResolutionScalingMode = mode;
} }
@ -185,11 +206,15 @@ This is still being worked on before the v1.0 release.");
if (Service.DebugConfig.IsDebug) if (Service.DebugConfig.IsDebug)
{ {
if (ImGui.Button("(DEBUG) Set game config to DLSS"))
{
Service.GameSize.SetConfigGraphicsRezoType(ResolutionScalingMode.DLSS);
}
ImGui.Text(Service.GameSize.DebugInfo); ImGui.Text(Service.GameSize.DebugInfo);
} }
} }
private void DrawSizeTabShared(ref ConfigurationV1.SizeConfig size, string name, bool scaleOnly = false) private void DrawSizeTabShared(ref ConfigurationV1.SizeConfig size, string name, bool scaleOnly = false)
{ {
ImGui.Checkbox("Enabled", ref size.IsEnabled); ImGui.Checkbox("Enabled", ref size.IsEnabled);
@ -305,11 +330,11 @@ Not intended to be used with proper fullscreen.");
ImGui.EndCombo(); ImGui.EndCombo();
} }
ImGui.Checkbox("Game hang protection", ref _.DisableOnHang); ImGui.Checkbox("Disable on lag", ref _.DisableOnHang);
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
ImGui.SetTooltip(@$"Automatically disables scaling if the game hangs for more than a second. ImGui.SetTooltip(@$"Automatically disables scaling if FPS goes below 1 FPS.
Leave this option enabled if you want this plugin to disable itself as a protection Leave this option enabled if you want this plugin to disable itself as a protection
in case the resolution changes unexpectedly, or in case the resolution goes too high. in case the resolution changes unexpectedly, or in case the resolution goes too high.
@ -317,5 +342,16 @@ 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: In the absolute worst case, delete the following file to reset your settings:
%%AppData%%\XIVLauncher\pluginConfigs\{Service.PluginName}.json"); %%AppData%%\XIVLauncher\pluginConfigs\{Service.PluginName}.json");
} }
#if DEBUG
var debug = Service.DebugConfig.IsDebug;
ImGui.Checkbox("DEBUG!", ref debug);
Service.DebugConfig.IsDebug = debug;
#endif
if (Service.DebugConfig.IsDebug)
{
ImGui.Text($"LAG: {Service.Plugin.LagsTotal} {Service.Plugin.Lag}");
}
} }
} }

View file

@ -8,27 +8,6 @@
"resolved": "12.0.0", "resolved": "12.0.0",
"contentHash": "J5TJLV3f16T/E2H2P17ClWjtfEBPpq3yxvqW46eN36JCm6wR+EaoaYkqG9Rm5sHqs3/nK/vKjWWyvEs/jhKoXw==" "contentHash": "J5TJLV3f16T/E2H2P17ClWjtfEBPpq3yxvqW46eN36JCm6wR+EaoaYkqG9Rm5sHqs3/nK/vKjWWyvEs/jhKoXw=="
}, },
"SharpDX.D3DCompiler": {
"type": "Direct",
"requested": "[4.2.0, )",
"resolved": "4.2.0",
"contentHash": "Rnsd6Ilp127xbXqhTit8WKFQUrXwWxqVGpglyWDNkIBCk0tWXNQEjrJpsl0KAObzyZaa33+EXAikLVt5fnd3GA==",
"dependencies": {
"NETStandard.Library": "1.6.1",
"SharpDX": "4.2.0"
}
},
"SharpDX.Direct2D1": {
"type": "Direct",
"requested": "[4.2.0, )",
"resolved": "4.2.0",
"contentHash": "Qs8LzDMaQf1u3KB8ArHu9pDv6itZ++QXs99a/bVAG+nKr0Hx5NG4mcN5vsfE0mVR2TkeHfeUm4PksRah6VUPtA==",
"dependencies": {
"NETStandard.Library": "1.6.1",
"SharpDX": "4.2.0",
"SharpDX.DXGI": "4.2.0"
}
},
"SharpDX.Direct3D11": { "SharpDX.Direct3D11": {
"type": "Direct", "type": "Direct",
"requested": "[4.2.0, )", "requested": "[4.2.0, )",
@ -40,12 +19,6 @@
"SharpDX.DXGI": "4.2.0" "SharpDX.DXGI": "4.2.0"
} }
}, },
"TerraFX.Interop.Windows": {
"type": "Direct",
"requested": "[10.0.22621.2, )",
"resolved": "10.0.22621.2",
"contentHash": "lORoYCoURS33Vi7PDvubRugLF2+l5v3rX2oVEqNhpBLjs3aZpqapRvTHPKVwsC+dGrsGuqJ/3yXuxVUb0vl3vg=="
},
"Microsoft.NETCore.Platforms": { "Microsoft.NETCore.Platforms": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.1.0", "resolved": "1.1.0",

View file

@ -23,15 +23,6 @@ everything where it was while still allowing you to switch between game scaling
is that this does NOT affect the screenshot resolution. is that this does NOT affect the screenshot resolution.
</details> </details>
<details><summary>I'm scaling by 3x or higher, but I'm still getting jagged edges!</summary>
The plugin currently only gives FFXIV a fake resolution to work with internally, but it doesn't ensure that the
extra pixels land on your screen. When scaling the display resolution, those will still be used for in-game or ReShade screenshots though.
Windows should perform basic filtering with 2x scaling (it might not work on some graphics cards?), and I haven't
heard back from anyone using this on Linux yet. But an universal fix for this is possible - it just requires more
time than I have on my hands right now.
</details>
<details><summary>I'm using DLSS, and my game looks pixelated / is small!</summary> <details><summary>I'm using DLSS, and my game looks pixelated / is small!</summary>
DLSS in FFXIV is bugged. No, I'm not joking: even without any plugins or mods loaded, it can glitch out when DLSS in FFXIV is bugged. No, I'm not joking: even without any plugins or mods loaded, it can glitch out when
simply resizing the window (most notably by rapidly pressing WinKey + Down and WinKey + Up). simply resizing the window (most notably by rapidly pressing WinKey + Down and WinKey + Up).