diff --git a/Dalamud/Interface/Internal/InterfaceManager.AsHook.cs b/Dalamud/Interface/Internal/InterfaceManager.AsHook.cs new file mode 100644 index 000000000..b2afb970f --- /dev/null +++ b/Dalamud/Interface/Internal/InterfaceManager.AsHook.cs @@ -0,0 +1,75 @@ +using System.Diagnostics; + +using Dalamud.Utility; + +namespace Dalamud.Interface.Internal; + +/// +/// This class manages interaction with the ImGui interface. +/// +internal partial class InterfaceManager +{ + private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags) + { + if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) + return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags); + + Debug.Assert(this.dxgiPresentHook is not null, "How did PresentDetour get called when presentHook is null?"); + Debug.Assert(this.dalamudAtlas is not null, "dalamudAtlas should have been set already"); + + if (this.scene == null) + this.InitScene(swapChain); + + Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception."); + + if (!this.dalamudAtlas!.HasBuiltAtlas) + { + if (this.dalamudAtlas.BuildTask.Exception != null) + { + // TODO: Can we do something more user-friendly here? Unload instead? + Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts"); + Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); + } + + return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags); + } + + this.CumulativePresentCalls++; + this.IsMainThreadInPresent = true; + + while (this.runBeforeImGuiRender.TryDequeue(out var action)) + action.InvokeSafely(); + + RenderImGui(this.scene!); + this.PostImGuiRender(); + this.IsMainThreadInPresent = false; + + return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags); + } + + private IntPtr AsHookResizeBuffersDetour( + IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags) + { + if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) + return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); + +#if DEBUG + Log.Verbose( + $"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}"); +#endif + + this.ResizeBuffers?.InvokeSafely(); + + this.scene?.OnPreResize(); + + var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); + if (ret.ToInt64() == 0x887A0001) + { + Log.Error("invalid call to resizeBuffers"); + } + + this.scene?.OnPostResize((int)width, (int)height); + + return ret; + } +} diff --git a/Dalamud/Interface/Internal/InterfaceManager.AsReShadeAddon.cs b/Dalamud/Interface/Internal/InterfaceManager.AsReShadeAddon.cs new file mode 100644 index 000000000..0f1eeb707 --- /dev/null +++ b/Dalamud/Interface/Internal/InterfaceManager.AsReShadeAddon.cs @@ -0,0 +1,86 @@ +using System.Diagnostics; + +using Dalamud.Utility; + +using TerraFX.Interop.DirectX; + +namespace Dalamud.Interface.Internal; + +/// +/// This class manages interaction with the ImGui interface. +/// +internal partial class InterfaceManager +{ + private unsafe void ReShadeAddonInterfaceOnDestroySwapChain(ref ReShadeAddonInterface.ApiObject swapchain) + { + var swapChain = swapchain.GetNative(); + if (this.scene?.SwapChain.NativePointer != (nint)swapChain) + return; + + this.scene?.OnPreResize(); + } + + private unsafe void ReShadeAddonInterfaceOnInitSwapChain(ref ReShadeAddonInterface.ApiObject swapchain) + { + var swapChain = swapchain.GetNative(); + if (this.scene?.SwapChain.NativePointer != (nint)swapChain) + return; + + DXGI_SWAP_CHAIN_DESC desc; + if (swapChain->GetDesc(&desc).FAILED) + return; + + this.scene?.OnPostResize((int)desc.BufferDesc.Width, (int)desc.BufferDesc.Height); + } + + private void ReShadeAddonInterfaceOnReShadeOverlay(ref ReShadeAddonInterface.ApiObject runtime) + { + var swapChain = runtime.GetNative(); + + if (this.scene == null) + this.InitScene(swapChain); + + if (this.scene?.SwapChain.NativePointer != swapChain) + return; + + Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null"); + + if (!this.dalamudAtlas!.HasBuiltAtlas) + { + if (this.dalamudAtlas.BuildTask.Exception != null) + { + // TODO: Can we do something more user-friendly here? Unload instead? + Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts"); + Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); + } + + return; + } + + this.CumulativePresentCalls++; + this.IsMainThreadInPresent = true; + + while (this.runBeforeImGuiRender.TryDequeue(out var action)) + action.InvokeSafely(); + + RenderImGui(this.scene!); + this.PostImGuiRender(); + this.IsMainThreadInPresent = false; + } + + private nint AsReShadeAddonResizeBuffersDetour( + nint swapChain, + uint bufferCount, + uint width, + uint height, + uint newFormat, + uint swapChainFlags) + { + // Hooked vtbl instead of registering ReShade event. This check is correct. + if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) + return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); + + this.ResizeBuffers?.InvokeSafely(); + return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); + } +} diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index cbbf63075..10d508d99 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -13,6 +13,7 @@ using Dalamud.Game; using Dalamud.Game.ClientState.GamePad; using Dalamud.Game.ClientState.Keys; using Dalamud.Hooking; +using Dalamud.Hooking.Internal; using Dalamud.Hooking.WndProcHook; using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Internal.ManagedAsserts; @@ -29,10 +30,11 @@ using ImGuiNET; using ImGuiScene; +using JetBrains.Annotations; + using PInvoke; -using SharpDX; -using SharpDX.DXGI; +using TerraFX.Interop.Windows; // general dev notes, here because it's easiest @@ -52,7 +54,7 @@ namespace Dalamud.Interface.Internal; /// This class manages interaction with the ImGui interface. /// [ServiceManager.EarlyLoadedService] -internal class InterfaceManager : IInternalDisposableService +internal partial class InterfaceManager : IInternalDisposableService { /// /// The default font size, in points. @@ -75,6 +77,11 @@ internal class InterfaceManager : IInternalDisposableService [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); + // ReShadeAddonInterface requires hooks to be alive to unregister itself. + [ServiceManager.ServiceDependency] + [UsedImplicitly] + private readonly HookManager hookManager = Service.Get(); + private readonly ConcurrentQueue runBeforeImGuiRender = new(); private readonly ConcurrentQueue runAfterImGuiRender = new(); @@ -82,8 +89,8 @@ internal class InterfaceManager : IInternalDisposableService private Hook? setCursorHook; private Hook? dxgiPresentHook; - private Hook? reshadeOnPresentHook; private Hook? resizeBuffersHook; + private ReShadeAddonInterface? reShadeAddonInterface; private IFontAtlas? dalamudAtlas; private ILockedImFont? defaultFontResourceLock; @@ -101,9 +108,6 @@ internal class InterfaceManager : IInternalDisposableService [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr DxgiPresentDelegate(IntPtr swapChain, uint syncInterval, uint presentFlags); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ReshadeOnPresentDelegate(nint swapChain, uint flags, nint presentParams); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr ResizeBuffersDelegate(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags); @@ -299,8 +303,8 @@ internal class InterfaceManager : IInternalDisposableService this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc; Interlocked.Exchange(ref this.setCursorHook, null)?.Dispose(); Interlocked.Exchange(ref this.dxgiPresentHook, null)?.Dispose(); - Interlocked.Exchange(ref this.reshadeOnPresentHook, null)?.Dispose(); Interlocked.Exchange(ref this.resizeBuffersHook, null)?.Dispose(); + Interlocked.Exchange(ref this.reShadeAddonInterface, null)?.Dispose(); } } @@ -431,11 +435,11 @@ internal class InterfaceManager : IInternalDisposableService try { var dxgiDev = this.Device.QueryInterfaceOrNull(); - var dxgiAdapter = dxgiDev?.Adapter.QueryInterfaceOrNull(); + var dxgiAdapter = dxgiDev?.Adapter.QueryInterfaceOrNull(); if (dxgiAdapter == null) return null; - var memInfo = dxgiAdapter.QueryVideoMemoryInfo(0, MemorySegmentGroup.Local); + var memInfo = dxgiAdapter.QueryVideoMemoryInfo(0, SharpDX.DXGI.MemorySegmentGroup.Local); return (memInfo.CurrentUsage, memInfo.CurrentReservation); } catch @@ -464,11 +468,11 @@ internal class InterfaceManager : IInternalDisposableService if (this.GameWindowHandle == 0) throw new InvalidOperationException("Game window is not yet ready."); var value = enabled ? 1 : 0; - ((Result)NativeFunctions.DwmSetWindowAttribute( + ((HRESULT)NativeFunctions.DwmSetWindowAttribute( this.GameWindowHandle, NativeFunctions.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref value, - sizeof(int))).CheckError(); + sizeof(int))).ThrowOnError(); } private static InterfaceManager WhenFontsReady() @@ -632,86 +636,6 @@ internal class InterfaceManager : IInternalDisposableService args.SuppressWithValue(r.Value); } - private void ReshadeOnPresentDetour(nint swapChain, uint flags, nint presentParams) - { - if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) - { - this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams); - return; - } - - Debug.Assert(this.reshadeOnPresentHook is not null, "this.reshadeOnPresentHook is not null"); - Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null"); - - if (this.scene == null) - this.InitScene(swapChain); - - Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception."); - - if (!this.dalamudAtlas!.HasBuiltAtlas) - { - if (this.dalamudAtlas.BuildTask.Exception != null) - { - // TODO: Can we do something more user-friendly here? Unload instead? - Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts"); - Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); - } - - this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams); - return; - } - - this.CumulativePresentCalls++; - this.IsMainThreadInPresent = true; - - while (this.runBeforeImGuiRender.TryDequeue(out var action)) - action.InvokeSafely(); - - this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams); - - RenderImGui(this.scene!); - this.PostImGuiRender(); - this.IsMainThreadInPresent = false; - } - - private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags) - { - if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) - return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags); - - Debug.Assert(this.dxgiPresentHook is not null, "How did PresentDetour get called when presentHook is null?"); - Debug.Assert(this.dalamudAtlas is not null, "dalamudAtlas should have been set already"); - - if (this.scene == null) - this.InitScene(swapChain); - - Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception."); - - if (!this.dalamudAtlas!.HasBuiltAtlas) - { - if (this.dalamudAtlas.BuildTask.Exception != null) - { - // TODO: Can we do something more user-friendly here? Unload instead? - Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts"); - Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); - } - - return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags); - } - - this.CumulativePresentCalls++; - this.IsMainThreadInPresent = true; - - while (this.runBeforeImGuiRender.TryDequeue(out var action)) - action.InvokeSafely(); - - RenderImGui(this.scene!); - this.PostImGuiRender(); - this.IsMainThreadInPresent = false; - - return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags); - } - private void PostImGuiRender() { while (this.runAfterImGuiRender.TryDequeue(out var action)) @@ -799,10 +723,7 @@ internal class InterfaceManager : IInternalDisposableService () => { // Update the ImGui default font. - unsafe - { - ImGui.GetIO().NativePtr->FontDefault = fontLocked.ImFont; - } + ImGui.GetIO().NativePtr->FontDefault = fontLocked.ImFont; // Update the reference to the resources of the default font. this.defaultFontResourceLock?.Dispose(); @@ -818,7 +739,6 @@ internal class InterfaceManager : IInternalDisposableService _ = this.dalamudAtlas.BuildFontsAsync(); SwapChainHelper.BusyWaitForGameDeviceSwapChain(); - SwapChainHelper.DetectReShade(); try { @@ -839,52 +759,36 @@ internal class InterfaceManager : IInternalDisposableService this.SetCursorDetour); Log.Verbose("===== S W A P C H A I N ====="); - this.resizeBuffersHook = Hook.FromAddress( - (nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers, - this.ResizeBuffersDetour); - Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}"); - - if (SwapChainHelper.ReshadeOnPresent is null) + if (ReShadeAddonInterface.TryRegisterAddon(out this.reShadeAddonInterface)) { - var addr = (nint)SwapChainHelper.GameDeviceSwapChainVtbl->Present; - this.dxgiPresentHook = Hook.FromAddress(addr, this.PresentDetour); - Log.Verbose($"ReShade::DXGISwapChain::on_present address {Util.DescribeAddress(addr)}"); + this.resizeBuffersHook = Hook.FromAddress( + (nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers, + this.AsReShadeAddonResizeBuffersDetour); + Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}"); + + Log.Verbose( + "Registered as a ReShade({name}: 0x{addr:X}) addon.", + ReShadeAddonInterface.ReShadeModule!.FileName, + ReShadeAddonInterface.ReShadeModule!.BaseAddress); + this.reShadeAddonInterface.InitSwapChain += this.ReShadeAddonInterfaceOnInitSwapChain; + this.reShadeAddonInterface.DestroySwapChain += this.ReShadeAddonInterfaceOnDestroySwapChain; + this.reShadeAddonInterface.ReShadeOverlay += this.ReShadeAddonInterfaceOnReShadeOverlay; } else { - var addr = (nint)SwapChainHelper.ReshadeOnPresent; - this.reshadeOnPresentHook = Hook.FromAddress(addr, this.ReshadeOnPresentDetour); + this.resizeBuffersHook = Hook.FromAddress( + (nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers, + this.AsHookResizeBuffersDetour); + Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}"); + + var addr = (nint)SwapChainHelper.GameDeviceSwapChainVtbl->Present; + this.dxgiPresentHook = Hook.FromAddress(addr, this.PresentDetour); Log.Verbose($"IDXGISwapChain::Present address {Util.DescribeAddress(addr)}"); } this.setCursorHook.Enable(); this.dxgiPresentHook?.Enable(); - this.reshadeOnPresentHook?.Enable(); - this.resizeBuffersHook.Enable(); - } - - private IntPtr ResizeBuffersDetour(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags) - { - if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain)) - return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); - -#if DEBUG - Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}"); -#endif - - this.ResizeBuffers?.InvokeSafely(); - - this.scene?.OnPreResize(); - - var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags); - if (ret.ToInt64() == 0x887A0001) - { - Log.Error("invalid call to resizeBuffers"); - } - - this.scene?.OnPostResize((int)width, (int)height); - - return ret; + this.resizeBuffersHook?.Enable(); } private IntPtr SetCursorDetour(IntPtr hCursor) diff --git a/Dalamud/Interface/Internal/ReShadeAddonInterface.AddonEvent.cs b/Dalamud/Interface/Internal/ReShadeAddonInterface.AddonEvent.cs new file mode 100644 index 000000000..23f01875d --- /dev/null +++ b/Dalamud/Interface/Internal/ReShadeAddonInterface.AddonEvent.cs @@ -0,0 +1,1706 @@ +namespace Dalamud.Interface.Internal; + +/// ReShade interface. +internal sealed partial class ReShadeAddonInterface +{ + /// Supported events emitted by ReShade. + private enum AddonEvent : uint + { +#pragma warning disable + + /// + /// Called after successful device creation, from: + /// + /// IDirect3D9::CreateDevice + /// IDirect3D9Ex::CreateDeviceEx + /// IDirect3DDevice9::Reset + /// IDirect3DDevice9Ex::ResetEx + /// D3D10CreateDevice + /// D3D10CreateDevice1 + /// D3D10CreateDeviceAndSwapChain + /// D3D10CreateDeviceAndSwapChain1 + /// D3D11CreateDevice + /// D3D11CreateDeviceAndSwapChain + /// D3D12CreateDevice + /// glMakeCurrent + /// vkCreateDevice + /// + /// Callback function signature: void (api::device *device) + /// + InitDevice, + + /// + /// Called on device destruction, before: + /// + /// IDirect3DDevice9::Reset + /// IDirect3DDevice9Ex::ResetEx + /// IDirect3DDevice9::Release + /// ID3D10Device::Release + /// ID3D11Device::Release + /// ID3D12Device::Release + /// wglDeleteContext + /// vkDestroyDevice + /// + /// Callback function signature: void (api::device *device) + /// + DestroyDevice, + + /// + /// Called after successful command list creation, from: + /// + /// ID3D11Device::CreateDeferredContext + /// ID3D11Device1::CreateDeferredContext1 + /// ID3D11Device2::CreateDeferredContext2 + /// ID3D11Device3::CreateDeferredContext3 + /// ID3D12Device::CreateCommandList + /// ID3D12Device4::CreateCommandList1 + /// vkAllocateCommandBuffers + /// + /// Callback function signature: void (api::command_list *cmd_list) + /// + /// + /// In case of D3D9, D3D10, D3D11 and OpenGL this is called during device initialization as well and behaves as if an implicit immediate command list was created. + /// + InitCommandList, + + /// + /// Called on command list destruction, before: + /// + /// ID3D11CommandList::Release + /// ID3D12CommandList::Release + /// vkFreeCommandBuffers + /// + /// Callback function signature: void (api::command_list *cmd_list) + /// + DestroyCommandList, + + /// + /// Called after successful command queue creation, from: + /// + /// ID3D12Device::CreateCommandQueue + /// vkCreateDevice (for every queue associated with the device) + /// + /// Callback function signature: void (api::command_queue *queue) + /// + /// + /// In case of D3D9, D3D10, D3D11 and OpenGL this is called during device initialization as well and behaves as if an implicit command queue was created. + /// + InitCommandQueue, + + /// + /// Called on command queue destruction, before: + /// + /// ID3D12CommandQueue::Release + /// vkDestroyDevice (for every queue associated with the device) + /// + /// Callback function signature: void (api::command_queue *queue) + /// + DestroyCommandQueue, + + /// + /// Called after successful swap chain creation, from: + /// + /// IDirect3D9::CreateDevice (for the implicit swap chain) + /// IDirect3D9Ex::CreateDeviceEx (for the implicit swap chain) + /// IDirect3D9Device::CreateAdditionalSwapChain + /// IDXGIFactory::CreateSwapChain + /// IDXGIFactory2::CreateSwapChain(...) + /// wglMakeCurrent + /// wglSwapBuffers (after window was resized) + /// vkCreateSwapchainKHR + /// xrCreateSession + /// + /// In addition, called when swap chain is resized, after: + /// + /// IDirect3DDevice9::Reset (for the implicit swap chain) + /// IDirect3DDevice9Ex::ResetEx (for the implicit swap chain) + /// IDXGISwapChain::ResizeBuffers + /// IDXGISwapChain3::ResizeBuffers1 + /// + /// Callback function signature: void (api::swapchain *swapchain) + /// + InitSwapChain, + + /// + /// Called on swap chain creation, before: + /// + /// IDirect3D9::CreateDevice (for the implicit swap chain) + /// IDirect3D9Ex::CreateDeviceEx (for the implicit swap chain) + /// IDirect3D9Device::CreateAdditionalSwapChain + /// IDirect3D9Device::Reset (for the implicit swap chain) + /// IDirect3D9DeviceEx::ResetEx (for the implicit swap chain) + /// IDXGIFactory::CreateSwapChain + /// IDXGIFactory2::CreateSwapChain(...) + /// IDXGISwapChain::ResizeBuffers + /// IDXGISwapChain3::ResizeBuffers1 + /// wglSetPixelFormat + /// vkCreateSwapchainKHR + /// + /// Callback function signature: bool (api::swapchain_desc &desc, void *hwnd) + /// + /// + /// To overwrite the swap chain description, modify desc in the callback and return , otherwise return . + /// + CreateSwapChain, + + /// + /// Called on swap chain destruction, before: + /// + /// IDirect3DDevice9::Release (for the implicit swap chain) + /// IDirect3DSwapChain9::Release + /// IDXGISwapChain::Release + /// wglDeleteContext + /// wglSwapBuffers (after window was resized) + /// vkDestroySwapchainKHR + /// xrDestroySession + /// + /// In addition, called when swap chain is resized, before: + /// + /// IDirect3DDevice9::Reset (for the implicit swap chain) + /// IDirect3DDevice9Ex::ResetEx (for the implicit swap chain) + /// IDXGISwapChain::ResizeBuffers + /// IDXGISwapChain1::ResizeBuffers1 + /// + /// Callback function signature: void (api::swapchain *swapchain) + /// + DestroySwapChain, + + /// + /// Called after effect runtime initialization (which happens after swap chain creation or a swap chain buffer resize). + /// Callback function signature: void (api::effect_runtime *runtime) + /// + InitEffectRuntime, + + /// + /// Called when an effect runtime is reset or destroyed. + /// Callback function signature: void (api::effect_runtime *runtime) + /// + DestroyEffectRuntime, + + /// + /// Called after successful sampler creation from: + /// + /// ID3D10Device::CreateSamplerState + /// ID3D11Device::CreateSamplerState + /// ID3D12Device::CreateSampler + /// vkCreateSampler + /// + /// Callback function signature: void (api::device *device, const api::sampler_desc &desc, api::sampler sampler) + /// + /// + /// Is not called in D3D9 (since samplers are loose state there) or OpenGL. + /// + InitSampler, + + /// + /// Called on sampler creation, before: + /// + /// ID3D10Device::CreateSamplerState + /// ID3D11Device::CreateSamplerState + /// ID3D12Device::CreateSampler + /// ID3D12Device::CreateRootSignature + /// vkCreateSampler + /// + /// Callback function signature: bool (api::device *device, api::sampler_desc &desc) + /// + /// + /// To overwrite the sampler description, modify desc in the callback and return , otherwise return . + /// Is not called in D3D9 (since samplers are loose state there) or OpenGL. + /// + CreateSampler, + + /// + /// Called on sampler destruction, before: + /// + /// ID3D10SamplerState::Release + /// ID3D11SamplerState::Release + /// glDeleteSamplers + /// vkDestroySampler + /// + /// Callback function signature: void (api::device *device, api::sampler sampler) + /// + /// + /// Is not called in D3D9 (since samplers are loose state there), D3D12 (since samplers are descriptor handles instead of objects there) or OpenGL. + /// + DestroySampler, + + /// + /// Called after successful resource creation from: + /// + /// IDirect3DDevice9::CreateVertexBuffer + /// IDirect3DDevice9::CreateIndexBuffer + /// IDirect3DDevice9::CreateTexture + /// IDirect3DDevice9::CreateCubeTexture + /// IDirect3DDevice9::CreateVolumeTexture + /// IDirect3DDevice9::CreateRenderTargetSurface + /// IDirect3DDevice9::CreateDepthStencilSurface + /// IDirect3DDevice9::CreateOffscreenPlainSurface + /// IDirect3DDevice9Ex::CreateRenderTargetSurfaceEx + /// IDirect3DDevice9Ex::CreateDepthStencilSurfaceEx + /// IDirect3DDevice9Ex::CreateOffscreenPlainSurfaceEx + /// ID3D10Device::CreateBuffer + /// ID3D10Device::CreateTexture1D + /// ID3D10Device::CreateTexture2D + /// ID3D10Device::CreateTexture2D + /// ID3D11Device::CreateBuffer + /// ID3D11Device::CreateTexture1D + /// ID3D11Device::CreateTexture2D + /// ID3D11Device::CreateTexture3D + /// ID3D11Device3::CreateTexture2D + /// ID3D11Device3::CreateTexture3D + /// ID3D12Device::CreateCommittedResource + /// ID3D12Device::CreatePlacedResource + /// ID3D12Device::CreateReservedResource + /// ID3D12Device4::CreateCommittedResource1 + /// ID3D12Device4::CreateReservedResource1 + /// glBufferData + /// glBufferStorage + /// glNamedBufferData + /// glNamedBufferStorage + /// glTexImage1D + /// glTexImage2D + /// glTexImage2DMultisample + /// glTexImage3D + /// glTexImage3DMultisample + /// glCompressedTexImage1D + /// glCompressedTexImage2D + /// glCompressedTexImage3D + /// glTexStorage1D + /// glTexStorage2D + /// glTexStorage2DMultisample + /// glTexStorage3D + /// glTexStorage3DMultisample + /// glTextureStorage1D + /// glTextureStorage2D + /// glTextureStorage2DMultisample + /// glTextureStorage3D + /// glTextureStorage3DMultisample + /// glRenderbufferStorage + /// glRenderbufferStorageMultisample + /// glNamedRenderbufferStorage + /// glNamedRenderbufferStorageMultisample + /// vkBindBufferMemory + /// vkBindBufferMemory2 + /// vkBindImageMemory + /// vkBindImageMemory2 + /// + /// Callback function signature: void (api::device *device, const api::resource_desc &desc, const api::subresource_data *initial_data, api::resource_usage initial_state, api::resource resource) + /// + /// + /// May be called multiple times with the same resource handle (whenever the resource is updated or its reference count is incremented). + /// + InitResource, + + /// + /// Called on resource creation, before: + /// + /// IDirect3DDevice9::CreateVertexBuffer + /// IDirect3DDevice9::CreateIndexBuffer + /// IDirect3DDevice9::CreateTexture + /// IDirect3DDevice9::CreateCubeTexture + /// IDirect3DDevice9::CreateVolumeTexture + /// IDirect3DDevice9::CreateRenderTargetSurface + /// IDirect3DDevice9::CreateDepthStencilSurface + /// IDirect3DDevice9::CreateOffscreenPlainSurface + /// IDirect3DDevice9Ex::CreateRenderTargetSurfaceEx + /// IDirect3DDevice9Ex::CreateDepthStencilSurfaceEx + /// IDirect3DDevice9Ex::CreateOffscreenPlainSurfaceEx + /// ID3D10Device::CreateBuffer + /// ID3D10Device::CreateTexture1D + /// ID3D10Device::CreateTexture2D + /// ID3D10Device::CreateTexture2D + /// ID3D11Device::CreateBuffer + /// ID3D11Device::CreateTexture1D + /// ID3D11Device::CreateTexture2D + /// ID3D11Device::CreateTexture3D + /// ID3D11Device3::CreateTexture2D + /// ID3D11Device3::CreateTexture3D + /// ID3D12Device::CreateCommittedResource + /// ID3D12Device::CreatePlacedResource + /// ID3D12Device::CreateReservedResource + /// ID3D12Device4::CreateCommittedResource1 + /// ID3D12Device4::CreateReservedResource1 + /// glBufferData + /// glBufferStorage + /// glNamedBufferData + /// glNamedBufferStorage + /// glTexImage1D + /// glTexImage2D + /// glTexImage2DMultisample + /// glTexImage3D + /// glTexImage3DMultisample + /// glCompressedTexImage1D + /// glCompressedTexImage2D + /// glCompressedTexImage3D + /// glTexStorage1D + /// glTexStorage2D + /// glTexStorage2DMultisample + /// glTexStorage3D + /// glTexStorage3DMultisample + /// glTextureStorage1D + /// glTextureStorage2D + /// glTextureStorage2DMultisample + /// glTextureStorage3D + /// glTextureStorage3DMultisample + /// glRenderbufferStorage + /// glRenderbufferStorageMultisample + /// glNamedRenderbufferStorage + /// glNamedRenderbufferStorageMultisample + /// vkCreateBuffer + /// vkCreateImage + /// + /// Callback function signature: bool (api::device *device, api::resource_desc &desc, api::subresource_data *initial_data, api::resource_usage initial_state) + /// + /// + /// To overwrite the resource description, modify desc in the callback and return , otherwise return . + /// + CreateResource, + + /// + /// Called on resource destruction, before: + /// + /// IDirect3DResource9::Release + /// ID3D10Resource::Release + /// ID3D11Resource::Release + /// ID3D12Resource::Release + /// glDeleteBuffers + /// glDeleteTextures + /// glDeleteRenderbuffers + /// vkDestroyBuffer + /// vkDestroyImage + /// + /// Callback function signature: void (api::device *device, api::resource resource) + /// + DestroyResource, + + /// + /// Called after successful resource view creation from: + /// + /// IDirect3DDevice9::CreateTexture + /// IDirect3DDevice9::CreateCubeTexture + /// IDirect3DDevice9::CreateVolumeTexture + /// ID3D10Device::CreateShaderResourceView + /// ID3D10Device::CreateRenderTargetView + /// ID3D10Device::CreateDepthStencilView + /// ID3D10Device1::CreateShaderResourceView1 + /// ID3D11Device::CreateShaderResourceView + /// ID3D11Device::CreateUnorderedAccessView + /// ID3D11Device::CreateRenderTargetView + /// ID3D11Device::CreateDepthStencilView + /// ID3D11Device3::CreateShaderResourceView1 + /// ID3D11Device3::CreateUnorderedAccessView1 + /// ID3D11Device3::CreateRenderTargetView1 + /// ID3D12Device::CreateShaderResourceView + /// ID3D12Device::CreateUnorderedAccessView + /// ID3D12Device::CreateRenderTargetView + /// ID3D12Device::CreateDepthStencilView + /// glTexBuffer + /// glTextureBuffer + /// glTextureView + /// vkCreateBufferView + /// vkCreateImageView + /// vkCreateAccelerationStructureKHR + /// + /// Callback function signature: void (api::device *device, api::resource resource, api::resource_usage usage_type, const api::resource_view_desc &desc, api::resource_view view) + /// + /// + /// May be called multiple times with the same resource view handle (whenever the resource view is updated). + /// + InitResourceView, + + /// + /// Called on resource view creation, before: + /// + /// ID3D10Device::CreateShaderResourceView + /// ID3D10Device::CreateRenderTargetView + /// ID3D10Device::CreateDepthStencilView + /// ID3D10Device1::CreateShaderResourceView1 + /// ID3D11Device::CreateShaderResourceView + /// ID3D11Device::CreateUnorderedAccessView + /// ID3D11Device::CreateRenderTargetView + /// ID3D11Device::CreateDepthStencilView + /// ID3D11Device3::CreateShaderResourceView1 + /// ID3D11Device3::CreateUnorderedAccessView1 + /// ID3D11Device3::CreateRenderTargetView1 + /// ID3D12Device::CreateShaderResourceView + /// ID3D12Device::CreateUnorderedAccessView + /// ID3D12Device::CreateRenderTargetView + /// ID3D12Device::CreateDepthStencilView + /// glTexBuffer + /// glTextureBuffer + /// glTextureView + /// vkCreateBufferView + /// vkCreateImageView + /// vkCreateAccelerationStructureKHR + /// + /// Callback function signature: bool (api::device *device, api::resource resource, api::resource_usage usage_type, api::resource_view_desc &desc) + /// + /// + /// To overwrite the resource view description, modify desc in the callback and return , otherwise return . + /// Is not called in D3D9 (since resource views are tied to resources there). + /// + CreateResourceView, + + /// + /// Called on resource view destruction, before: + /// + /// IDirect3DResource9::Release + /// ID3D10View::Release + /// ID3D11View::Release + /// glDeleteTextures + /// vkDestroyBufferView + /// vkDestroyImageView + /// vkDestroyAccelerationStructureKHR + /// + /// Callback function signature: void (api::device *device, api::resource_view view) + /// + /// + /// Is not called in D3D12 (since resource views are descriptor handles instead of objects there). + /// + DestroyResourceView, + + /// + /// Called after: + /// + /// IDirect3DVertexBuffer9::Lock + /// IDirect3DIndexBuffer9::Lock + /// ID3D10Resource::Map + /// ID3D11DeviceContext::Map + /// ID3D12Resource::Map + /// glMapBuffer + /// glMapBufferRange + /// glMapNamedBuffer + /// glMapNamedBufferRange + /// + /// Callback function signature: void (api::device *device, api::resource resource, uint64_t offset, uint64_t size, api::map_access access, void **data) + /// + MapBufferRegion, + + /// + /// Called before: + /// + /// IDirect3DVertexBuffer9::Unlock + /// IDirect3DIndexBuffer9::Unlock + /// ID3D10Resource::Unmap + /// ID3D11DeviceContext::Unmap + /// ID3D12Resource::Unmap + /// glUnmapBuffer + /// glUnmapNamedBuffer + /// + /// Callback function signature: void (api::device *device, api::resource resource) + /// + UnmapBufferRegion, + + /// + /// Called after: + /// + /// IDirect3DSurface9::LockRect + /// IDirect3DVolume9::LockBox + /// IDirect3DTexture9::LockRect + /// IDirect3DVolumeTexture9::LockBox + /// IDirect3DCubeTexture9::LockRect + /// ID3D10Resource::Map + /// ID3D11DeviceContext::Map + /// ID3D12Resource::Map + /// + /// Callback function signature: void (api::device *device, api::resource resource, uint32_t subresource, const api::subresource_box *box, api::map_access access, api::subresource_data *data) + /// + MapTextureRegion, + + /// + /// Called before: + /// + /// IDirect3DSurface9::UnlockRect + /// IDirect3DVolume9::UnlockBox + /// IDirect3DTexture9::UnlockRect + /// IDirect3DVolumeTexture9::UnlockBox + /// IDirect3DCubeTexture9::UnlockRect + /// ID3D10Resource::Unmap + /// ID3D11DeviceContext::Unmap + /// ID3D12Resource::Unmap + /// + /// Callback function signature: void (api::device *device, api::resource resource, uint32_t subresource) + /// + UnmapTextureRegion, + + /// + /// Called before: + /// + /// ID3D10Device::UpdateSubresource + /// ID3D11DeviceContext::UpdateSubresource + /// glBufferSubData + /// glNamedBufferSubData + /// + /// Callback function signature: bool (api::device *device, const void *data, api::resource resource, uint64_t offset, uint64_t size) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Destination resource will be in the state. + /// + UpdateBufferRegion, + + /// + /// Called before: + /// + /// ID3D10Device::UpdateSubresource + /// ID3D11DeviceContext::UpdateSubresource + /// glTexSubData1D + /// glTexSubData2D + /// glTexSubData3D + /// glTextureSubData1D + /// glTextureSubData2D + /// glTextureSubData3D + /// glCompressedTexSubData1D + /// glCompressedTexSubData2D + /// glCompressedTexSubData3D + /// glCompressedTextureSubData1D + /// glCompressedTextureSubData2D + /// glCompressedTextureSubData3D + /// + /// Callback function signature: bool (api::device *device, const api::subresource_data &data, api::resource resource, uint32_t subresource, const api::subresource_box *box) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Destination resource will be in the state. + /// + UpdateTextureRegion, + + /// + /// Called after successful pipeline creation from: + /// + /// IDirect3DDevice9::CreateVertexShader + /// IDirect3DDevice9::CreatePixelShader + /// IDirect3DDevice9::CreateVertexDeclaration + /// ID3D10Device::CreateVertexShader + /// ID3D10Device::CreateGeometryShader + /// ID3D10Device::CreateGeometryShaderWithStreamOutput + /// ID3D10Device::CreatePixelShader + /// ID3D10Device::CreateInputLayout + /// ID3D10Device::CreateBlendState + /// ID3D10Device::CreateDepthStencilState + /// ID3D10Device::CreateRasterizerState + /// ID3D10Device1::CreateBlendState1 + /// ID3D11Device::CreateVertexShader + /// ID3D11Device::CreateHullShader + /// ID3D11Device::CreateDomainShader + /// ID3D11Device::CreateGeometryShader + /// ID3D11Device::CreateGeometryShaderWithStreamOutput + /// ID3D11Device::CreatePixelShader + /// ID3D11Device::CreateComputeShader + /// ID3D11Device::CreateInputLayout + /// ID3D11Device::CreateBlendState + /// ID3D11Device::CreateDepthStencilState + /// ID3D11Device::CreateRasterizerState + /// ID3D11Device1::CreateBlendState1 + /// ID3D11Device1::CreateRasterizerState1 + /// ID3D11Device3::CreateRasterizerState2 + /// ID3D12Device::CreateComputePipelineState + /// ID3D12Device::CreateGraphicsPipelineState + /// ID3D12Device2::CreatePipelineState + /// ID3D12Device5::CreateStateObject + /// ID3D12Device7::AddToStateObject + /// ID3D12PipelineLibrary::LoadComputePipeline + /// ID3D12PipelineLibrary::LoadGraphicsPipeline + /// ID3D12PipelineLibrary1::LoadPipeline + /// glLinkProgram + /// vkCreateComputePipelines + /// vkCreateGraphicsPipelines + /// + /// Callback function signature: void (api::device *device, api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline pipeline) + /// + /// + /// May be called multiple times with the same pipeline handle (whenever the pipeline is updated or its reference count is incremented). + /// + InitPipeline, + + /// + /// Called on pipeline creation, before: + /// + /// IDirect3DDevice9::CreateVertexShader + /// IDirect3DDevice9::CreatePixelShader + /// IDirect3DDevice9::CreateVertexDeclaration + /// ID3D10Device::CreateVertexShader + /// ID3D10Device::CreateGeometryShader + /// ID3D10Device::CreateGeometryShaderWithStreamOutput + /// ID3D10Device::CreatePixelShader + /// ID3D10Device::CreateInputLayout + /// ID3D10Device::CreateBlendState + /// ID3D10Device::CreateDepthStencilState + /// ID3D10Device::CreateRasterizerState + /// ID3D10Device1::CreateBlendState1 + /// ID3D11Device::CreateVertexShader + /// ID3D11Device::CreateHullShader + /// ID3D11Device::CreateDomainShader + /// ID3D11Device::CreateGeometryShader + /// ID3D11Device::CreateGeometryShaderWithStreamOutput + /// ID3D11Device::CreatePixelShader + /// ID3D11Device::CreateComputeShader + /// ID3D11Device::CreateInputLayout + /// ID3D11Device::CreateBlendState + /// ID3D11Device::CreateDepthStencilState + /// ID3D11Device::CreateRasterizerState + /// ID3D11Device1::CreateBlendState1 + /// ID3D11Device1::CreateRasterizerState1 + /// ID3D11Device3::CreateRasterizerState2 + /// ID3D12Device::CreateComputePipelineState + /// ID3D12Device::CreateGraphicsPipelineState + /// ID3D12Device2::CreatePipelineState + /// ID3D12Device5::CreateStateObject + /// glShaderSource + /// vkCreateComputePipelines + /// vkCreateGraphicsPipelines + /// + /// Callback function signature: bool (api::device *device, api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects) + /// + /// + /// To overwrite the pipeline description, modify desc in the callback and return , otherwise return . + /// + CreatePipeline, + + /// + /// Called on pipeline destruction, before: + /// + /// ID3D10VertexShader::Release + /// ID3D10GeometryShader::Release + /// ID3D10PixelShader::Release + /// ID3D10InputLayout::Release + /// ID3D10BlendState::Release + /// ID3D10DepthStencilState::Release + /// ID3D10RasterizerState::Release + /// ID3D11VertexShader::Release + /// ID3D11HullShader::Release + /// ID3D11DomainShader::Release + /// ID3D11GeometryShader::Release + /// ID3D11PixelShader::Release + /// ID3D11ComputeShader::Release + /// ID3D11InputLayout::Release + /// ID3D11BlendState::Release + /// ID3D11DepthStencilState::Release + /// ID3D11RasterizerState::Release + /// ID3D12PipelineState::Release + /// ID3D12StateObject::Release + /// glDeleteProgram + /// vkDestroyPipeline + /// + /// Callback function signature: void (api::device *device, api::pipeline pipeline) + /// + /// + /// Is not called in D3D9. + /// + DestroyPipeline, + + /// + /// Called after successful pipeline layout creation from: + /// + /// ID3D12Device::CreateRootSignature + /// vkCreatePipelineLayout + /// + /// Callback function signature: void (api::device *device, uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout layout) + /// + /// + /// In case of D3D9, D3D10, D3D11 and OpenGL this is called during device initialization as well and behaves as if an implicit global pipeline layout was created. + /// + InitPipelineLayout, + + /// + /// Called on pipeline layout creation, before: + /// + /// ID3D12Device::CreateRootSignature + /// vkCreatePipelineLayout + /// + /// Callback function signature: bool (api::device *device, uint32_t &param_count, api::pipeline_layout_param *&params) + /// + /// + /// Is not called in D3D9, D3D10, D3D11 or OpenGL. + /// + CreatePipelineLayout, + + /// + /// Called on pipeline layout destruction, before: + /// + /// ID3D12RootSignature::Release + /// VkDestroyPipelineLayout + /// + /// Callback function signature: void (api::device *device, api::pipeline_layout layout) + /// + DestroyPipelineLayout, + + /// + /// Called before: + /// + /// ID3D12Device::CopyDescriptors + /// ID3D12Device::CopyDescriptorsSimple + /// vkUpdateDescriptorSets + /// + /// Callback function signature: bool (api::device *device, uint32_t count, const api::descriptor_table_copy *copies) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + CopyDescriptorTables, + + /// + /// Called before: + /// + /// ID3D12Device::CreateConstantBufferView + /// ID3D12Device::CreateShaderResourceView + /// ID3D12Device::CreateUnorderedAccessView + /// ID3D12Device::CreateSampler + /// vkUpdateDescriptorSets + /// + /// Callback function signature: bool (api::device *device, uint32_t count, const api::descriptor_table_update *updates) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + UpdateDescriptorTables, + + /// + /// Called after successful query heap creation from: + /// + /// ID3D12Device::CreateQueryHeap + /// vkCreateQueryPool + /// + /// Callback function signature: void (api::device *device, api::query_type type, uint32_t size, api::query_heap heap) + /// + InitQueryHeap, + + /// + /// Called on query heap creation, before: + /// + /// ID3D12Device::CreateQueryHeap + /// vkCreateQueryPool + /// + /// Callback function signature: bool (api::device *device, api::query_type type, uint32_t &size) + /// + CreateQueryHeap, + + /// + /// Called on query heap destruction, before: + /// + /// ID3D12QueryHeap::Release + /// vkDestroyQueryPool + /// + /// Callback function signature: void (api::device *device, api::query_heap heap) + /// + DestroyQueryHeap, + + /// + /// Called before: + /// + /// vkGetQueryPoolResults + /// + /// Callback function signature: bool (api::device *device, api::query_heap heap, uint32_t first, uint32_t count, void *results, uint32_t stride) + /// + GetQueryHeapResults, + + /// + /// Called after: + /// + /// ID3D12GraphicsCommandList::ResourceBarrier + /// ID3D12GraphicsCommandList7::Barrier + /// vkCmdPipelineBarrier + /// vkCmdPipelineBarrier2 + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t count, const api::resource *resources, const api::resource_usage *old_states, const api::resource_usage *new_states) + /// + Barrier, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList4::BeginRenderPass + /// vkCmdBeginRenderPass + /// vkCmdBeginRenderPass2 + /// vkCmdNextSubpass + /// vkCmdNextSubpass2 + /// vkCmdBeginRendering + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t count, const api::render_pass_render_target_desc *rts, const api::render_pass_depth_stencil_desc *ds) + /// + /// + /// The depth-stencil description argument is optional and may be (which indicates that no depth-stencil is used). + /// + BeginRenderPass, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList4::EndRenderPass + /// vkCmdEndRenderPass + /// vkCmdEndRenderPass2 + /// vkCmdNextSubpass + /// vkCmdNextSubpass2 + /// vkCmdEndRendering + /// + /// Callback function signature: void (api::command_list *cmd_list) + /// + EndRenderPass, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetRenderTarget + /// IDirect3DDevice9::SetDepthStencilSurface + /// ID3D10Device::OMSetRenderTargets + /// ID3D11DeviceContext::OMSetRenderTargets + /// ID3D11DeviceContext::OMSetRenderTargetsAndUnorderedAccessViews + /// ID3D12GraphicsCommandList::OMSetRenderTargets + /// glBindFramebuffer + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t count, const api::resource_view *rtvs, api::resource_view dsv) + /// + BindRenderTargetsAndDepthStencil, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetVertexShader + /// IDirect3DDevice9::SetPixelShader + /// IDirect3DDevice9::SetVertexDeclaration + /// IDirect3DDevice9::ProcessVertices + /// ID3D10Device::VSSetShader + /// ID3D10Device::GSSetShader + /// ID3D10Device::PSSetShader + /// ID3D10Device::IASetInputLayout + /// ID3D10Device::OMSetBlendState + /// ID3D10Device::OMSetDepthStencilState + /// ID3D10Device::RSSetState + /// ID3D11DeviceContext::VSSetShader + /// ID3D11DeviceContext::HSSetShader + /// ID3D11DeviceContext::DSSetShader + /// ID3D11DeviceContext::GSSetShader + /// ID3D11DeviceContext::PSSetShader + /// ID3D11DeviceContext::CSSetShader + /// ID3D11DeviceContext::IASetInputLayout + /// ID3D11DeviceContext::OMSetBlendState + /// ID3D11DeviceContext::OMSetDepthStencilState + /// ID3D11DeviceContext::RSSetState + /// ID3D12GraphicsCommandList::Reset + /// ID3D12GraphicsCommandList::SetPipelineState + /// ID3D12GraphicsCommandList4::SetPipelineState1 + /// glUseProgram + /// glBindVertexArray + /// vkCmdBindPipeline + /// + /// Callback function signature: void (api::command_list *cmd_list, api::pipeline_stage stages, api::pipeline pipeline) + /// + BindPipeline, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetRenderState + /// ID3D10Device::IASetPrimitiveTopology + /// ID3D10Device::OMSetBlendState + /// ID3D10Device::OMSetDepthStencilState + /// ID3D11DeviceContext::IASetPrimitiveTopology + /// ID3D11DeviceContext::OMSetBlendState + /// ID3D11DeviceContext::OMSetDepthStencilState + /// ID3D12GraphicsCommandList::IASetPrimitiveTopology + /// ID3D12GraphicsCommandList::OMSetBlendFactor + /// ID3D12GraphicsCommandList::OMSetStencilRef + /// gl(...) + /// vkCmdSetDepthBias + /// vkCmdSetBlendConstants + /// vkCmdSetStencilCompareMask + /// vkCmdSetStencilWriteMask + /// vkCmdSetStencilReference + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t count, const api::dynamic_state *states, const uint32_t *values) + /// + BindPipelineStates, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetViewport + /// IDirect3DDevice9::SetRenderTarget (implicitly updates the viewport) + /// ID3D10Device::RSSetViewports + /// ID3D11DeviceContext::RSSetViewports + /// ID3D12GraphicsCommandList::RSSetViewports + /// glViewport + /// glViewportArrayv + /// glViewportIndexedf + /// glViewportIndexedfv + /// vkCmdSetViewport + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t first, uint32_t count, const api::viewport *viewports) + /// + BindViewports, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetScissorRect + /// ID3D10Device::RSSetScissorRects + /// ID3D11DeviceContext::RSSetScissorRects + /// ID3D12GraphicsCommandList::RSSetScissorRects + /// glScissor + /// glScissorArrayv + /// glScissorIndexed + /// glScissorIndexedv + /// vkCmdSetScissor + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t first, uint32_t count, const api::rect *rects) + /// + BindScissorRects, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetVertexShaderConstantF + /// IDirect3DDevice9::SetPixelShaderConstantF + /// ID3D12GraphicsCommandList::SetComputeRoot32BitConstant + /// ID3D12GraphicsCommandList::SetComputeRoot32BitConstants + /// ID3D12GraphicsCommandList::SetGraphicsRoot32BitConstant + /// ID3D12GraphicsCommandList::SetGraphicsRoot32BitConstants + /// glUniform(...) + /// vkCmdPushConstants + /// + /// Callback function signature: void (api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) + /// + PushConstants, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetTexture + /// ID3D10Device::VSSetSamplers + /// ID3D10Device::VSSetShaderResources + /// ID3D10Device::VSSetConstantBuffers + /// ID3D10Device::GSSetSamplers + /// ID3D10Device::GSSetShaderResources + /// ID3D10Device::GSSetConstantBuffers + /// ID3D10Device::PSSetSamplers + /// ID3D10Device::PSSetShaderResources + /// ID3D10Device::PSSetConstantBuffers + /// ID3D11DeviceContext::VSSetSamplers + /// ID3D11DeviceContext::VSSetShaderResources + /// ID3D11DeviceContext::VSSetConstantBuffers + /// ID3D11DeviceContext::HSSetSamplers + /// ID3D11DeviceContext::HSSetShaderResources + /// ID3D11DeviceContext::HSSetConstantBuffers + /// ID3D11DeviceContext::DSSetSamplers + /// ID3D11DeviceContext::DSSetShaderResources + /// ID3D11DeviceContext::DSSetConstantBuffers + /// ID3D11DeviceContext::GSSetSamplers + /// ID3D11DeviceContext::GSSetShaderResources + /// ID3D11DeviceContext::GSSetConstantBuffers + /// ID3D11DeviceContext::PSSetSamplers + /// ID3D11DeviceContext::PSSetShaderResources + /// ID3D11DeviceContext::PSSetConstantBuffers + /// ID3D11DeviceContext::CSSetSamplers + /// ID3D11DeviceContext::CSSetShaderResources + /// ID3D11DeviceContext::CSSetUnorderedAccessViews + /// ID3D11DeviceContext::CSSetConstantBuffers + /// ID3D12GraphicsCommandList::SetComputeRootConstantBufferView + /// ID3D12GraphicsCommandList::SetGraphicsRootConstantBufferView + /// ID3D12GraphicsCommandList::SetComputeRootShaderResourceView + /// ID3D12GraphicsCommandList::SetGraphicsRootShaderResourceView + /// ID3D12GraphicsCommandList::SetComputeRootUnorderedAccessView + /// ID3D12GraphicsCommandList::SetGraphicsRootUnorderedAccessView + /// glBindBufferBase + /// glBindBufferRange + /// glBindBuffersBase + /// glBindBuffersRange + /// glBindTexture + /// glBindImageTexture + /// glBindTextures + /// glBindImageTextures + /// glBindTextureUnit + /// glBindMultiTextureEXT + /// vkCmdPushDescriptorSetKHR + /// + /// Callback function signature: void (api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) + /// + PushDescriptors, + + /// + /// Called after: + /// + /// ID3D12GraphicsCommandList::SetComputeRootSignature + /// ID3D12GraphicsCommandList::SetGraphicsRootSignature + /// ID3D12GraphicsCommandList::SetComputeRootDescriptorTable + /// ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable + /// vkCmdBindDescriptorSets + /// + /// Callback function signature: void (api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) + /// + BindDescriptorTables, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetIndices + /// ID3D10Device::IASetIndexBuffer + /// ID3D11DeviceContext::IASetIndexBuffer + /// ID3D12GraphicsCommandList::IASetIndexBuffer + /// glBindBuffer + /// vkCmdBindIndexBuffer + /// + /// Callback function signature: void (api::command_list *cmd_list, api::resource buffer, uint64_t offset, uint32_t index_size) + /// + BindIndexBuffer, + + /// + /// Called after: + /// + /// IDirect3DDevice9::SetStreamSource + /// ID3D10Device::IASetVertexBuffers + /// ID3D11DeviceContext::IASetVertexBuffers + /// ID3D12GraphicsCommandList::IASetVertexBuffers + /// glBindBuffer + /// glBindVertexBuffer + /// glBindVertexBuffers + /// vkCmdBindVertexBuffers + /// vkCmdBindVertexBuffers2 + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) + /// + /// + /// The strides argument is optional and may be . + /// + BindVertexBuffers, + + /// + /// Called after: + /// + /// IDirect3DDevice9::ProcessVertices + /// ID3D10Device::SOSetTargets + /// ID3D11DeviceContext::SOSetTargets + /// ID3D12GraphicsCommandList::SOSetTargets + /// glBindBufferBase + /// glBindBufferRange + /// glBindBuffersBase + /// glBindBuffersRange + /// vkCmdBindTransformFeedbackBuffersEXT + /// + /// Callback function signature: void (api::command_list *cmd_list, uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint64_t *max_sizes, const api::resource *counter_buffers, const uint64_t *counter_offsets) + /// + /// + /// The counter arguments are optional and may be . + /// + BindStreamOutputBuffers, + + /// + /// Called before: + /// + /// IDirect3DDevice9::DrawPrimitive + /// IDirect3DDevice9::DrawPrimitiveUP + /// IDirect3DDevice9::ProcessVertices + /// ID3D10Device::Draw + /// ID3D10Device::DrawInstanced + /// ID3D11DeviceContext::Draw + /// ID3D11DeviceContext::DrawInstanced + /// ID3D12GraphicsCommandList::DrawInstanced + /// glDrawArrays + /// glDrawArraysInstanced + /// glDrawArraysInstancedBaseInstance + /// glMultiDrawArrays + /// vkCmdDraw + /// + /// Callback function signature: bool (api::command_list *cmd_list, uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + Draw, + + /// + /// Called before: + /// + /// IDirect3DDevice9::DrawIndexedPrimitive + /// IDirect3DDevice9::DrawIndexedPrimitiveUP + /// ID3D10Device::DrawIndexed + /// ID3D10Device::DrawIndexedInstanced + /// ID3D11DeviceContext::DrawIndexed + /// ID3D11DeviceContext::DrawIndexedInstanced + /// ID3D12GraphicsCommandList::DrawIndexedInstanced + /// glDrawElements + /// glDrawElementsBaseVertex + /// glDrawElementsInstanced + /// glDrawElementsInstancedBaseVertex + /// glDrawElementsInstancedBaseInstance + /// glDrawElementsInstancedBaseVertexBaseInstance + /// glMultiDrawElements + /// glMultiDrawElementsBaseVertex + /// vkCmdDrawIndexed + /// + /// Callback function signature: bool (api::command_list *cmd_list, uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t vertex_offset, uint32_t first_instance) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + DrawIndexed, + + /// + /// Called before: + /// + /// ID3D11DeviceContext::Dispatch + /// ID3D12GraphicsCommandList::Dispatch + /// glDispatchCompute + /// vkCmdDispatch + /// + /// Callback function signature: bool (api::command_list *cmd_list, uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + Dispatch = 54, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::DispatchMesh + /// vkCmdDrawMeshTasksEXT + /// + /// Callback function signature: bool (api::command_list *cmd_list, uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + DispatchMesh = 89, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::DispatchRays + /// vkCmdTraceRaysKHR + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource raygen, uint64_t raygen_offset, uint64_t raygen_size, api::resource miss, uint64_t miss_offset, uint64_t miss_size, uint64_t miss_stride, api::resource hit_group, uint64_t hit_group_offset, uint64_t hit_group_size, uint64_t hit_group_stride, api::resource callable, uint64_t callable_offset, uint64_t callable_size, uint64_t callable_stride, uint32_t width, uint32_t height, uint32_t depth) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// In case of D3D12 and Vulkan, the shader handle buffer handles may be zero with the buffers instead referred to via a device address passed in the related offset argument. + /// + DispatchRays = 90, + + /// + /// Called before: + /// + /// ID3D11DeviceContext::DrawInstancedIndirect + /// ID3D11DeviceContext::DrawIndexedInstancedIndirect + /// ID3D11DeviceContext::DispatchIndirect + /// ID3D12GraphicsCommandList::ExecuteIndirect + /// glDrawArraysIndirect + /// glDrawElementsIndirect + /// glMultiDrawArraysIndirect + /// glMultiDrawElementsIndirect + /// glDispatchComputeIndirect + /// vkCmdDrawIndirect + /// vkCmdDrawIndexedIndirect + /// vkCmdDispatchIndirect + /// vkCmdTraceRaysIndirect2KHR + /// vkCmdDrawMeshTasksIndirectEXT + /// vkCmdDrawMeshTasksIndirectCountEXT + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::indirect_command type, api::resource buffer, uint64_t offset, uint32_t draw_count, uint32_t stride) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + DrawOrDispatchIndirect = 55, + + /// + /// Called before: + /// + /// IDirect3DDevice9::UpdateTexture + /// IDirect3DDevice9::GetRenderTargetData + /// ID3D10Device::CopyResource + /// ID3D11DeviceContext::CopyResource + /// ID3D12GraphicsCommandList::CopyResource + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource source, api::resource dest) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Source resource will be in the state. + /// Destination resource will be in the state. + /// + CopyResource, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::CopyBufferRegion + /// glCopyBufferSubData + /// glCopyNamedBufferSubData + /// vkCmdCopyBuffer + /// vkCmdCopyBuffer2 + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource source, uint64_t source_offset, api::resource dest, uint64_t dest_offset, uint64_t size) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Source resource will be in the state. + /// Destination resource will be in the state. + /// + CopyBufferRegion, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::CopyTextureRegion + /// vkCmdCopyBufferToImage + /// vkCmdCopyBufferToImage2 + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource source, uint64_t source_offset, uint32_t row_length, uint32_t slice_height, api::resource dest, uint32_t dest_subresource, const api::subresource_box *dest_box) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Source resource will be in the state. + /// Destination resource will be in the state. + /// The subresource box argument is optional and may be (which indicates the entire subresource is referenced). + /// + CopyBufferToTexture, + + /// + /// Called before: + /// + /// IDirect3DDevice9::UpdateSurface + /// IDirect3DDevice9::StretchRect + /// ID3D10Device::CopySubresourceRegion + /// ID3D11DeviceContext::CopySubresourceRegion + /// ID3D12GraphicsCommandList::CopyTextureRegion + /// glBlitFramebuffer + /// glBlitNamedFramebuffer + /// glCopyImageSubData + /// glCopyTexSubImage1D + /// glCopyTexSubImage2D + /// glCopyTexSubImage3D + /// glCopyTextureSubImage1D + /// glCopyTextureSubImage2D + /// glCopyTextureSubImage3D + /// vkCmdBlitImage + /// vkCmdBlitImage2 + /// vkCmdCopyImage + /// vkCmdCopyImage2 + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource source, uint32_t source_subresource, const api::subresource_box *source_box, api::resource dest, uint32_t dest_subresource, const api::subresource_box *dest_box, api::filter_mode filter) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Source resource will be in the state. + /// Destination resource will be in the state. + /// The subresource box arguments are optional and may be (which indicates the entire subresource is used). + /// + CopyTextureRegion, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::CopyTextureRegion + /// vkCmdCopyImageToBuffer + /// vkCmdCopyImageToBuffer2 + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource source, uint32_t source_subresource, const api::subresource_box *source_box, api::resource dest, uint64_t dest_offset, uint32_t row_length, uint32_t slice_height) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Source resource will be in the state. + /// Destination resource will be in the state. + /// The subresource box argument is optional and may be (which indicates the entire subresource is used). + /// + CopyTextureToBuffer, + + /// + /// Called before: + /// + /// IDirect3DDevice9::StretchRect + /// ID3D10Device::ResolveSubresource + /// ID3D11DeviceContext::ResolveSubresource + /// ID3D12GraphicsCommandList::ResolveSubresource + /// ID3D12GraphicsCommandList1::ResolveSubresourceRegion + /// glBlitFramebuffer + /// glBlitNamedFramebuffer + /// vkCmdResolveImage + /// vkCmdResolveImage2 + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource source, uint32_t source_subresource, const api::subresource_box *source_box, api::resource dest, uint32_t dest_subresource, int32_t dest_x, int32_t dest_y, int32_t dest_z, api::format format) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Source resource will be in the state. + /// Destination resource will be in the state. + /// The subresource box argument is optional and may be (which indicates the entire subresource is used). + /// + ResolveTextureRegion, + + /// + /// Called before: + /// + /// IDirect3DDevice9::Clear + /// ID3D10Device::ClearDepthStencilView + /// ID3D11DeviceContext::ClearDepthStencilView + /// ID3D11DeviceContext1::ClearView (for depth-stencil views) + /// ID3D12GraphicsCommandList::ClearDepthStencilView + /// glClear + /// glClearBufferfi + /// glClearBufferfv + /// glClearNamedFramebufferfi + /// glClearNamedFramebufferfv + /// vkCmdClearDepthStencilImage + /// vkCmdClearAttachments + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource_view dsv, const float *depth, const uint8_t *stencil, uint32_t rect_count, const api::rect *rects) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Resource will be in the state. + /// One of the depth or stencil clear value arguments may be when the respective component is not cleared. + /// + ClearDepthStencilView, + + /// + /// Called before: + /// + /// IDirect3DDevice9::Clear + /// IDirect3DDevice9::ColorFill + /// ID3D10Device::ClearRenderTargetView + /// ID3D11DeviceContext::ClearRenderTargetView + /// ID3D11DeviceContext1::ClearView (for render target views) + /// ID3D12GraphicsCommandList::ClearRenderTargetView + /// glClear + /// glClearBufferfv + /// glClearNamedFramebufferfv + /// vkCmdClearColorImage + /// vkCmdClearAttachments + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource_view rtv, const float color[4], uint32_t rect_count, const api::rect *rects) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Resources will be in the state. + /// + ClearRenderTargetView, + + /// + /// Called before: + /// + /// ID3D11DeviceContext::ClearUnorderedAccessViewUint + /// ID3D12GraphicsCommandList::ClearUnorderedAccessViewUint + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource_view uav, const uint32_t values[4], uint32_t rect_count, const api::rect *rects) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Resource will be in the state. + /// + ClearUnorderedAccessViewUint, + + /// + /// Called before: + /// + /// ID3D11DeviceContext::ClearUnorderedAccessViewFloat + /// ID3D11DeviceContext1::ClearView (for unordered access views) + /// ID3D12GraphicsCommandList::ClearUnorderedAccessViewFloat + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource_view uav, const float values[4], uint32_t rect_count, const api::rect *rects) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// Resource will be in the state. + /// + ClearUnorderedAccessViewFloat, + + /// + /// Called before: + /// + /// ID3D10Device::GenerateMips + /// ID3D11DeviceContext::GenerateMips + /// glGenerateMipmap + /// glGenerateTextureMipmap + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource_view srv) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + GenerateMipmaps, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::BeginQuery + /// vkCmdBeginQuery + /// vkCmdBeginQueryIndexedEXT + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::query_heap heap, api::query_type type, uint32_t index) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + BeginQuery, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::EndQuery + /// vkCmdEndQuery + /// vkCmdEndQueryIndexedEXT + /// vkCmdWriteTimestamp + /// vkCmdWriteTimestamp2 + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::query_heap heap, api::query_type type, uint32_t index) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + EndQuery, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::ResolveQueryData + /// vkCmdCopyQueryPoolResults + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::query_heap heap, api::query_type type, uint32_t first, uint32_t count, api::resource dest, uint64_t dest_offset, uint32_t stride) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + CopyQueryHeapResults = 69, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList4::CopyRaytracingAccelerationStructure + /// vkCmdCopyAccelerationStructureKHR + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::resource_view source, api::resource_view dest, api::acceleration_structure_copy_mode mode) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// + CopyAccelerationStructure = 91, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList4::BuildRaytracingAccelerationStructure + /// vkCmdBuildAccelerationStructuresKHR + /// + /// Callback function signature: bool (api::command_list *cmd_list, api::acceleration_structure_type type, api::acceleration_structure_build_flags flags, uint32_t input_count, const api::acceleration_structure_build_input *inputs, api::resource scratch, uint64_t scratch_offset, api::resource_view source, api::resource_view dest, api::acceleration_structure_build_mode mode) + /// + /// + /// To prevent this command from being executed, return , otherwise return . + /// In case of D3D12 and Vulkan, the scratch buffer handle may be zero with the buffer instead referred to via a device address passed in the related offset argument. + /// Scratch buffer will be in the resource state. + /// + BuildAccelerationStructure = 92, + + /// + /// Called before: + /// + /// ID3D12GraphicsCommandList::Reset + /// vkBeginCommandBuffer + /// + /// Callback function signature: void (api::command_list *cmd_list) + /// + /// + /// Is not called for immediate command lists (since they cannot be reset). + /// + ResetCommandList = 70, + + /// + /// Called before: + /// + /// ID3D11DeviceContext::FinishCommandList + /// ID3D12GraphicsCommandList::Close + /// vkEndCommandBuffer + /// + /// Callback function signature: void (api::command_list *cmd_list) + /// + /// + /// Is not called for immediate command lists (since they cannot be closed). + /// + CloseCommandList, + + /// + /// Called when a command list is submitted to a command queue (or an immediate command list is flushed), before: + /// + /// IDirect3DDevice9::EndScene + /// ID3D10Device::Flush + /// ID3D11DeviceContext::Flush + /// ID3D11DeviceContext3::Flush1 + /// ID3D12CommandQueue::ExecuteCommandLists + /// glFlush + /// vkQueueSubmit + /// + /// Callback function signature: void (api::command_queue *queue, api::command_list *cmd_list) + /// + ExecuteCommandList, + + /// + /// Called when a secondary command list is executed on a primary command list, before: + /// + /// ID3D11DeviceContext::ExecuteCommandList + /// ID3D12GraphicsCommandList::ExecuteBundle + /// vkCmdExecuteCommands + /// + /// In addition, called after: + /// + /// ID3D11DeviceContext::FinishCommandList + /// + /// Callback function signature: void (api::command_list *cmd_list, api::command_list *secondary_cmd_list) + /// + ExecuteSecondaryCommandList, + + /// + /// Called before: + /// + /// IDirect3DDevice9::Present + /// IDirect3DDevice9Ex::PresentEx + /// IDirect3DSwapChain9::Present + /// IDXGISwapChain::Present + /// IDXGISwapChain3::Present1 + /// ID3D12CommandQueueDownlevel::Present + /// wglSwapBuffers + /// vkQueuePresentKHR + /// IVRCompositor::Submit + /// xrEndFrame + /// + /// Callback function signature: void (api::command_queue *queue, api::swapchain *swapchain, const api::rect *source_rect, const api::rect *dest_rect, uint32_t dirty_rect_count, const api::rect *dirty_rects) + /// + /// + /// The source and destination rectangle arguments are optional and may be (which indicates the swap chain is presented in its entirety). + /// + Present, + + /// + /// Called before: + /// + /// IDXGISwapChain::SetFullscreenState + /// vkAcquireFullScreenExclusiveModeEXT + /// vkReleaseFullScreenExclusiveModeEXT + /// + /// Callback function signature: bool (api::swapchain *swapchain, bool fullscreen, void *hmonitor) + /// + /// + /// To prevent the fullscreen state from being changed, return , otherwise return . + /// + SetFullscreenState = 93, + + /// + /// Called after ReShade has rendered its overlay. + /// Callback function signature: void (api::effect_runtime *runtime) + /// + ReShadePresent = 75, + + /// + /// Called right before ReShade effects are rendered. + /// Callback function signature: void (api::effect_runtime *runtime, api::command_list *cmd_list, api::resource_view rtv, api::resource_view rtv_srgb) + /// + ReShadeBeginEffects, + + /// + /// Called right after ReShade effects were rendered. + /// Callback function signature: void (api::effect_runtime *runtime, api::command_list *cmd_list, api::resource_view rtv, api::resource_view rtv_srgb) + /// + ReShadeFinishEffects, + + /// + /// Called right after all ReShade effects were reloaded. + /// This occurs during effect runtime initialization or because the user pressed the "Reload" button in the overlay. + /// Any , and handles are invalidated when this event occurs and need to be queried again. + /// Callback function signature: void (api::effect_runtime *runtime) + /// + ReShadeReloadedEffects, + + /// + /// Called before a uniform variable is changed, with the new value. + /// Callback function signature: bool (api::effect_runtime *runtime, api::effect_uniform_variable variable, const void *new_value, size_t new_value_size) + /// + /// + /// To prevent the variable value from being changed, return , otherwise return . + /// The new value has the data type reported by . The new value size is in bytes. + /// + ReShadeSetUniformValue, + + /// + /// Called before a technique is enabled or disabled, with the new state. + /// Callback function signature: bool (api::effect_runtime *runtime, api::effect_technique technique, bool enabled) + /// + /// + /// To prevent the technique state from being changed, return , otherwise return . + /// + ReShadeSetTechniqueState, + + /// + /// Called between the ImGui::NewFrame and ImGui::EndFrame calls for the ReShade overlay. + /// Can be used to perform custom Dear ImGui calls, but it is recommended to instead use to register a dedicated overlay. + /// Callback function signature: void (api::effect_runtime *runtime) + /// + /// + /// This is not called for effect runtimes in VR. + /// + ReShadeOverlay, + + /// + /// Called after a screenshot was taken and saved to disk, with the path to the saved image file. + /// Callback function signature: void (api::effect_runtime *runtime, const char *path) + /// + ReShadeScreenshot, + + /// + /// Called for each technique after it was rendered, usually between and . + /// Callback function signature: void (api::effect_runtime *runtime, api::effect_technique technique, api::command_list *cmd_list, api::resource_view rtv, api::resource_view rtv_srgb) + /// + ReShadeRenderTechnique, + + /// + /// Called when all effects are about to be enabled or disabled. + /// Callback function signature: bool (api::effect_runtime *runtime, bool enabled) + /// + /// + /// To prevent the effects state from being changed, return , otherwise return . + /// + ReShadeSetEffectsState = 94, + + /// + /// Called after a preset was loaded and applied. + /// This occurs after effect reloading or when the user chooses a new preset in the overlay. + /// Callback function signature: void (api::effect_runtime *runtime, const char *path) + /// + ReShadeSetCurrentPresetPath = 84, + + /// + /// Called when the rendering order of loaded techniques is changed, with a handle array specifying the new order. + /// Callback function signature: bool (api::effect_runtime *runtime, size_t count, api::effect_technique *techniques) + /// + /// + /// To prevent the order from being changed, return , otherwise return . + /// + ReShadeReorderTechniques, + + /// + /// Called when the ReShade overlay is about to be opened or closed. + /// Callback function signature: bool (api::effect_runtime *runtime, bool open, api::input_source source) + /// + /// + /// To prevent the overlay state from being changed, return , otherwise return . + /// + ReShadeOpenOverlay, + + /// + /// Called when a uniform variable widget is added to the variable list in the overlay. + /// Can be used to replace with custom one or add widgets for specific uniform variables. + /// Callback function signature: bool (api::effect_runtime *runtime, api::effect_uniform_variable variable) + /// + /// + /// To prevent the normal widget from being added to the overlay, return , otherwise return . + /// + ReShadeOverlayUniformVariable, + + /// + /// Called when a technique is added to the technique list in the overlay. + /// Can be used to replace with custom one or add widgets for specific techniques. + /// Callback function signature: bool (api::effect_runtime *runtime, api::effect_technique technique) + /// + /// + /// To prevent the normal widget from being added to the overlay, return , otherwise return . + /// + ReShadeOverlayTechnique, + } +} diff --git a/Dalamud/Interface/Internal/ReShadeAddonInterface.Exports.cs b/Dalamud/Interface/Internal/ReShadeAddonInterface.Exports.cs new file mode 100644 index 000000000..3dce1b79b --- /dev/null +++ b/Dalamud/Interface/Internal/ReShadeAddonInterface.Exports.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +using TerraFX.Interop.Windows; + +using static TerraFX.Interop.Windows.Windows; + +namespace Dalamud.Interface.Internal; + +/// ReShade interface. +[SuppressMessage( + "StyleCop.CSharp.LayoutRules", + "SA1519:Braces should not be omitted from multi-line child statement", + Justification = "Multiple fixed blocks")] +internal sealed unsafe partial class ReShadeAddonInterface +{ + private static readonly ExportsStruct Exports; + + static ReShadeAddonInterface() + { + foreach (var m in Process.GetCurrentProcess().Modules.Cast()) + { + ExportsStruct e; + if (!GetProcAddressInto(m, nameof(e.ReShadeRegisterAddon), &e.ReShadeRegisterAddon) || + !GetProcAddressInto(m, nameof(e.ReShadeUnregisterAddon), &e.ReShadeUnregisterAddon) || + !GetProcAddressInto(m, nameof(e.ReShadeRegisterEvent), &e.ReShadeRegisterEvent) || + !GetProcAddressInto(m, nameof(e.ReShadeUnregisterEvent), &e.ReShadeUnregisterEvent)) + continue; + + ReShadeModule = m; + Exports = e; + return; + } + + return; + + bool GetProcAddressInto(ProcessModule m, ReadOnlySpan name, void* res) + { + Span name8 = stackalloc byte[Encoding.UTF8.GetByteCount(name) + 1]; + name8[Encoding.UTF8.GetBytes(name, name8)] = 0; + *(nint*)res = GetProcAddress((HMODULE)m.BaseAddress, (sbyte*)Unsafe.AsPointer(ref name8[0])); + return *(nint*)res != 0; + } + } + + /// Gets the active ReShade module. + public static ProcessModule? ReShadeModule { get; private set; } + + private struct ExportsStruct + { + public delegate* unmanaged ReShadeRegisterAddon; + public delegate* unmanaged ReShadeUnregisterAddon; + public delegate* unmanaged ReShadeRegisterEvent; + public delegate* unmanaged ReShadeUnregisterEvent; + } +} diff --git a/Dalamud/Interface/Internal/ReShadeAddonInterface.cs b/Dalamud/Interface/Internal/ReShadeAddonInterface.cs new file mode 100644 index 000000000..156688c27 --- /dev/null +++ b/Dalamud/Interface/Internal/ReShadeAddonInterface.cs @@ -0,0 +1,176 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Dalamud.Hooking; + +using JetBrains.Annotations; + +using TerraFX.Interop.Windows; + +using static TerraFX.Interop.Windows.Windows; + +namespace Dalamud.Interface.Internal; + +/// ReShade interface. +internal sealed unsafe partial class ReShadeAddonInterface : IDisposable +{ + private const int ReShadeApiVersion = 12; + + private readonly HMODULE hDalamudModule; + + private readonly Hook addonModuleResolverHook; + + private readonly DelegateStorage reShadeOverlayDelegate; + private readonly DelegateStorage initSwapChainDelegate; + private readonly DelegateStorage destroySwapChainDelegate; + + private ReShadeAddonInterface() + { + this.hDalamudModule = (HMODULE)Marshal.GetHINSTANCE(typeof(ReShadeAddonInterface).Assembly.ManifestModule); + if (!Exports.ReShadeRegisterAddon(this.hDalamudModule, ReShadeApiVersion)) + throw new InvalidOperationException("ReShadeRegisterAddon failure."); + + this.addonModuleResolverHook = Hook.FromImport( + ReShadeModule!, + "kernel32.dll", + nameof(GetModuleHandleExW), + 0, + this.GetModuleHandleExWDetour); + + this.addonModuleResolverHook.Enable(); + Exports.ReShadeRegisterEvent( + AddonEvent.ReShadeOverlay, + this.reShadeOverlayDelegate = new((ref ApiObject rt) => this.ReShadeOverlay?.Invoke(ref rt))); + Exports.ReShadeRegisterEvent( + AddonEvent.InitSwapChain, + this.initSwapChainDelegate = new((ref ApiObject rt) => this.InitSwapChain?.Invoke(ref rt))); + Exports.ReShadeRegisterEvent( + AddonEvent.DestroySwapChain, + this.destroySwapChainDelegate = new((ref ApiObject rt) => this.DestroySwapChain?.Invoke(ref rt))); + } + + /// Finalizes an instance of the class. + ~ReShadeAddonInterface() => this.ReleaseUnmanagedResources(); + + /// Delegate for . + /// Reference to the ReShade runtime. + public delegate void ReShadeOverlayDelegate(ref ApiObject effectRuntime); + + /// Delegate for . + /// Reference to the ReShade SwapChain wrapper. + public delegate void ReShadeInitSwapChain(ref ApiObject swapChain); + + /// Delegate for . + /// Reference to the ReShade SwapChain wrapper. + public delegate void ReShadeDestroySwapChain(ref ApiObject swapChain); + + private delegate BOOL GetModuleHandleExWDelegate(uint dwFlags, ushort* lpModuleName, HMODULE* phModule); + + /// Called on . + public event ReShadeOverlayDelegate? ReShadeOverlay; + + /// Called on . + public event ReShadeInitSwapChain? InitSwapChain; + + /// Called on . + public event ReShadeDestroySwapChain? DestroySwapChain; + + /// Registers Dalamud as a ReShade addon. + /// Initialized interface. + /// true on success. + public static bool TryRegisterAddon([NotNullWhen(true)] out ReShadeAddonInterface? r) + { + try + { + r = Exports.ReShadeRegisterAddon is null ? null : new(); + return r is not null; + } + catch + { + r = null; + return false; + } + } + + /// + public void Dispose() + { + this.ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + private void ReleaseUnmanagedResources() + { + Exports.ReShadeUnregisterEvent(AddonEvent.InitSwapChain, this.initSwapChainDelegate); + Exports.ReShadeUnregisterEvent(AddonEvent.DestroySwapChain, this.destroySwapChainDelegate); + Exports.ReShadeUnregisterEvent(AddonEvent.ReShadeOverlay, this.reShadeOverlayDelegate); + Exports.ReShadeUnregisterAddon(this.hDalamudModule); + this.addonModuleResolverHook.Disable(); + this.addonModuleResolverHook.Dispose(); + } + + private BOOL GetModuleHandleExWDetour(uint dwFlags, ushort* lpModuleName, HMODULE* phModule) + { + if ((dwFlags & GET.GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) == 0) + return this.addonModuleResolverHook.Original(dwFlags, lpModuleName, phModule); + if ((dwFlags & GET.GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) == 0) + return this.addonModuleResolverHook.Original(dwFlags, lpModuleName, phModule); + if (lpModuleName == this.initSwapChainDelegate || + lpModuleName == this.destroySwapChainDelegate || + lpModuleName == this.reShadeOverlayDelegate) + { + *phModule = this.hDalamudModule; + return BOOL.TRUE; + } + + return this.addonModuleResolverHook.Original(dwFlags, lpModuleName, phModule); + } + + /// ReShade effect runtime object. + [StructLayout(LayoutKind.Sequential)] + public struct ApiObject + { + /// The vtable. + public VTable* Vtbl; + + /// Gets this object as a typed pointer. + /// Address of this instance. + /// This call is invalid if this object is not already fixed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ApiObject* AsPointer() => (ApiObject*)Unsafe.AsPointer(ref this); + + /// Gets the native object. + /// The native object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public nint GetNative() => this.Vtbl->GetNative(this.AsPointer()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* GetNative() where T : unmanaged => (T*)this.GetNative(); + + /// VTable of . + [StructLayout(LayoutKind.Sequential)] + public struct VTable + { + /// + public delegate* unmanaged GetNative; + } + } + + private readonly struct DelegateStorage where T : Delegate + { + [UsedImplicitly] + public readonly T Delegate; + + public readonly void* Address; + + public DelegateStorage(T @delegate) + { + this.Delegate = @delegate; + this.Address = (void*)Marshal.GetFunctionPointerForDelegate(@delegate); + } + + public static implicit operator void*(DelegateStorage sto) => sto.Address; + } +} diff --git a/Dalamud/Interface/Internal/SwapChainHelper.cs b/Dalamud/Interface/Internal/SwapChainHelper.cs index 09c1f52fd..e0c3f21a8 100644 --- a/Dalamud/Interface/Internal/SwapChainHelper.cs +++ b/Dalamud/Interface/Internal/SwapChainHelper.cs @@ -1,13 +1,7 @@ -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; @@ -16,12 +10,6 @@ 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 @@ -92,102 +80,4 @@ internal static unsafe class SwapChainHelper 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; - } - } - } }