Compare commits
1 commit
main
...
feature-mi
Author | SHA1 | Date | |
---|---|---|---|
8e5ad95b8d |
13 changed files with 556 additions and 343 deletions
|
@ -115,51 +115,18 @@ public static class MinSizeModeExt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ResolutionScalingMode
|
public enum ResolutionScalingMode : byte
|
||||||
{
|
{
|
||||||
Point = 0,
|
Fast = 0,
|
||||||
Linear = 1,
|
FSR = 1,
|
||||||
FSR = 2,
|
DLSS = 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.Point => "Pixelated",
|
ResolutionScalingMode.Fast => "Fast Pixelation",
|
||||||
ResolutionScalingMode.Linear => "Blurry",
|
|
||||||
ResolutionScalingMode.FSR => "AMD FSR",
|
ResolutionScalingMode.FSR => "AMD FSR",
|
||||||
ResolutionScalingMode.DLSS => "NVIDIA DLSS",
|
ResolutionScalingMode.DLSS => "NVIDIA DLSS",
|
||||||
_ => mode.ToString(),
|
_ => mode.ToString(),
|
||||||
|
@ -167,25 +134,11 @@ public static class ResolutionScalingModeExt
|
||||||
|
|
||||||
public static string? ToHumanInfoString(this ResolutionScalingMode mode) => mode switch
|
public static string? ToHumanInfoString(this ResolutionScalingMode mode) => mode switch
|
||||||
{
|
{
|
||||||
ResolutionScalingMode.Point => "Lowest quality option which results in pixelation.",
|
ResolutionScalingMode.Fast => "Lowest quality option which results in blurry 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 IsBroken(this ResolutionScalingMode mode) => mode switch
|
public static bool IsUnsupported(this ResolutionScalingMode mode) => mode == ResolutionScalingMode.DLSS;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors>0x0ade</Authors>
|
<Authors>0x0ade</Authors>
|
||||||
<Company></Company>
|
<Company></Company>
|
||||||
<Version>0.4.2.1</Version>
|
<Version>0.4.1.0</Version>
|
||||||
<Description></Description>
|
<Description></Description>
|
||||||
<Copyright></Copyright>
|
<Copyright></Copyright>
|
||||||
<PackageProjectUrl></PackageProjectUrl>
|
<PackageProjectUrl></PackageProjectUrl>
|
||||||
|
@ -29,6 +29,15 @@
|
||||||
<DalamudLibPath Condition="$(DALAMUD_HOME) != ''">$(DALAMUD_HOME)/</DalamudLibPath>
|
<DalamudLibPath Condition="$(DALAMUD_HOME) != ''">$(DALAMUD_HOME)/</DalamudLibPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Shaders\Example.hlsl" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Shaders\Blit.hlsl" />
|
||||||
|
<EmbeddedResource Include="Shaders\Example.hlsl" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DalamudPackager" Version="12.0.0" />
|
<PackageReference Include="DalamudPackager" Version="12.0.0" />
|
||||||
<Reference Include="FFXIVClientStructs">
|
<Reference Include="FFXIVClientStructs">
|
||||||
|
@ -75,7 +84,9 @@
|
||||||
<HintPath>$(DalamudLibPath)TerraFX.Interop.Windows.dll</HintPath>
|
<HintPath>$(DalamudLibPath)TerraFX.Interop.Windows.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private>false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" />
|
||||||
<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>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Game.Config;
|
using CustomResolution.Shaders;
|
||||||
|
using Dalamud.Game.Config;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||||
|
@ -6,12 +7,12 @@ using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
using FloppyUtils;
|
using FloppyUtils;
|
||||||
using SharpDX;
|
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.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
using Device = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device;
|
using Device = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device;
|
||||||
using SDX11 = SharpDX.Direct3D11;
|
|
||||||
|
|
||||||
namespace CustomResolution;
|
namespace CustomResolution;
|
||||||
|
|
||||||
|
@ -21,55 +22,28 @@ 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<ImmediateProcessCommands> _immediateProcessCommandsHook;
|
private Hook<ICDX11ProcessCommands> _icdx11ProcessCommandsHook;
|
||||||
private Hook<DDX11PostTick> _ddx11PostTickHook;
|
private Hook<DDX11PostTick> _ddx11PostTickHook;
|
||||||
private Hook<TaskRenderGraphicsRender> _taskRenderGraphicsRenderHook;
|
private Hook<TaskRenderGraphicsRender> _taskRenderGraphicsRenderHook;
|
||||||
private Hook<PrepareTexture> _prepareTextureHook;
|
private Hook<PrepareTexture> _prepareTextureHook;
|
||||||
private Hook<ImmediateBindPSSRVs> _immediateBindPSSRVsHook;
|
private Hook<ImmediatePreparePS> _immediatePreparePSHook;
|
||||||
private Hook<ImmediateBindCSSRVs> _immediateBindCSSRVsHook;
|
private Hook<ImmediatePrepareCS> _immediatePrepareCSHook;
|
||||||
private Hook<CreateTexture2D> _createTexture2DHook;
|
private Hook<CreateTexture2D> _createTexture2DHook;
|
||||||
|
private Hook<Draw> _drawHook;
|
||||||
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 uint _gameplayDrawCount = 0;
|
||||||
|
private bool _drawIsGameplay = false;
|
||||||
|
|
||||||
private ResolutionScalingMode? _setConfigGraphicsRezoType;
|
private BlitShader _blitShader;
|
||||||
|
|
||||||
public GameSizeState()
|
public GameSizeState()
|
||||||
{
|
{
|
||||||
var pfx = PostEffectManagerEx.Instance();
|
_blitShader = new();
|
||||||
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??
|
||||||
|
@ -80,6 +54,8 @@ 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.
|
||||||
|
@ -111,11 +87,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...
|
||||||
*/
|
*/
|
||||||
_immediateProcessCommandsHook = Service.GameInteropProvider.HookFromAddress<ImmediateProcessCommands>(
|
_icdx11ProcessCommandsHook = Service.GameInteropProvider.HookFromAddress<ICDX11ProcessCommands>(
|
||||||
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"),
|
||||||
ImmediateProcessCommandsDetour
|
ICDX11ProcessCommandsDetour
|
||||||
);
|
);
|
||||||
_immediateProcessCommandsHook.Enable();
|
_icdx11ProcessCommandsHook.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"),
|
||||||
|
@ -153,25 +129,33 @@ public unsafe class GameSizeState : IDisposable
|
||||||
|
|
||||||
/* The shader resource view of GameplayTexture gets read by these two.
|
/* The shader resource view of GameplayTexture gets read by these two.
|
||||||
*/
|
*/
|
||||||
_immediateBindPSSRVsHook = Service.GameInteropProvider.HookFromAddress<ImmediateBindPSSRVs>(
|
_immediatePreparePSHook = Service.GameInteropProvider.HookFromAddress<ImmediatePreparePS>(
|
||||||
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"),
|
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
|
ImmediatePreparePSDetour
|
||||||
);
|
);
|
||||||
_immediateBindPSSRVsHook.Enable();
|
_immediatePreparePSHook.Enable();
|
||||||
_immediateBindCSSRVsHook = Service.GameInteropProvider.HookFromAddress<ImmediateBindCSSRVs>(
|
_immediatePrepareCSHook = Service.GameInteropProvider.HookFromAddress<ImmediatePrepareCS>(
|
||||||
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"),
|
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
|
ImmediatePrepareCSDetour
|
||||||
);
|
);
|
||||||
_immediateBindCSSRVsHook.Enable();
|
_immediatePrepareCSHook.Enable();
|
||||||
|
|
||||||
/* And because all that isn't bad enough:
|
/* Hook ID3D11Device funcs based on their vtable indices,
|
||||||
* Hook CreateTexture2D to forcibly set the mipmap generation flag.
|
* because FFXIV might not set some flags we would like to have set on the textures.,
|
||||||
|
* and because aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
*/
|
*/
|
||||||
|
ID3D11Device* d3ddev = null;
|
||||||
|
dev->D3D11DeviceContext->GetDevice(&d3ddev);
|
||||||
_createTexture2DHook = Service.GameInteropProvider.HookFromAddress<CreateTexture2D>(
|
_createTexture2DHook = Service.GameInteropProvider.HookFromAddress<CreateTexture2D>(
|
||||||
((ID3D11Device*) _d3ddev.NativePointer)->lpVtbl[5],
|
d3ddev->lpVtbl[5],
|
||||||
CreateTexture2DDetour
|
CreateTexture2DDetour
|
||||||
);
|
);
|
||||||
_createTexture2DHook.Enable();
|
_createTexture2DHook.Enable();
|
||||||
|
_drawHook = Service.GameInteropProvider.HookFromAddress<Draw>(
|
||||||
|
dev->D3D11DeviceContext->lpVtbl[13],
|
||||||
|
DrawDetour
|
||||||
|
);
|
||||||
|
_drawHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ConfigDynRezo { get; private set; }
|
public bool ConfigDynRezo { get; private set; }
|
||||||
|
@ -193,7 +177,7 @@ public unsafe class GameSizeState : IDisposable
|
||||||
private delegate void RTMRegenAfterResize();
|
private delegate void RTMRegenAfterResize();
|
||||||
|
|
||||||
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
private delegate void ImmediateProcessCommands(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count);
|
private delegate void ICDX11ProcessCommands(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count);
|
||||||
|
|
||||||
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
private delegate void DDX11PostTick(DeviceEx* dev);
|
private delegate void DDX11PostTick(DeviceEx* dev);
|
||||||
|
@ -205,55 +189,41 @@ public unsafe class GameSizeState : IDisposable
|
||||||
private delegate ulong PrepareTexture(Texture* tex, uint* size, byte mips, uint format);
|
private delegate ulong PrepareTexture(Texture* tex, uint* size, byte mips, uint format);
|
||||||
|
|
||||||
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
private delegate void ImmediateBindPSSRVs(ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data);
|
private delegate void ImmediatePreparePS(ImmediateContextEx* im, PixelShader* ps, ImmediateBlitResource* data);
|
||||||
|
|
||||||
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
private delegate void ImmediateBindCSSRVs(ImmediateContextEx* im, Shader* cs, ImmediateSRV* data);
|
private delegate void ImmediatePrepareCS(ImmediateContextEx* im, Shader* cs, ImmediateBlitResource* data);
|
||||||
|
|
||||||
private delegate int CreateTexture2D(ID3D11Device* d3ddev, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* initialData, ID3D11Texture2D** tex);
|
private delegate int CreateTexture2D(ID3D11Device* d3ddev, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* initialData, ID3D11Texture2D** tex);
|
||||||
|
|
||||||
|
private delegate void Draw(ID3D11DeviceContext* d3dctx, uint vertexCount, uint startVertexPosition);
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_rtmApplyScalingHook.Dispose();
|
_rtmApplyScalingHook.Dispose();
|
||||||
_rtmDestroyAfterResizeHook.Dispose();
|
_rtmDestroyAfterResizeHook.Dispose();
|
||||||
_rtmRegenAfterResizeHook.Dispose();
|
_rtmRegenAfterResizeHook.Dispose();
|
||||||
_immediateProcessCommandsHook.Dispose();
|
_icdx11ProcessCommandsHook.Dispose();
|
||||||
_ddx11PostTickHook.Dispose();
|
_ddx11PostTickHook.Dispose();
|
||||||
_taskRenderGraphicsRenderHook.Dispose();
|
_taskRenderGraphicsRenderHook.Dispose();
|
||||||
_prepareTextureHook.Dispose();
|
_prepareTextureHook.Dispose();
|
||||||
_immediateBindPSSRVsHook.Dispose();
|
_immediatePreparePSHook.Dispose();
|
||||||
_immediateBindCSSRVsHook.Dispose();
|
_immediatePrepareCSHook.Dispose();
|
||||||
_createTexture2DHook.Dispose();
|
_createTexture2DHook.Dispose();
|
||||||
lock (_renderLock)
|
_drawHook.Dispose();
|
||||||
{
|
_blitShader.Dispose();
|
||||||
_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 = ResolutionScalingModeExt.FromXIVSysConf(Service.GameConfig.System.GetUInt(SystemConfigOption.GraphicsRezoUpscaleType.ToString()));
|
ConfigGraphicsRezoType = (ResolutionScalingMode) (Service.GameConfig.System.GetUInt(SystemConfigOption.GraphicsRezoUpscaleType.ToString()) + 1);
|
||||||
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();
|
||||||
|
@ -262,8 +232,14 @@ public unsafe class GameSizeState : IDisposable
|
||||||
|
|
||||||
if (Service.DebugConfig.IsDebug)
|
if (Service.DebugConfig.IsDebug)
|
||||||
{
|
{
|
||||||
_dbg.Append(@$"DR !{gfx->DynamicRezoEnable} +{gfx->DynamicRezoEnableBeyond1} _{gfx->DynamicRezoEnableCutScene} ?{gfx->DynamicRezoEnableUnkx47} ?{gfx->DynamicRezoEnableUnkx48}
|
_dbg.Append(@$"RTMApplyScaling 0x{(_rtmApplyScalingHook.IsDisposed ? null : _rtmApplyScalingHook.Address):X16}
|
||||||
|
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}
|
||||||
|
@ -280,12 +256,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 = cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode.ToXIVGFX() : ResolutionScalingMode.Linear.ToXIVGFX();
|
gfx->GraphicsRezoUpscaleType = (byte) (cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode : ResolutionScalingMode.Fast);
|
||||||
rtm->DynamicResolutionMinimumHeight = rtm->DynamicResolutionMaximumHeight;
|
rtm->DynamicResolutionMinimumHeight = rtm->DynamicResolutionMaximumHeight;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gfx->GraphicsRezoUpscaleType = ConfigGraphicsRezoType.ToSupported().ToXIVGFX();
|
gfx->GraphicsRezoUpscaleType = (byte) ConfigGraphicsRezoType;
|
||||||
}
|
}
|
||||||
_wasEnabled = enabled;
|
_wasEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
@ -293,16 +269,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->GameplayTextureUnk3;
|
var tex = rtm->GameplayTexture;
|
||||||
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 - locked ImmediateContextDX11.ProcessCommands");
|
Service.PluginLog.Debug($"Regenerating dirty RTM - locking ImmediateContextDX11.ProcessCommands");
|
||||||
dev->RequestResolutionChange = 1;
|
dev->RequestResolutionChange = 1;
|
||||||
RTMDestroyAfterResizeDetour();
|
RTMDestroyAfterResizeDetour();
|
||||||
RTMRegenAfterResizeDetour();
|
RTMRegenAfterResizeDetour();
|
||||||
|
@ -320,7 +296,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 - expected {widthS} x {heightS}, got {rtm->Resolution_Width} x {rtm->Resolution_Height}");
|
Service.PluginLog.Debug("_forceUpdateRTM -> 1");
|
||||||
_forceUpdateRTM = ForceUpdateRTMState._1_FakeInv;
|
_forceUpdateRTM = ForceUpdateRTMState._1_FakeInv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,14 +334,12 @@ 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, MidpointRounding.AwayFromZero);
|
heightS = (uint) MathF.Round(height * scale);
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -375,7 +349,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 = (byte) (cfg.Scale > 1f ? 1 : 0);
|
gfx->DynamicRezoEnableBeyond1 = 1;
|
||||||
|
|
||||||
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);
|
||||||
|
@ -396,7 +370,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: {rv} 0x{(long) (nint) rtm->GameplayTextureUnk1->D3D11Texture2D:X16} 0x{(long) (nint) rtm->GameplayTextureUnk3->D3D11Texture2D:X16}");
|
Service.PluginLog.Debug($"After: {rv} 0x{(long) (nint) rtm->GameplayTexture->D3D11Texture2D:X16}");
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,17 +389,16 @@ 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->GameplayTextureUnk1->D3D11Texture2D:X16} 0x{(long) (nint) rtm->GameplayTextureUnk3->D3D11Texture2D:X16}");
|
Service.PluginLog.Debug($"After: 0x{(long) (nint) rtm->GameplayTexture->D3D11Texture2D:X16}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImmediateProcessCommandsDetour(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count)
|
private void ICDX11ProcessCommandsDetour(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count)
|
||||||
{
|
{
|
||||||
_drawCount = 0;
|
_gameplayDrawCount = 0;
|
||||||
// Service.PluginLog.Debug("--- ImmediateProcessCommands ---");
|
|
||||||
|
|
||||||
lock (_renderLock)
|
lock (_renderLock)
|
||||||
{
|
{
|
||||||
_immediateProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
|
_icdx11ProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,16 +426,21 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
||||||
private ulong PrepareTextureDetour(Texture* tex, uint* size, byte mips, uint format)
|
private ulong PrepareTextureDetour(Texture* tex, uint* size, byte mips, uint format)
|
||||||
{
|
{
|
||||||
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
if (tex != rtm->GameplayTextureUnk1 && tex != rtm->GameplayTextureUnk3)
|
if (tex != rtm->GameplayTexture)
|
||||||
{
|
{
|
||||||
return _prepareTextureHook.OriginalDisposeSafe(tex, size, mips, format);
|
return _prepareTextureHook.OriginalDisposeSafe(tex, size, mips, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var cfg = ref Service.Config._;
|
ref var cfg = ref Service.Config._.Game;
|
||||||
|
|
||||||
if (!Service.Plugin.Unloading && cfg.Game.IsEnabled && cfg.Game.Scale > 2f)
|
// TODO: MathF.Log
|
||||||
|
mips = 1;
|
||||||
|
if (!Service.Plugin.Unloading && cfg.IsEnabled)
|
||||||
{
|
{
|
||||||
mips = (byte) MathF.Ceiling(MathF.Log2(cfg.Game.Scale));
|
for (var s = cfg.Scale; s > 2f; s /= 2f)
|
||||||
|
{
|
||||||
|
mips++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Service.PluginLog.Debug($"Preparing gameplay texture 0x{(long) (nint) tex:X16} with {mips} mips, currently 0x{(long) (nint) tex->D3D11Texture2D:X16}");
|
Service.PluginLog.Debug($"Preparing gameplay texture 0x{(long) (nint) tex:X16} with {mips} mips, currently 0x{(long) (nint) tex->D3D11Texture2D:X16}");
|
||||||
|
@ -471,93 +449,54 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImmediateBindPSCSSRVsDetourCommon(
|
private void ImmediatePreparePSDetour(ImmediateContextEx* im, PixelShader* ps, ImmediateBlitResource* data)
|
||||||
bool isPS,
|
|
||||||
SDX11.CommonShaderStage stage,
|
|
||||||
ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
Debug.Assert(im == Device.Instance()->ImmediateContext);
|
//Service.PluginLog.Debug($"ImmediatePreparePS 0x{(long) im:X16} 0x{(long) ps:X16} 0x{(long) unk3:X16}");
|
||||||
|
|
||||||
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
bool called = false;
|
var tex = rtm->GameplayTexture;
|
||||||
|
if (ps->Shader.SamplerCount >= 1 && data[0].Texture == tex)
|
||||||
// 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}");
|
_gameplayDrawCount++;
|
||||||
var tex = data[0].Texture;
|
// Service.PluginLog.Debug($"ImmediatePreparePS #{_gameplayDrawCount} 0x{(long) im:X16} 0x{(long) ps:X16} 0x{(long) data[0].Unk1:X16} 0x{(long) (nint) data[0].Texture:X16} 0x{data[0].Unk2:X8} 0x{data[0].Unk3:X8}");
|
||||||
|
|
||||||
Orig();
|
var gfx = (GraphicsConfigEx*) GraphicsConfig.Instance();
|
||||||
|
if ((gfx->GraphicsRezoUpscaleType == (byte) ResolutionScalingMode.FSR && gfx->GraphicsRezoScale < 1f) ||
|
||||||
SDX11.SamplerState? sampler = null;
|
gfx->GraphicsRezoUpscaleType == (byte) ResolutionScalingMode.DLSS)
|
||||||
|
|
||||||
if (Service.Config._.Game.IsEnabled && Service.Config._.ResolutionScalingMode == ResolutionScalingMode.Point)
|
|
||||||
{
|
{
|
||||||
sampler = _samplerPoint;
|
_drawIsGameplay = _gameplayDrawCount == 1;
|
||||||
}
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
_immediateBindCSSRVsHook.OriginalDisposeSafe(im, (Shader*) ps, data);
|
_drawIsGameplay = _gameplayDrawCount == 3;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImmediateBindPSSRVsDetour(ImmediateContextEx* im, PixelShader* ps, ImmediateSRV* data)
|
_immediatePreparePSHook.OriginalDisposeSafe(im, ps, data);
|
||||||
{
|
|
||||||
ImmediateBindPSCSSRVsDetourCommon(true, _d3dctx.PixelShader, im, ps, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImmediateBindCSSRVsDetour(ImmediateContextEx* im, Shader* cs, ImmediateSRV* data)
|
private void ImmediatePrepareCSDetour(ImmediateContextEx* im, Shader* cs, ImmediateBlitResource* data)
|
||||||
{
|
{
|
||||||
ImmediateBindPSCSSRVsDetourCommon(false, _d3dctx.ComputeShader, im, (PixelShader*) cs, data);
|
//Service.PluginLog.Debug($"ImmediatePrepareCS 0x{(long) im:X16} 0x{(long) cs:X16} 0x{(long) unk3:X16}");
|
||||||
|
|
||||||
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
|
if (((PixelShader*) cs)->Shader.SamplerCount >= 1 && data[0].Texture == rtm->GameplayTexture)
|
||||||
|
{
|
||||||
|
_gameplayDrawCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_immediatePrepareCSHook.OriginalDisposeSafe(im, cs, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int CreateTexture2DDetour(ID3D11Device* d3ddev, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* initialData, ID3D11Texture2D** tex)
|
private int CreateTexture2DDetour(ID3D11Device* d3ddev, D3D11_TEXTURE2D_DESC* desc, D3D11_SUBRESOURCE_DATA* initialData, ID3D11Texture2D** tex)
|
||||||
{
|
{
|
||||||
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
if (tex != &rtm->GameplayTextureUnk1->D3D11Texture2D && tex != &rtm->GameplayTextureUnk3->D3D11Texture2D)
|
if (tex != &rtm->GameplayTexture->D3D11Texture2D)
|
||||||
{
|
{
|
||||||
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
|
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtm->GameplayTextureUnk1->MipLevel != 1)
|
if (rtm->GameplayTexture->MipLevel != 1)
|
||||||
{
|
{
|
||||||
desc->MiscFlags |= (uint) D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_GENERATE_MIPS;
|
desc->MiscFlags |= (uint) D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_GENERATE_MIPS;
|
||||||
}
|
}
|
||||||
|
@ -565,6 +504,41 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
||||||
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
|
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawDetour(ID3D11DeviceContext* d3dctx, uint vertexCount, uint startVertexPosition)
|
||||||
|
{
|
||||||
|
ref var cfg = ref Service.Config._.Game;
|
||||||
|
if (!_drawIsGameplay)
|
||||||
|
{
|
||||||
|
_drawHook.OriginalDisposeSafe(d3dctx, vertexCount, startVertexPosition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawIsGameplay = false;
|
||||||
|
|
||||||
|
if (!cfg.IsEnabled)
|
||||||
|
{
|
||||||
|
_drawHook.OriginalDisposeSafe(d3dctx, vertexCount, startVertexPosition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
|
|
||||||
|
SharpDX.Direct3D11.SamplerStateDescription description = SharpDX.Direct3D11.SamplerStateDescription.Default();
|
||||||
|
description.Filter = SharpDX.Direct3D11.Filter.MinMagMipLinear;
|
||||||
|
using SharpDX.Direct3D11.SamplerState sampler = new(_blitShader.Device, description);
|
||||||
|
_blitShader.Ctx.PixelShader.SetSampler(0, sampler);
|
||||||
|
|
||||||
|
/*
|
||||||
|
_blitShader.Draw(
|
||||||
|
CppObject.FromPointer<SharpDX.Direct3D11.ShaderResourceView>((nint) rtm->GameplayTexture->D3D11ShaderResourceView)
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
_blitShader.Ctx.GenerateMips(CppObject.FromPointer<SharpDX.Direct3D11.ShaderResourceView>((nint) rtm->GameplayTexture->D3D11ShaderResourceView));
|
||||||
|
|
||||||
|
_drawHook.OriginalDisposeSafe(d3dctx, vertexCount, startVertexPosition);
|
||||||
|
}
|
||||||
|
|
||||||
private enum ForceUpdateRTMState
|
private enum ForceUpdateRTMState
|
||||||
{
|
{
|
||||||
_0_Idle,
|
_0_Idle,
|
||||||
|
@ -603,6 +577,7 @@ 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;
|
||||||
|
@ -624,8 +599,7 @@ public unsafe struct RenderTargetManagerEx
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public RenderTargetManager _;
|
public RenderTargetManager _;
|
||||||
|
|
||||||
// Title screen: Gets blitted FROM after actual gameplay texture. Actual purpose unknown.
|
// Gets blitted FROM after actual gameplay texture. Actual purpose unknown.
|
||||||
// In-game outdoors: Gets blitted to backbuffer.
|
|
||||||
[FieldOffset(0x68)]
|
[FieldOffset(0x68)]
|
||||||
public Texture* GameplayTextureUnk1;
|
public Texture* GameplayTextureUnk1;
|
||||||
|
|
||||||
|
@ -633,10 +607,9 @@ public unsafe struct RenderTargetManagerEx
|
||||||
[FieldOffset(0x100)]
|
[FieldOffset(0x100)]
|
||||||
public Texture* GameplayTextureUnk2;
|
public Texture* GameplayTextureUnk2;
|
||||||
|
|
||||||
// Title screen: Gets blitted to backbuffer.
|
// Gets blitted to backbuffer.
|
||||||
// In-game outdoors: Totally unknown.
|
|
||||||
[FieldOffset(0x258)]
|
[FieldOffset(0x258)]
|
||||||
public Texture* GameplayTextureUnk3;
|
public Texture* GameplayTexture;
|
||||||
|
|
||||||
[FieldOffset(0x428)]
|
[FieldOffset(0x428)]
|
||||||
public uint Resolution_Width;
|
public uint Resolution_Width;
|
||||||
|
@ -665,48 +638,19 @@ public unsafe struct RenderTargetManagerEx
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
||||||
public unsafe struct ImmediateSRV
|
public unsafe struct ImmediateBlitResource
|
||||||
{
|
{
|
||||||
// D3D11ShaderResourceView must be at 0x58 + (GlobalIndex % 3) * 8
|
// D3D11ShaderResourceView must be at 0x58 + (GlobalIndex % 3) * 8
|
||||||
// Smells like something swapchain-adjacent, but have yet to analyze anything with != null.
|
// Smells like something swapchain-adjacent, but have yet to analyze anything with != null.
|
||||||
[FieldOffset(0x0)]
|
[FieldOffset(0x0)]
|
||||||
public nint Unkx0;
|
public nint Unk1;
|
||||||
|
|
||||||
[FieldOffset(0x8)]
|
[FieldOffset(0x8)]
|
||||||
public Texture* Texture;
|
public Texture* Texture;
|
||||||
|
|
||||||
[FieldOffset(0x10)]
|
[FieldOffset(0x10)]
|
||||||
public uint Unkx10;
|
public uint Unk2;
|
||||||
|
|
||||||
[FieldOffset(0x14)]
|
[FieldOffset(0x14)]
|
||||||
public uint Unkx14;
|
public uint Unk3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
184
CustomResolution2782/MiniShader.cs
Normal file
184
CustomResolution2782/MiniShader.cs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
|
using FFXIVClientStructs.FFXIV.Common.Lua;
|
||||||
|
using MonoMod.Utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using SharpDX;
|
||||||
|
using SharpDX.D3DCompiler;
|
||||||
|
using SharpDX.Direct3D;
|
||||||
|
using SharpDX.Direct3D11;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
using SDXDevice = SharpDX.Direct3D11.Device;
|
||||||
|
using XIVDevice = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device;
|
||||||
|
|
||||||
|
namespace CustomResolution;
|
||||||
|
|
||||||
|
public unsafe class MiniShader : IDisposable
|
||||||
|
{
|
||||||
|
public MiniShader(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
|
||||||
|
var asm = Assembly.GetExecutingAssembly();
|
||||||
|
var path = $"{nameof(CustomResolution)}.Shaders.{name}.hlsl";
|
||||||
|
|
||||||
|
using (var stream = asm.GetManifestResourceStream(path) ?? throw new FileNotFoundException(path))
|
||||||
|
using (var streamReader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
Source = streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dev = (DeviceEx*) XIVDevice.Instance();
|
||||||
|
Ctx = CppObject.FromPointer<DeviceContext>((nint) dev->D3D11DeviceContext);
|
||||||
|
Device = Ctx.Device;
|
||||||
|
|
||||||
|
VS = new(Device, ShaderBytecode.Compile(Source, "vs", "vs_5_0"));
|
||||||
|
PS = new(Device, ShaderBytecode.Compile(Source, "ps", "ps_5_0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public string Source { get; private set; }
|
||||||
|
|
||||||
|
public DeviceContext Ctx { get; private set; }
|
||||||
|
public SDXDevice Device { get; private set; }
|
||||||
|
|
||||||
|
public SharpDX.Direct3D11.VertexShader VS { get; private set; }
|
||||||
|
public SharpDX.Direct3D11.PixelShader PS { get; private set; }
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
PS.Dispose();
|
||||||
|
VS.Dispose();
|
||||||
|
Device.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MiniShaderConstants<T> : IDisposable where T : unmanaged
|
||||||
|
{
|
||||||
|
private readonly MiniShader _shader;
|
||||||
|
private T _value;
|
||||||
|
|
||||||
|
public MiniShaderConstants(MiniShader shader)
|
||||||
|
{
|
||||||
|
_shader = shader;
|
||||||
|
|
||||||
|
Buffer = new(
|
||||||
|
_shader.Device,
|
||||||
|
Math.Max(16, Marshal.SizeOf<T>()),
|
||||||
|
ResourceUsage.Default,
|
||||||
|
BindFlags.ConstantBuffer,
|
||||||
|
CpuAccessFlags.None,
|
||||||
|
ResourceOptionFlags.None,
|
||||||
|
Marshal.SizeOf<T>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharpDX.Direct3D11.Buffer Buffer { get; private set; }
|
||||||
|
public ref T Value => ref _value;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Buffer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
Set(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(T data)
|
||||||
|
{
|
||||||
|
_value = data;
|
||||||
|
_shader.Ctx.UpdateSubresource(ref data, Buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MiniShaderState : IDisposable
|
||||||
|
{
|
||||||
|
private List<Action> _undos = [];
|
||||||
|
|
||||||
|
public MiniShaderState()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var undo in _undos)
|
||||||
|
{
|
||||||
|
undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set<TOwner, TValue>(TOwner owner, string name, TValue value)
|
||||||
|
{
|
||||||
|
var prev = ReflCache<TOwner>.Get<TValue>(owner, name);
|
||||||
|
_undos.Add(() => ReflCache<TOwner>.Set(owner, name, prev));
|
||||||
|
ReflCache<TOwner>.Set(owner, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetShader<T>(CommonShaderStage<T> stage, T? shader) where T : DeviceChild
|
||||||
|
{
|
||||||
|
var prev = stage.Get();
|
||||||
|
_undos.Add(() => stage.Set(prev));
|
||||||
|
stage.Set(shader!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConstantBuffer<T>(CommonShaderStage<T> stage, int slot, SharpDX.Direct3D11.Buffer buffer) where T : DeviceChild
|
||||||
|
{
|
||||||
|
var prev = stage.GetConstantBuffers(slot, 1);
|
||||||
|
_undos.Add(() => stage.SetConstantBuffer(slot, prev[0]));
|
||||||
|
stage.SetConstantBuffer(slot, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetShaderResource<T>(CommonShaderStage<T> stage, int slot, ShaderResourceView srv) where T : DeviceChild
|
||||||
|
{
|
||||||
|
var prev = stage.GetShaderResources(slot, 1);
|
||||||
|
_undos.Add(() => stage.SetShaderResource(slot, prev[0]));
|
||||||
|
stage.SetShaderResource(slot, srv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ReflCache<TOwner>
|
||||||
|
{
|
||||||
|
private static readonly Type _tOwner = typeof(TOwner);
|
||||||
|
private static readonly Dictionary<string, PropertyInfo?> _props = [];
|
||||||
|
|
||||||
|
private static PropertyInfo? GetProperty(string name)
|
||||||
|
{
|
||||||
|
if (_props.TryGetValue(name, out var prop))
|
||||||
|
{
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _props[name] = _tOwner.GetProperty(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TValue Get<TValue>(TOwner owner, string name)
|
||||||
|
{
|
||||||
|
if (GetProperty(name) is { } prop)
|
||||||
|
{
|
||||||
|
return (TValue) prop.GetValue(owner)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Set<TValue>(TOwner owner, string name, TValue value)
|
||||||
|
{
|
||||||
|
if (GetProperty(name) is { } prop)
|
||||||
|
{
|
||||||
|
prop.SetValue(owner, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,9 +17,6 @@ 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>();
|
||||||
|
@ -58,9 +55,6 @@ 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;
|
||||||
|
@ -87,25 +81,10 @@ 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 (Lag && Service.Config._.DisableOnHang && size.IsEnabled)
|
if (Service.ClientState.IsLoggedIn && framework.UpdateDelta.TotalSeconds >= 1 && 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();
|
||||||
}
|
}
|
||||||
|
@ -136,18 +115,6 @@ 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);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
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;
|
||||||
|
@ -58,9 +57,6 @@ 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
|
||||||
|
|
23
CustomResolution2782/Shaders/Blit.hlsl
Normal file
23
CustomResolution2782/Shaders/Blit.hlsl
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Texture2D<float4> inputTexture : register(t0);
|
||||||
|
SamplerState TextureSampler;
|
||||||
|
|
||||||
|
struct VSOutput
|
||||||
|
{
|
||||||
|
float4 pos : SV_POSITION;
|
||||||
|
float2 uv : TEXCOORD;
|
||||||
|
};
|
||||||
|
|
||||||
|
VSOutput vs(uint id : SV_VertexID)
|
||||||
|
{
|
||||||
|
VSOutput output;
|
||||||
|
float2 uv = float2((id << 1) & 2, id & 2);
|
||||||
|
output.pos = float4(uv * 2 - 1, 0, 1);
|
||||||
|
output.uv = uv * float2(1, -1) + float2(0, 1);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 ps(VSOutput input) : SV_Target
|
||||||
|
{
|
||||||
|
float4 color = inputTexture.Sample(TextureSampler, input.uv);
|
||||||
|
return color;
|
||||||
|
}
|
51
CustomResolution2782/Shaders/BlitShader.cs
Normal file
51
CustomResolution2782/Shaders/BlitShader.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
using SharpDX.Direct3D;
|
||||||
|
using SharpDX.Direct3D11;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
namespace CustomResolution.Shaders;
|
||||||
|
|
||||||
|
public class BlitShader : MiniShader
|
||||||
|
{
|
||||||
|
public BlitShader() : base("Blit")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(ShaderResourceView srv)
|
||||||
|
{
|
||||||
|
using MiniShaderState state = new();
|
||||||
|
|
||||||
|
state.SetShader(Ctx.VertexShader, VS);
|
||||||
|
state.SetShader(Ctx.PixelShader, PS);
|
||||||
|
state.SetShader(Ctx.GeometryShader, null);
|
||||||
|
|
||||||
|
state.Set(Ctx.InputAssembler, nameof(Ctx.InputAssembler.PrimitiveTopology), PrimitiveTopology.TriangleList);
|
||||||
|
|
||||||
|
var blendDesc = Ctx.OutputMerger.BlendState.Description;
|
||||||
|
blendDesc.RenderTarget[0].IsBlendEnabled = false;
|
||||||
|
|
||||||
|
using var blendState = new BlendState(Device, blendDesc);
|
||||||
|
state.Set(Ctx.OutputMerger, nameof(Ctx.OutputMerger.BlendState), blendState);
|
||||||
|
|
||||||
|
state.SetShaderResource(Ctx.PixelShader, 0, srv);
|
||||||
|
|
||||||
|
Ctx.Draw(3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct TConstants
|
||||||
|
{
|
||||||
|
public float Time;
|
||||||
|
}
|
||||||
|
}
|
26
CustomResolution2782/Shaders/Example.hlsl
Normal file
26
CustomResolution2782/Shaders/Example.hlsl
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
struct Constants
|
||||||
|
{
|
||||||
|
float time;
|
||||||
|
};
|
||||||
|
Constants k : register(b0);
|
||||||
|
|
||||||
|
struct VSOutput
|
||||||
|
{
|
||||||
|
float4 pos : SV_POSITION;
|
||||||
|
float2 uv : TEXCOORD;
|
||||||
|
};
|
||||||
|
|
||||||
|
VSOutput vs(uint id : SV_VertexID)
|
||||||
|
{
|
||||||
|
VSOutput output;
|
||||||
|
float2 uv = float2((id << 1) & 2, id & 2);
|
||||||
|
output.pos = float4(uv - 1, 0, 1);
|
||||||
|
output.uv = uv;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 ps(VSOutput input) : SV_Target
|
||||||
|
{
|
||||||
|
float4 color = float4(0.5, 0, frac(k.time), 0.5);
|
||||||
|
return color;
|
||||||
|
}
|
64
CustomResolution2782/Shaders/ExampleShader.cs
Normal file
64
CustomResolution2782/Shaders/ExampleShader.cs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
using SharpDX.Direct3D;
|
||||||
|
using SharpDX.Direct3D11;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
namespace CustomResolution.Shaders;
|
||||||
|
|
||||||
|
public class ExampleShader : MiniShader
|
||||||
|
{
|
||||||
|
public ExampleShader() : base("Example")
|
||||||
|
{
|
||||||
|
Constants = new(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MiniShaderConstants<TConstants> Constants { get; private set; }
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
Constants.Dispose();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
using MiniShaderState state = new();
|
||||||
|
|
||||||
|
state.SetShader(Ctx.VertexShader, VS);
|
||||||
|
state.SetShader(Ctx.PixelShader, PS);
|
||||||
|
state.SetShader(Ctx.GeometryShader, null);
|
||||||
|
|
||||||
|
state.Set(Ctx.InputAssembler, nameof(Ctx.InputAssembler.PrimitiveTopology), PrimitiveTopology.TriangleList);
|
||||||
|
|
||||||
|
state.SetConstantBuffer(Ctx.PixelShader, 0, Constants.Buffer);
|
||||||
|
|
||||||
|
Ctx.PixelShader.SetConstantBuffer(0, Constants.Buffer);
|
||||||
|
|
||||||
|
var blendDesc = Ctx.OutputMerger.BlendState.Description;
|
||||||
|
blendDesc.RenderTarget[0].IsBlendEnabled = true;
|
||||||
|
blendDesc.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;
|
||||||
|
blendDesc.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
|
||||||
|
blendDesc.RenderTarget[0].BlendOperation = BlendOperation.Add;
|
||||||
|
blendDesc.RenderTarget[0].SourceAlphaBlend = BlendOption.SourceAlpha;
|
||||||
|
blendDesc.RenderTarget[0].DestinationAlphaBlend = BlendOption.InverseSourceAlpha;
|
||||||
|
blendDesc.RenderTarget[0].AlphaBlendOperation = BlendOperation.Maximum;
|
||||||
|
blendDesc.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
|
||||||
|
|
||||||
|
using var blendState = new BlendState(Device, blendDesc);
|
||||||
|
state.Set(Ctx.OutputMerger, nameof(Ctx.OutputMerger.BlendState), blendState);
|
||||||
|
|
||||||
|
Ctx.Draw(3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct TConstants
|
||||||
|
{
|
||||||
|
public float Time;
|
||||||
|
}
|
||||||
|
}
|
|
@ -125,22 +125,7 @@ Changed via /cres");
|
||||||
|
|
||||||
private void DrawGameTab()
|
private void DrawGameTab()
|
||||||
{
|
{
|
||||||
var unsupportedSysScale = !Service.GameSize.ConfigGraphicsRezoType.IsSupported();
|
using var imTab = ImRaii.TabItem($"Gameplay##GameTab");
|
||||||
|
|
||||||
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())
|
||||||
{
|
{
|
||||||
|
@ -162,21 +147,6 @@ 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]);
|
||||||
|
@ -184,13 +154,22 @@ Click here to reset to FSR and fix the scaling slider.##FixDLSS"))
|
||||||
|
|
||||||
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.IsBroken() ? ImGuiSelectableFlags.Disabled : ImGuiSelectableFlags.None))
|
if (ImGui.Selectable(mode.ToHumanNameString(), _.ResolutionScalingMode == mode, mode.IsUnsupported() ? ImGuiSelectableFlags.Disabled : ImGuiSelectableFlags.None))
|
||||||
{
|
{
|
||||||
_.ResolutionScalingMode = mode;
|
_.ResolutionScalingMode = mode;
|
||||||
}
|
}
|
||||||
|
@ -206,15 +185,11 @@ Click here to reset to FSR and fix the scaling slider.##FixDLSS"))
|
||||||
|
|
||||||
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);
|
||||||
|
@ -330,11 +305,11 @@ Not intended to be used with proper fullscreen.");
|
||||||
ImGui.EndCombo();
|
ImGui.EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Checkbox("Disable on lag", ref _.DisableOnHang);
|
ImGui.Checkbox("Game hang protection", ref _.DisableOnHang);
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
ImGui.SetTooltip(@$"Automatically disables scaling if FPS goes below 1 FPS.
|
ImGui.SetTooltip(@$"Automatically disables scaling if the game hangs for more than a second.
|
||||||
|
|
||||||
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.
|
||||||
|
@ -342,16 +317,5 @@ 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}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,27 @@
|
||||||
"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, )",
|
||||||
|
|
|
@ -23,6 +23,15 @@ 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).
|
||||||
|
|
Loading…
Add table
Reference in a new issue