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);
}
}