dirty rnd: MiniShader utility, inspired by Pictomancy
This commit is contained in:
parent
51f2cd8348
commit
8e5ad95b8d
10 changed files with 593 additions and 13 deletions
|
@ -115,7 +115,7 @@ public static class MinSizeModeExt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ResolutionScalingMode
|
public enum ResolutionScalingMode : byte
|
||||||
{
|
{
|
||||||
Fast = 0,
|
Fast = 0,
|
||||||
FSR = 1,
|
FSR = 1,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
<RootNamespace>CustomResolution</RootNamespace>
|
<RootNamespace>CustomResolution</RootNamespace>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@ -28,9 +29,17 @@
|
||||||
<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" />
|
||||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.22621.2" />
|
|
||||||
<Reference Include="FFXIVClientStructs">
|
<Reference Include="FFXIVClientStructs">
|
||||||
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private>false</Private>
|
||||||
|
@ -71,6 +80,10 @@
|
||||||
<HintPath>$(DalamudLibPath)SharpDX.Mathematics.dll</HintPath>
|
<HintPath>$(DalamudLibPath)SharpDX.Mathematics.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private>false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="TerraFX.Interop.Windows">
|
||||||
|
<HintPath>$(DalamudLibPath)TerraFX.Interop.Windows.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" />
|
<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" />
|
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />
|
||||||
|
|
|
@ -6,6 +6,7 @@ using FloppyUtils;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
using static TerraFX.Interop.Windows.Windows;
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
|
@ -421,13 +422,14 @@ public unsafe struct DeviceEx
|
||||||
public ref uint NewHeight => ref _.NewHeight;
|
public ref uint NewHeight => ref _.NewHeight;
|
||||||
|
|
||||||
// C# doesn't let us use ptr types as generic arguments, oh well.
|
// C# doesn't let us use ptr types as generic arguments, oh well.
|
||||||
|
public readonly ID3D11DeviceContext* D3D11DeviceContext => (ID3D11DeviceContext*) _.D3D11DeviceContext;
|
||||||
public readonly ImmediateContextEx* ImmediateContext => (ImmediateContextEx*) _.ImmediateContext;
|
public readonly ImmediateContextEx* ImmediateContext => (ImmediateContextEx*) _.ImmediateContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImmediateContextEx in FFXIVClientStructs is a bare minimum.
|
// ImmediateContextEx in FFXIVClientStructs is a bare minimum.
|
||||||
// https://github.com/aers/FFXIVClientStructs/blob/ef5e9a5997671fb2c9a72cb9d57d841855f62085/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
|
// https://github.com/aers/FFXIVClientStructs/blob/ef5e9a5997671fb2c9a72cb9d57d841855f62085/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
public struct ImmediateContextEx
|
public unsafe struct ImmediateContextEx
|
||||||
{
|
{
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public ImmediateContext _;
|
public ImmediateContext _;
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
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;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
using FloppyUtils;
|
using FloppyUtils;
|
||||||
|
using SharpDX;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
|
using Device = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device;
|
||||||
|
|
||||||
namespace CustomResolution;
|
namespace CustomResolution;
|
||||||
|
|
||||||
|
@ -20,14 +25,26 @@ public unsafe class GameSizeState : IDisposable
|
||||||
private Hook<ICDX11ProcessCommands> _icdx11ProcessCommandsHook;
|
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<ImmediatePreparePS> _immediatePreparePSHook;
|
||||||
|
private Hook<ImmediatePrepareCS> _immediatePrepareCSHook;
|
||||||
|
private Hook<CreateTexture2D> _createTexture2DHook;
|
||||||
|
private Hook<Draw> _drawHook;
|
||||||
|
|
||||||
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 _gameplayDrawCount = 0;
|
||||||
|
private bool _drawIsGameplay = false;
|
||||||
|
|
||||||
|
private BlitShader _blitShader;
|
||||||
|
|
||||||
public GameSizeState()
|
public GameSizeState()
|
||||||
{
|
{
|
||||||
|
_blitShader = new();
|
||||||
|
|
||||||
/* 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??
|
||||||
*/
|
*/
|
||||||
|
@ -100,6 +117,45 @@ public unsafe class GameSizeState : IDisposable
|
||||||
TaskRenderGraphicsRenderDetour
|
TaskRenderGraphicsRenderDetour
|
||||||
);
|
);
|
||||||
_taskRenderGraphicsRenderHook.Enable();
|
_taskRenderGraphicsRenderHook.Enable();
|
||||||
|
|
||||||
|
/* Texture preparation used in a lot of places, but most notable for
|
||||||
|
* writing to the mip level count of the gameplay texture and also updating the D3D11 ptrs.
|
||||||
|
*/
|
||||||
|
_prepareTextureHook = Service.GameInteropProvider.HookFromAddress<PrepareTexture>(
|
||||||
|
Service.SigScanner.ScanText("40 53 48 83 EC 20 48 8B 02 48 8B D9 48 89 41 38 48 8B 02 48 89 41 40"),
|
||||||
|
PrepareTextureDetour
|
||||||
|
);
|
||||||
|
_prepareTextureHook.Enable();
|
||||||
|
|
||||||
|
/* The shader resource view of GameplayTexture gets read by these two.
|
||||||
|
*/
|
||||||
|
_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"),
|
||||||
|
ImmediatePreparePSDetour
|
||||||
|
);
|
||||||
|
_immediatePreparePSHook.Enable();
|
||||||
|
_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"),
|
||||||
|
ImmediatePrepareCSDetour
|
||||||
|
);
|
||||||
|
_immediatePrepareCSHook.Enable();
|
||||||
|
|
||||||
|
/* Hook ID3D11Device funcs based on their vtable indices,
|
||||||
|
* 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>(
|
||||||
|
d3ddev->lpVtbl[5],
|
||||||
|
CreateTexture2DDetour
|
||||||
|
);
|
||||||
|
_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; }
|
||||||
|
@ -129,6 +185,19 @@ public unsafe class GameSizeState : IDisposable
|
||||||
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
private delegate void TaskRenderGraphicsRender();
|
private delegate void TaskRenderGraphicsRender();
|
||||||
|
|
||||||
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
|
private delegate ulong PrepareTexture(Texture* tex, uint* size, byte mips, uint format);
|
||||||
|
|
||||||
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
|
private delegate void ImmediatePreparePS(ImmediateContextEx* im, PixelShader* ps, ImmediateBlitResource* data);
|
||||||
|
|
||||||
|
// [UnmanagedFunctionPointer(CallingConvention.FastCall)]
|
||||||
|
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 void Draw(ID3D11DeviceContext* d3dctx, uint vertexCount, uint startVertexPosition);
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_rtmApplyScalingHook.Dispose();
|
_rtmApplyScalingHook.Dispose();
|
||||||
|
@ -137,6 +206,12 @@ public unsafe class GameSizeState : IDisposable
|
||||||
_icdx11ProcessCommandsHook.Dispose();
|
_icdx11ProcessCommandsHook.Dispose();
|
||||||
_ddx11PostTickHook.Dispose();
|
_ddx11PostTickHook.Dispose();
|
||||||
_taskRenderGraphicsRenderHook.Dispose();
|
_taskRenderGraphicsRenderHook.Dispose();
|
||||||
|
_prepareTextureHook.Dispose();
|
||||||
|
_immediatePreparePSHook.Dispose();
|
||||||
|
_immediatePrepareCSHook.Dispose();
|
||||||
|
_createTexture2DHook.Dispose();
|
||||||
|
_drawHook.Dispose();
|
||||||
|
_blitShader.Dispose();
|
||||||
Service.Framework.RunOnFrameworkThread(Update);
|
Service.Framework.RunOnFrameworkThread(Update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +271,7 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
||||||
|
|
||||||
// TODO: Figure out a more consistent way to get to the currently allocated size.
|
// TODO: Figure out a more consistent way to get to the currently allocated size.
|
||||||
// Check if the backing RTs are the expected size.
|
// Check if the backing RTs are the expected size.
|
||||||
var tex = rtm->_.Unk20[0].Value;
|
var tex = rtm->GameplayTexture;
|
||||||
if (tex->AllocatedWidth != widthS || tex->AllocatedHeight != heightS)
|
if (tex->AllocatedWidth != widthS || tex->AllocatedHeight != heightS)
|
||||||
{
|
{
|
||||||
if (!unloading)
|
if (!unloading)
|
||||||
|
@ -295,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: 0x{(long) (nint) rtm->_.Unk20[0].Value->D3D11Texture2D:X16}");
|
Service.PluginLog.Debug($"After: {rv} 0x{(long) (nint) rtm->GameplayTexture->D3D11Texture2D:X16}");
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,11 +389,13 @@ RR {dev->RequestRender} 0x{(long) dev->ImmediateContext->IfNonZeroSkipPostTickPr
|
||||||
using var scale = new ScaleState();
|
using var scale = new ScaleState();
|
||||||
Service.PluginLog.Debug($"Regenerating RTM resources: {dev->Width} x {dev->Height}");
|
Service.PluginLog.Debug($"Regenerating RTM resources: {dev->Width} x {dev->Height}");
|
||||||
_rtmRegenAfterResizeHook.OriginalDisposeSafe();
|
_rtmRegenAfterResizeHook.OriginalDisposeSafe();
|
||||||
Service.PluginLog.Debug($"After: 0x{(long) (nint) rtm->_.Unk20[0].Value->D3D11Texture2D:X16}");
|
Service.PluginLog.Debug($"After: 0x{(long) (nint) rtm->GameplayTexture->D3D11Texture2D:X16}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ICDX11ProcessCommandsDetour(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count)
|
private void ICDX11ProcessCommandsDetour(ImmediateContext* ctx, RenderCommandBufferGroup* cmds, uint count)
|
||||||
{
|
{
|
||||||
|
_gameplayDrawCount = 0;
|
||||||
|
|
||||||
lock (_renderLock)
|
lock (_renderLock)
|
||||||
{
|
{
|
||||||
_icdx11ProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
|
_icdx11ProcessCommandsHook.OriginalDisposeSafe(ctx, cmds, count);
|
||||||
|
@ -346,6 +423,122 @@ 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->GameplayTexture)
|
||||||
|
{
|
||||||
|
return _prepareTextureHook.OriginalDisposeSafe(tex, size, mips, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var cfg = ref Service.Config._.Game;
|
||||||
|
|
||||||
|
// TODO: MathF.Log
|
||||||
|
mips = 1;
|
||||||
|
if (!Service.Plugin.Unloading && cfg.IsEnabled)
|
||||||
|
{
|
||||||
|
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}");
|
||||||
|
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 ImmediatePreparePSDetour(ImmediateContextEx* im, PixelShader* ps, ImmediateBlitResource* data)
|
||||||
|
{
|
||||||
|
//Service.PluginLog.Debug($"ImmediatePreparePS 0x{(long) im:X16} 0x{(long) ps:X16} 0x{(long) unk3:X16}");
|
||||||
|
|
||||||
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
|
var tex = rtm->GameplayTexture;
|
||||||
|
if (ps->Shader.SamplerCount >= 1 && data[0].Texture == tex)
|
||||||
|
{
|
||||||
|
_gameplayDrawCount++;
|
||||||
|
// 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}");
|
||||||
|
|
||||||
|
var gfx = (GraphicsConfigEx*) GraphicsConfig.Instance();
|
||||||
|
if ((gfx->GraphicsRezoUpscaleType == (byte) ResolutionScalingMode.FSR && gfx->GraphicsRezoScale < 1f) ||
|
||||||
|
gfx->GraphicsRezoUpscaleType == (byte) ResolutionScalingMode.DLSS)
|
||||||
|
{
|
||||||
|
_drawIsGameplay = _gameplayDrawCount == 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_drawIsGameplay = _gameplayDrawCount == 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_immediatePreparePSHook.OriginalDisposeSafe(im, ps, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ImmediatePrepareCSDetour(ImmediateContextEx* im, Shader* cs, ImmediateBlitResource* 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)
|
||||||
|
{
|
||||||
|
var rtm = (RenderTargetManagerEx*) RenderTargetManager.Instance();
|
||||||
|
if (tex != &rtm->GameplayTexture->D3D11Texture2D)
|
||||||
|
{
|
||||||
|
return _createTexture2DHook.OriginalDisposeSafe(d3ddev, desc, initialData, tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtm->GameplayTexture->MipLevel != 1)
|
||||||
|
{
|
||||||
|
desc->MiscFlags |= (uint) D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_GENERATE_MIPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
@ -406,6 +599,18 @@ public unsafe struct RenderTargetManagerEx
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public RenderTargetManager _;
|
public RenderTargetManager _;
|
||||||
|
|
||||||
|
// Gets blitted FROM after actual gameplay texture. Actual purpose unknown.
|
||||||
|
[FieldOffset(0x68)]
|
||||||
|
public Texture* GameplayTextureUnk1;
|
||||||
|
|
||||||
|
// Gets blitted TO after actual gameplay texture, using Unk1. Actual purpose unknown.
|
||||||
|
[FieldOffset(0x100)]
|
||||||
|
public Texture* GameplayTextureUnk2;
|
||||||
|
|
||||||
|
// Gets blitted to backbuffer.
|
||||||
|
[FieldOffset(0x258)]
|
||||||
|
public Texture* GameplayTexture;
|
||||||
|
|
||||||
[FieldOffset(0x428)]
|
[FieldOffset(0x428)]
|
||||||
public uint Resolution_Width;
|
public uint Resolution_Width;
|
||||||
[FieldOffset(0x428 + 0x4)]
|
[FieldOffset(0x428 + 0x4)]
|
||||||
|
@ -431,3 +636,21 @@ public unsafe struct RenderTargetManagerEx
|
||||||
[FieldOffset(0x70C + 4 * 4)]
|
[FieldOffset(0x70C + 4 * 4)]
|
||||||
public float GraphicsRezoScaleUnk2;
|
public float GraphicsRezoScaleUnk2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
||||||
|
public unsafe struct ImmediateBlitResource
|
||||||
|
{
|
||||||
|
// 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 Unk1;
|
||||||
|
|
||||||
|
[FieldOffset(0x8)]
|
||||||
|
public Texture* Texture;
|
||||||
|
|
||||||
|
[FieldOffset(0x10)]
|
||||||
|
public uint Unk2;
|
||||||
|
|
||||||
|
[FieldOffset(0x14)]
|
||||||
|
public uint Unk3;
|
||||||
|
}
|
||||||
|
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,12 +40,6 @@
|
||||||
"SharpDX.DXGI": "4.2.0"
|
"SharpDX.DXGI": "4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TerraFX.Interop.Windows": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[10.0.22621.2, )",
|
|
||||||
"resolved": "10.0.22621.2",
|
|
||||||
"contentHash": "lORoYCoURS33Vi7PDvubRugLF2+l5v3rX2oVEqNhpBLjs3aZpqapRvTHPKVwsC+dGrsGuqJ/3yXuxVUb0vl3vg=="
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Platforms": {
|
"Microsoft.NETCore.Platforms": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.1.0",
|
"resolved": "1.1.0",
|
||||||
|
|
Loading…
Add table
Reference in a new issue