game: Implement mip gen and point scaling
This commit is contained in:
parent
51f2cd8348
commit
50ad7cff36
8 changed files with 267 additions and 78 deletions
|
@ -117,16 +117,27 @@ public static class MinSizeModeExt
|
|||
|
||||
public enum ResolutionScalingMode
|
||||
{
|
||||
Fast = 0,
|
||||
FSR = 1,
|
||||
DLSS = 2
|
||||
Point = 0,
|
||||
Linear = 1,
|
||||
FSR = 2,
|
||||
DLSS = 3
|
||||
}
|
||||
|
||||
public static class ResolutionScalingModeExt
|
||||
{
|
||||
public static byte ToXIV(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
|
||||
{
|
||||
ResolutionScalingMode.Fast => "Fast Pixelation",
|
||||
ResolutionScalingMode.Point => "Pixelated",
|
||||
ResolutionScalingMode.Linear => "Blurry",
|
||||
ResolutionScalingMode.FSR => "AMD FSR",
|
||||
ResolutionScalingMode.DLSS => "NVIDIA DLSS",
|
||||
_ => mode.ToString(),
|
||||
|
@ -134,7 +145,8 @@ public static class ResolutionScalingModeExt
|
|||
|
||||
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.DLSS => "DLSS in FFXIV is buggy, even without any plugins.",
|
||||
_ => null
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<RootNamespace>CustomResolution</RootNamespace>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -30,7 +31,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="12.0.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.22621.2" />
|
||||
<Reference Include="FFXIVClientStructs">
|
||||
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
|
@ -71,9 +71,11 @@
|
|||
<HintPath>$(DalamudLibPath)SharpDX.Mathematics.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</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.Direct2D1" Version="4.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -6,6 +6,7 @@ using FloppyUtils;
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
|
@ -421,13 +422,14 @@ public unsafe struct DeviceEx
|
|||
public ref uint NewHeight => ref _.NewHeight;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ImmediateContextEx in FFXIVClientStructs is a bare minimum.
|
||||
// https://github.com/aers/FFXIVClientStructs/blob/ef5e9a5997671fb2c9a72cb9d57d841855f62085/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ImmediateContextEx
|
||||
public unsafe struct ImmediateContextEx
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ImmediateContext _;
|
||||
|
|
|
@ -4,10 +4,13 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
|||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using FloppyUtils;
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
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;
|
||||
|
||||
|
@ -17,17 +20,44 @@ public unsafe class GameSizeState : IDisposable
|
|||
private Hook<RTMApplyScaling> _rtmApplyScalingHook;
|
||||
private Hook<RTMDestroyAfterResize> _rtmDestroyAfterResizeHook;
|
||||
private Hook<RTMRegenAfterResize> _rtmRegenAfterResizeHook;
|
||||
private Hook<ICDX11ProcessCommands> _icdx11ProcessCommandsHook;
|
||||
private Hook<ImmediateProcessCommands> _immediateProcessCommandsHook;
|
||||
private Hook<DDX11PostTick> _ddx11PostTickHook;
|
||||
private Hook<TaskRenderGraphicsRender> _taskRenderGraphicsRenderHook;
|
||||
private Hook<PrepareTexture> _prepareTextureHook;
|
||||
private Hook<ImmediateBindPSSRVs> _immediateBindPSSRVsHook;
|
||||
private Hook<ImmediateBindCSSRVs> _immediateBindCSSRVsHook;
|
||||
private Hook<CreateTexture2D> _createTexture2DHook;
|
||||
|
||||
private SDX11.DeviceContext _d3dctx;
|
||||
private SDX11.Device _d3ddev;
|
||||
private SDX11.SamplerState _samplerMips;
|
||||
private SDX11.SamplerState _samplerPoint;
|
||||
|
||||
private bool _wasEnabled = false;
|
||||
private object _renderLock = new();
|
||||
|
||||
private ForceUpdateRTMState _forceUpdateRTM = ForceUpdateRTMState._0_Idle;
|
||||
|
||||
private uint _drawCount;
|
||||
|
||||
public GameSizeState()
|
||||
{
|
||||
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);
|
||||
|
||||
/* Writes DynamicResolutionTargetHeight to size[1] near the end,
|
||||
* but if in (gpose || main menu), only scale < 1??
|
||||
*/
|
||||
|
@ -37,8 +67,6 @@ public unsafe class GameSizeState : IDisposable
|
|||
);
|
||||
_rtmApplyScalingHook.Enable();
|
||||
|
||||
var dev = (DeviceEx*) Device.Instance();
|
||||
|
||||
/* Device.RequestResolutionChange and other triggers run some callbacks, namely
|
||||
* (at the time of 7.2) 0x40 for RTM destruction and 0x48 for RTM reconstruction,
|
||||
* registered in RTM init and run in PostTick.
|
||||
|
@ -70,11 +98,11 @@ public unsafe class GameSizeState : IDisposable
|
|||
* with some other race conditions regarding the fields we modify.
|
||||
* 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"),
|
||||
ICDX11ProcessCommandsDetour
|
||||
ImmediateProcessCommandsDetour
|
||||
);
|
||||
_icdx11ProcessCommandsHook.Enable();
|
||||
_immediateProcessCommandsHook.Enable();
|
||||
|
||||
_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"),
|
||||
|
@ -100,6 +128,38 @@ public unsafe class GameSizeState : IDisposable
|
|||
TaskRenderGraphicsRenderDetour
|
||||
);
|
||||
_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 ID3D11Device funcs based on their vtable indices, to allow mipmap generation,
|
||||
* and to allow changing the sampler state before drawing.
|
||||
*/
|
||||
_createTexture2DHook = Service.GameInteropProvider.HookFromAddress<CreateTexture2D>(
|
||||
((ID3D11Device*) _d3ddev.NativePointer)->lpVtbl[5],
|
||||
CreateTexture2DDetour
|
||||
);
|
||||
_createTexture2DHook.Enable();
|
||||
}
|
||||
|
||||
public bool ConfigDynRezo { get; private set; }
|
||||
|
@ -121,7 +181,7 @@ public unsafe class GameSizeState : IDisposable
|
|||
private delegate void RTMRegenAfterResize();
|
||||
|
||||
// [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)]
|
||||
private delegate void DDX11PostTick(DeviceEx* dev);
|
||||
|
@ -129,14 +189,37 @@ public unsafe class GameSizeState : IDisposable
|
|||
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||
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);
|
||||
|
||||
private delegate void Draw(ID3D11DeviceContext* d3dctx, uint vertexCount, uint startVertexLocation);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_rtmApplyScalingHook.Dispose();
|
||||
_rtmDestroyAfterResizeHook.Dispose();
|
||||
_rtmRegenAfterResizeHook.Dispose();
|
||||
_icdx11ProcessCommandsHook.Dispose();
|
||||
_immediateProcessCommandsHook.Dispose();
|
||||
_ddx11PostTickHook.Dispose();
|
||||
_taskRenderGraphicsRenderHook.Dispose();
|
||||
_prepareTextureHook.Dispose();
|
||||
_immediateBindPSSRVsHook.Dispose();
|
||||
_immediateBindCSSRVsHook.Dispose();
|
||||
_createTexture2DHook.Dispose();
|
||||
lock (_renderLock)
|
||||
{
|
||||
_samplerPoint.Dispose();
|
||||
_samplerMips.Dispose();
|
||||
}
|
||||
_d3ddev.Dispose();
|
||||
Service.Framework.RunOnFrameworkThread(Update);
|
||||
}
|
||||
|
||||
|
@ -157,10 +240,7 @@ public unsafe class GameSizeState : IDisposable
|
|||
|
||||
if (Service.DebugConfig.IsDebug)
|
||||
{
|
||||
_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}
|
||||
_dbg.Append(@$"DR !{gfx->DynamicRezoEnable} +{gfx->DynamicRezoEnableBeyond1} _{gfx->DynamicRezoEnableCutScene} ?{gfx->DynamicRezoEnableUnkx47} ?{gfx->DynamicRezoEnableUnkx48}
|
||||
GR x{gfx->GraphicsRezoScale} ?{gfx->GraphicsRezoUnk1} _{gfx->GraphicsRezoUpscaleType} ?{gfx->GraphicsRezoUnk2}
|
||||
DEV 0x{(long) (nint) dev:X16}
|
||||
GFX 0x{(long) (nint) gfx:X16}
|
||||
|
@ -181,7 +261,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
|||
gfx->DynamicRezoEnable = (byte) (ConfigDynRezo || enabled ? 1 : 0);
|
||||
if (enabled)
|
||||
{
|
||||
gfx->GraphicsRezoUpscaleType = (byte) (cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode : ResolutionScalingMode.Fast);
|
||||
gfx->GraphicsRezoUpscaleType = cfg.Scale <= 1f ? Service.Config._.ResolutionScalingMode.ToXIV() : ResolutionScalingMode.Linear.ToXIV();
|
||||
rtm->DynamicResolutionMinimumHeight = rtm->DynamicResolutionMaximumHeight;
|
||||
}
|
||||
else
|
||||
|
@ -194,9 +274,8 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
|||
var scale = MathF.Max(1f, enabled ? cfg.Scale : 1f);
|
||||
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.
|
||||
var tex = rtm->_.Unk20[0].Value;
|
||||
var tex = rtm->GameplayTextureUnk1;
|
||||
if (tex->AllocatedWidth != widthS || tex->AllocatedHeight != heightS)
|
||||
{
|
||||
if (!unloading)
|
||||
|
@ -274,7 +353,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
|||
|
||||
var gfx = (GraphicsConfigEx*) GraphicsConfig.Instance();
|
||||
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}");
|
||||
_rtmApplyScalingHook.OriginalDisposeSafe(rtm, size, unk1);
|
||||
|
@ -295,7 +374,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
|||
|
||||
Service.PluginLog.Debug($"Destroying RTM resources");
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -314,14 +393,17 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
|||
using var scale = new ScaleState();
|
||||
Service.PluginLog.Debug($"Regenerating RTM resources: {dev->Width} x {dev->Height}");
|
||||
_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)
|
||||
{
|
||||
_icdx11ProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
|
||||
_immediateProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,6 +428,112 @@ 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
|
||||
)
|
||||
{
|
||||
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();
|
||||
|
||||
if (Service.Config._.Game.IsEnabled && Service.Config._.ResolutionScalingMode == ResolutionScalingMode.Point)
|
||||
{
|
||||
stage.SetSampler(0, _samplerPoint);
|
||||
if (!isPS)
|
||||
{
|
||||
_d3dctx.PixelShader.SetSampler(0, _samplerPoint);
|
||||
}
|
||||
}
|
||||
else if (tex->MipLevel != 1)
|
||||
{
|
||||
_d3dctx.PixelShader.SetSampler(0, _samplerMips);
|
||||
if (!isPS)
|
||||
{
|
||||
_d3dctx.PixelShader.SetSampler(0, _samplerMips);
|
||||
}
|
||||
_d3dctx.GenerateMips(CppObject.FromPointer<SDX11.ShaderResourceView>((nint) tex->D3D11ShaderResourceView));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
_0_Idle,
|
||||
|
@ -384,7 +572,6 @@ public unsafe struct GraphicsConfigEx
|
|||
[FieldOffset(0)]
|
||||
public GraphicsConfig _;
|
||||
|
||||
// CRES_CLEAN never
|
||||
public ref byte DynamicRezoEnable => ref _.DynamicRezoEnable;
|
||||
// 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;
|
||||
|
@ -406,6 +593,20 @@ public unsafe struct RenderTargetManagerEx
|
|||
[FieldOffset(0)]
|
||||
public RenderTargetManager _;
|
||||
|
||||
// Totle screen: Gets blitted FROM after actual gameplay texture. Actual purpose unknown.
|
||||
// Im-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)]
|
||||
public uint Resolution_Width;
|
||||
[FieldOffset(0x428 + 0x4)]
|
||||
|
@ -431,3 +632,21 @@ public unsafe struct RenderTargetManagerEx
|
|||
[FieldOffset(0x70C + 4 * 4)]
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using Dalamud.Game.Config;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using ImGuiNET;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
|
@ -154,16 +154,7 @@ Changed via /gres");
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
ImGui.InputFloat("Scale", ref _.Game.Scale, 0.01f, 0.1f, "%.3f", ImGuiInputTextFlags.None);
|
||||
|
||||
if (ImGui.BeginCombo("Graphics Upscaling", _.ResolutionScalingMode.ToHumanNameString()))
|
||||
{
|
||||
|
|
|
@ -8,27 +8,6 @@
|
|||
"resolved": "12.0.0",
|
||||
"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": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.0, )",
|
||||
|
@ -40,12 +19,6 @@
|
|||
"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": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
|
|
|
@ -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.
|
||||
</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>
|
||||
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).
|
||||
|
|
Loading…
Add table
Reference in a new issue