using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Utility; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; namespace Dalamud.Interface.Internal; /// /// This class manages interaction with the ImGui interface. /// internal unsafe partial class InterfaceManager { // NOTE: Do not use HRESULT as return value type. It appears that .NET marshaller thinks HRESULT needs to be still // treated as a type that does not fit into RAX. /// Delegate for DXGISwapChain::on_present(UINT flags, const DXGI_PRESENT_PARAMETERS *params) in /// dxgi_swapchain.cpp. /// Pointer to an instance of DXGISwapChain, which happens to be an /// . /// An integer value that contains swap-chain presentation options. These options are defined by /// the DXGI_PRESENT constants. /// Optional; DXGI present parameters. [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate void ReShadeDxgiSwapChainPresentDelegate( ReShadeDxgiSwapChain* swapChain, uint flags, DXGI_PRESENT_PARAMETERS* presentParams); /// Delegate for . /// Microsoft /// Learn. /// Pointer to an instance of . /// An integer that specifies how to synchronize presentation of a frame with the /// vertical blank. /// An integer value that contains swap-chain presentation options. These options are defined by /// the DXGI_PRESENT constants. /// A representing the result of the operation. [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate int DxgiSwapChainPresentDelegate(IDXGISwapChain* swapChain, uint syncInterval, uint flags); /// Detour function for . /// /// Microsoft Learn. /// Pointer to an instance of . /// The number of buffers in the swap chain (including all back and front buffers). /// This number can be different from the number of buffers with which you created the swap chain. This number /// can't be greater than . Set this number to zero to preserve the /// existing number of buffers in the swap chain. You can't specify less than two buffers for the flip presentation /// model. /// The new width of the back buffer. If you specify zero, DXGI will use the width of the client /// area of the target window. You can't specify the width as zero if you called the /// method to create the swap chain for a composition /// surface. /// The new height of the back buffer. If you specify zero, DXGI will use the height of the /// client area of the target window. You can't specify the height as zero if you called the /// method to create the swap chain for a composition /// surface. /// A DXGI_FORMAT-typed value for the new format of the back buffer. Set this value to /// to preserve the existing format of the back buffer. The flip /// presentation model supports a more restricted set of formats than the bit-block transfer (bitblt) model. /// A combination of -typed values that are combined /// by using a bitwise OR operation. The resulting value specifies options for swap-chain behavior. /// A representing the result of the operation. [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate int ResizeBuffersDelegate( IDXGISwapChain* swapChain, uint bufferCount, uint width, uint height, DXGI_FORMAT newFormat, uint swapChainFlags); private void ReShadeDxgiSwapChainOnPresentDetour( ReShadeDxgiSwapChain* swapChain, uint flags, DXGI_PRESENT_PARAMETERS* presentParams) { Debug.Assert( this.reShadeDxgiSwapChainPresentHook is not null, "this.reShadeDxgiSwapChainPresentHook is not null"); if (this.RenderDalamudCheckAndInitialize(swapChain->AsIDxgiSwapChain(), flags) is { } activeScene) this.RenderDalamudDraw(activeScene); this.reShadeDxgiSwapChainPresentHook!.Original(swapChain, flags, presentParams); // Upstream call to system IDXGISwapChain::Present will be called by ReShade. } private int DxgiSwapChainPresentDetour(IDXGISwapChain* swapChain, uint syncInterval, uint flags) { Debug.Assert(this.dxgiSwapChainPresentHook is not null, "this.dxgiSwapChainPresentHook is not null"); if (this.RenderDalamudCheckAndInitialize(swapChain, flags) is { } activeScene) this.RenderDalamudDraw(activeScene); return this.dxgiSwapChainPresentHook!.Original(swapChain, syncInterval, flags); } private int AsHookDxgiSwapChainResizeBuffersDetour( IDXGISwapChain* swapChain, uint bufferCount, uint width, uint height, DXGI_FORMAT newFormat, uint swapChainFlags) { if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) return this.dxgiSwapChainResizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); #if DEBUG Log.Verbose( $"Calling resizebuffers swap@{(nint)swapChain:X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}"); #endif this.ResizeBuffers?.InvokeSafely(); this.scene?.OnPreResize(); var ret = this.dxgiSwapChainResizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); if (ret == DXGI.DXGI_ERROR_INVALID_CALL) Log.Error("invalid call to resizeBuffers"); this.scene?.OnPostResize((int)width, (int)height); return ret; } /// Represents DXGISwapChain in ReShade. [StructLayout(LayoutKind.Sequential)] private struct ReShadeDxgiSwapChain { // DXGISwapChain only implements IDXGISwapChain4. The only vtable should be that. [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDXGISwapChain* AsIDxgiSwapChain() => (IDXGISwapChain*)Unsafe.AsPointer(ref this); } }