diff --git a/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs b/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
index a8a84b20d..1346de439 100644
--- a/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
+++ b/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
@@ -193,8 +193,8 @@ internal sealed unsafe class Dx11Win32Backend : IWin32Backend
///
public bool IsAttachedToPresentationTarget(nint targetHandle) =>
- this.swapChain.Get() == (void*)targetHandle
- || this.swapChainPossiblyWrapped.Get() == (void*)targetHandle;
+ AreIUnknownEqual(this.swapChain.Get(), (IUnknown*)targetHandle)
+ || AreIUnknownEqual(this.swapChainPossiblyWrapped.Get(), (IUnknown*)targetHandle);
///
public bool IsMainViewportFullScreen()
@@ -204,6 +204,31 @@ internal sealed unsafe class Dx11Win32Backend : IWin32Backend
return fullscreen;
}
+ private static bool AreIUnknownEqual(T1* punk1, T2* punk2)
+ where T1 : unmanaged, IUnknown.Interface
+ where T2 : unmanaged, IUnknown.Interface
+ {
+ // https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
+ // For any given COM object (also known as a COM component), a specific query for the IUnknown interface on any
+ // of the object's interfaces must always return the same pointer value.
+
+ if (punk1 is null || punk2 is null)
+ return false;
+
+ fixed (Guid* iid = &IID.IID_IUnknown)
+ {
+ using var u1 = default(ComPtr);
+ if (punk1->QueryInterface(iid, (void**)u1.GetAddressOf()).FAILED)
+ return false;
+
+ using var u2 = default(ComPtr);
+ if (punk2->QueryInterface(iid, (void**)u2.GetAddressOf()).FAILED)
+ return false;
+
+ return u1.Get() == u2.Get();
+ }
+ }
+
private void ReleaseUnmanagedResources()
{
if (this.device.IsEmpty())
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index a4f61c00e..f7087d3ac 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -780,6 +780,8 @@ internal class InterfaceManager : IInternalDisposableService
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
_ = this.dalamudAtlas.BuildFontsAsync();
+ SwapChainHelper.BusyWaitForGameDeviceSwapChain();
+
try
{
if (Service.Get().WindowIsImmersive)
@@ -797,9 +799,6 @@ internal class InterfaceManager : IInternalDisposableService
0,
this.SetCursorDetour);
- SwapChainHelper.BusyWaitForGameDeviceSwapChain();
- SwapChainHelper.DetectReShade();
-
Log.Verbose("===== S W A P C H A I N =====");
this.resizeBuffersHook = Hook.FromAddress(
(nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers,
diff --git a/Dalamud/Interface/Internal/SwapChainHelper.cs b/Dalamud/Interface/Internal/SwapChainHelper.cs
index 09c1f52fd..e30483983 100644
--- a/Dalamud/Interface/Internal/SwapChainHelper.cs
+++ b/Dalamud/Interface/Internal/SwapChainHelper.cs
@@ -1,27 +1,14 @@
-using System.Diagnostics;
using System.Threading;
-using Dalamud.Game;
-using Dalamud.Utility;
-
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
-using Serilog;
-
using TerraFX.Interop.DirectX;
-using TerraFX.Interop.Windows;
namespace Dalamud.Interface.Internal;
/// Helper for dealing with swap chains.
internal static unsafe class SwapChainHelper
{
- ///
- /// Gets the function pointer for ReShade's DXGISwapChain::on_present.
- /// Source.
- ///
- public static delegate* unmanaged ReshadeOnPresent { get; private set; }
-
/// Gets the game's active instance of IDXGISwapChain that is initialized.
/// Address of the game's instance of IDXGISwapChain, or null if not available (yet.)
public static IDXGISwapChain* GameDeviceSwapChain
@@ -55,139 +42,10 @@ internal static unsafe class SwapChainHelper
}
}
- ///
- public static bool IsGameDeviceSwapChain(nint punk) => IsGameDeviceSwapChain((IUnknown*)punk);
-
- /// Determines if the given instance of IUnknown is the game device's swap chain.
- /// Object to check.
- /// Type of the object to check.
- /// true if the object is the game's swap chain.
- public static bool IsGameDeviceSwapChain(T* punk) where T : unmanaged, IUnknown.Interface
- {
- // https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
- // For any given COM object (also known as a COM component), a specific query for the IUnknown interface on any
- // of the object's interfaces must always return the same pointer value.
-
- var gdsc = GameDeviceSwapChain;
- if (gdsc is null || punk is null)
- return false;
-
- fixed (Guid* iid = &IID.IID_IUnknown)
- {
- using var u1 = default(ComPtr);
- if (gdsc->QueryInterface(iid, (void**)u1.GetAddressOf()).FAILED)
- return false;
-
- using var u2 = default(ComPtr);
- if (punk->QueryInterface(iid, (void**)u2.GetAddressOf()).FAILED)
- return false;
-
- return u1.Get() == u2.Get();
- }
- }
-
/// Wait for the game to have finished initializing the IDXGISwapChain.
public static void BusyWaitForGameDeviceSwapChain()
{
while (GameDeviceSwapChain is null)
Thread.Yield();
}
-
- /// Detects ReShade and populate .
- public static void DetectReShade()
- {
- var modules = Process.GetCurrentProcess().Modules;
- foreach (ProcessModule processModule in modules)
- {
- if (!processModule.FileName.EndsWith("game\\dxgi.dll", StringComparison.InvariantCultureIgnoreCase))
- continue;
-
- try
- {
- var fileInfo = FileVersionInfo.GetVersionInfo(processModule.FileName);
-
- if (fileInfo.FileDescription == null)
- break;
-
- if (!fileInfo.FileDescription.Contains("GShade") && !fileInfo.FileDescription.Contains("ReShade"))
- break;
-
- // warning: these comments may no longer be accurate.
- // reshade master@4232872 RVA
- // var p = processModule.BaseAddress + 0x82C7E0; // DXGISwapChain::Present
- // var p = processModule.BaseAddress + 0x82FAC0; // DXGISwapChain::runtime_present
- // DXGISwapChain::handle_device_loss =>df DXGISwapChain::Present => DXGISwapChain::runtime_present
- // 5.2+ - F6 C2 01 0F 85
- // 6.0+ - F6 C2 01 0F 85 88
-
- var scanner = new SigScanner(processModule);
- var reShadeDxgiPresent = nint.Zero;
-
- if (fileInfo.FileVersion?.StartsWith("6.") == true)
- {
- // No Addon
- if (scanner.TryScanText("F6 C2 01 0F 85 A8", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for ReShade 6 No-Addon");
- }
-
- // Addon
- else if (scanner.TryScanText("F6 C2 01 0F 85 88", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for ReShade 6 Addon");
- }
-
- // Fallback
- else
- {
- Log.Error("Failed to get ReShade 6 DXGISwapChain::on_present offset!");
- }
- }
-
- // Looks like this sig only works for GShade 4
- if (reShadeDxgiPresent == nint.Zero && fileInfo.FileDescription?.Contains("GShade 4.") == true)
- {
- if (scanner.TryScanText("E8 ?? ?? ?? ?? 45 0F B6 5E ??", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for GShade 4");
- }
- else
- {
- Log.Error("Failed to find GShade 4 DXGISwapChain::on_present offset!");
- }
- }
-
- if (reShadeDxgiPresent == nint.Zero)
- {
- if (scanner.TryScanText("F6 C2 01 0F 85", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for ReShade with fallback 5.X sig");
- }
- else
- {
- Log.Error("Failed to find ReShade DXGISwapChain::on_present offset with fallback sig!");
- }
- }
-
- Log.Information(
- "ReShade DLL: {FileName} ({Info} - {Version}) with DXGISwapChain::on_present at {Address}",
- processModule.FileName,
- fileInfo.FileDescription ?? "Unknown",
- fileInfo.FileVersion ?? "Unknown",
- Util.DescribeAddress(reShadeDxgiPresent));
-
- if (reShadeDxgiPresent != nint.Zero)
- {
- ReshadeOnPresent = (delegate* unmanaged)reShadeDxgiPresent;
- }
-
- break;
- }
- catch (Exception e)
- {
- Log.Error(e, "Failed to get ReShade version info");
- break;
- }
- }
- }
}