From f4e0f66459b600b5229178e0eaf9406ddbf16777 Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Tue, 28 Nov 2023 02:46:14 +0900 Subject: [PATCH] More safety --- Dalamud/ImGuiScene/Helpers/UnknownComBase.cs | 99 +++++ Dalamud/ImGuiScene/IImGuiRenderer.cs | 35 +- Dalamud/ImGuiScene/IImGuiScene.cs | 25 +- Dalamud/ImGuiScene/ITexturePipelineWrap.cs | 18 + .../Implementations/Dx11Renderer.Texture.cs | 219 +++++------ .../Dx11Renderer.ViewportHandler.cs | 212 ++++++---- .../Implementations/Dx11Renderer.cs | 336 +++++++++------- .../Implementations/Dx11Win32Scene.cs | 34 +- .../Implementations/Dx12OnDx11Win32Scene.cs | 12 +- .../Implementations/Dx12Renderer.Textures.cs | 363 +++++++++--------- .../Dx12Renderer.ViewportHandler.cs | 126 ++++-- .../Implementations/Dx12Renderer.cs | 115 +++--- .../Implementations/Dx12Win32Scene.cs | 12 +- .../Interface/Internal/DalamudTextureWrap.cs | 23 +- .../Interface/Internal/InterfaceManager.cs | 48 ++- Dalamud/Interface/Internal/TextureManager.cs | 2 +- .../Windows/Data/Widgets/IconBrowserWidget.cs | 2 +- Dalamud/Interface/Utility/ImVectorWrapper.cs | 4 +- 18 files changed, 984 insertions(+), 701 deletions(-) create mode 100644 Dalamud/ImGuiScene/Helpers/UnknownComBase.cs create mode 100644 Dalamud/ImGuiScene/ITexturePipelineWrap.cs diff --git a/Dalamud/ImGuiScene/Helpers/UnknownComBase.cs b/Dalamud/ImGuiScene/Helpers/UnknownComBase.cs new file mode 100644 index 0000000000..c20c5c3b9c --- /dev/null +++ b/Dalamud/ImGuiScene/Helpers/UnknownComBase.cs @@ -0,0 +1,99 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +using TerraFX.Interop.Windows; + +namespace Dalamud.ImGuiScene.Helpers; + +/// +/// Base for implementing COM. The implementor must have this struct at the beginning of the struct layout. +/// +/// +/// Would use generics as UnknownComBase{T}, but doing so makes the types containing this fail to initialize. +/// +[StructLayout(LayoutKind.Sequential)] +[SuppressMessage( + "StyleCop.CSharp.SpacingRules", + "SA1023:Dereference and access of symbols should be spaced correctly", + Justification = "Wtf")] +internal unsafe struct UnknownComBase +{ + private static readonly nint[] Vtbl; + + private void* pVtbl; + private void* pObject; + private uint* pRefCount; + private delegate* pfnDynamicCast; + private delegate* pfnFinalRelease; + + static UnknownComBase() + { + Vtbl = GC.AllocateArray(3, true); + Vtbl[0] = (nint)(delegate*)&StaticQueryInterface; + Vtbl[1] = (nint)(delegate*)&StaticAddRef; + Vtbl[2] = (nint)(delegate*)&StaticRelease; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A pointer to the parent struct. + /// A pointer to the reference counter. + /// Dynamic cast function. Return null to . Do not change refcount. + /// Final release function. + public UnknownComBase( + void* pObject, + uint* pRefCount, + delegate* pfnDynamicCast, + delegate* pfnFinalRelease = null) + { + this.pVtbl = Unsafe.AsPointer(ref Vtbl[0]); + this.pObject = pObject; + this.pRefCount = pRefCount; + this.pfnDynamicCast = pfnDynamicCast; + this.pfnFinalRelease = pfnFinalRelease; + } + + /// + /// Gets the address of this struct as a pointer to . + /// + public IUnknown* AsPunk => (IUnknown*)Unsafe.AsPointer(ref this); + + private static HRESULT StaticQueryInterface(UnknownComBase* self, Guid* piid, void** ppvObject) + { + if (ppvObject == null) + return E.E_POINTER; + + *ppvObject = null; + if (piid == null) + return E.E_INVALIDARG; + + if (*piid == IID.IID_IUnknown) + { + StaticAddRef(self); + *ppvObject = self; + return S.S_OK; + } + + if ((*ppvObject = self->pfnDynamicCast(self->pObject, *piid)) is null) + return E.E_NOINTERFACE; + + StaticAddRef(self); + return S.S_OK; + } + + private static uint StaticAddRef(UnknownComBase* self) => Interlocked.Increment(ref *self->pRefCount); + + private static uint StaticRelease(UnknownComBase* self) + { + var r = Interlocked.Decrement(ref *self->pRefCount); + if (r > 0) + return r; + if (self->pfnFinalRelease is not null) + self->pfnFinalRelease(self->pObject); + Marshal.FreeHGlobal((nint)self->pObject); + return 0; + } +} diff --git a/Dalamud/ImGuiScene/IImGuiRenderer.cs b/Dalamud/ImGuiScene/IImGuiRenderer.cs index efd5d8adfa..73aa0ec8f7 100644 --- a/Dalamud/ImGuiScene/IImGuiRenderer.cs +++ b/Dalamud/ImGuiScene/IImGuiRenderer.cs @@ -1,4 +1,6 @@ -using Dalamud.Interface.Internal; +using System.Runtime.CompilerServices; + +using Dalamud.Interface.Internal; using ImGuiNET; @@ -41,26 +43,20 @@ internal interface IImGuiRenderer : IDisposable /// /// Sets the texture pipeline. The pipeline must be created from the concrete implementation of this interface.
- /// The references of and are copied. - /// You may dispose after the call. + /// The references of and are copied. + /// You may dispose after the call. ///
- /// The texture handle. - /// The pipeline handle. - void SetTexturePipeline(nint textureHandle, nint pipelineHandle); + /// The texture handle. + /// The pipeline handle to set, or null to clear. + void SetTexturePipeline(IDalamudTextureWrap texture, ITexturePipelineWrap? pipeline); /// /// Creates a new reference of the pipeline registered for use with the given texture.
/// Dispose after use. ///
- /// The texture handle. - /// The previous pixel shader handle, or 0 if none. - nint GetTexturePipeline(nint textureHandle); - - /// - /// Disposes a reference to the pipeline. - /// - /// The pipeline handle. - void ReleaseTexturePipeline(nint pipelineHandle); + /// The texture handle. + /// The previous pixel shader handle, or null if none. + ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap texture); /// /// Adds a user callback handler. @@ -83,6 +79,13 @@ internal interface IImGuiRenderer : IDisposable /// The width in pixels. /// The height in pixels. /// Format of the texture. + /// Name for debugging. /// A texture, ready to use in ImGui. - IDalamudTextureWrap LoadTexture(ReadOnlySpan data, int pitch, int width, int height, int format); + IDalamudTextureWrap LoadTexture( + ReadOnlySpan data, + int pitch, + int width, + int height, + int format, + [CallerMemberName] string debugName = ""); } diff --git a/Dalamud/ImGuiScene/IImGuiScene.cs b/Dalamud/ImGuiScene/IImGuiScene.cs index 10ab7f2a67..b501295988 100644 --- a/Dalamud/ImGuiScene/IImGuiScene.cs +++ b/Dalamud/ImGuiScene/IImGuiScene.cs @@ -117,28 +117,11 @@ internal interface IImGuiScene : IDisposable /// A texture, ready to use in ImGui. IDalamudTextureWrap LoadImageFormat(ReadOnlySpan data, int pitch, int width, int height, int format); - /// - /// Sets the texture pipeline. The pipeline must be created from the concrete implementation of this interface.
- /// The references of and are copied. - /// You may dispose after the call. - ///
- /// The texture handle. - /// The pipeline handle. - void SetTexturePipeline(nint textureHandle, nint pipelineHandle); + /// + void SetTexturePipeline(IDalamudTextureWrap textureHandle, ITexturePipelineWrap? pipelineHandle); - /// - /// Creates a new reference of the pipeline registered for use with the given texture.
- /// Dispose after use. - ///
- /// The texture handle. - /// The previous pixel shader handle, or 0 if none. - nint GetTexturePipeline(nint textureHandle); - - /// - /// Disposes a reference to the pipeline. - /// - /// The pipeline handle. - void ReleaseTexturePipeline(nint pipelineHandle); + /// + ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap textureHandle); /// /// Determines if is owned by this. diff --git a/Dalamud/ImGuiScene/ITexturePipelineWrap.cs b/Dalamud/ImGuiScene/ITexturePipelineWrap.cs new file mode 100644 index 0000000000..eb05a5fecc --- /dev/null +++ b/Dalamud/ImGuiScene/ITexturePipelineWrap.cs @@ -0,0 +1,18 @@ +namespace Dalamud.ImGuiScene; + +/// +/// Represents a handle to an immutable texture pipeline. +/// +public interface ITexturePipelineWrap : ICloneable, IDisposable +{ + /// + /// Gets a value indicating whether this instance of has been disposed. + /// + bool IsDisposed { get; } + + /// + new ITexturePipelineWrap Clone(); + + /// + object ICloneable.Clone() => this.Clone(); +} diff --git a/Dalamud/ImGuiScene/Implementations/Dx11Renderer.Texture.cs b/Dalamud/ImGuiScene/Implementations/Dx11Renderer.Texture.cs index 969d4b19f0..7f0013061e 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx11Renderer.Texture.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx11Renderer.Texture.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; +using Dalamud.ImGuiScene.Helpers; using Dalamud.Interface.Internal; using Dalamud.Utility; @@ -28,26 +28,48 @@ private struct TexturePipeline : IUnknown.Interface private static readonly Guid MyGuid = new(0xd3a0fe60, 0x060a, 0x49d6, 0x8f, 0x6d, 0x68, 0xe2, 0xec, 0x59, 0x05, 0xc5); - private static readonly nint[] Vtbl; - - private void* vtbl; + private UnknownComBase combase; private uint refCount; private ComPtr shader; private ComPtr sampler; - static TexturePipeline() + public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in MyGuid)); + + public static TexturePipeline* Create(ID3D11PixelShader* pixelShader, ID3D11SamplerState* samplerState) { - Vtbl = GC.AllocateArray(3, true); - Vtbl[0] = (nint)(delegate*)&StaticQueryInterface; - Vtbl[1] = (nint)(delegate*)&StaticAddRef; - Vtbl[2] = (nint)(delegate*)&StaticRelease; + var mem = (TexturePipeline*)Marshal.AllocHGlobal(sizeof(TexturePipeline)); + if (mem is null) + throw new OutOfMemoryException(); + + *mem = new() + { + combase = new(mem, &mem->refCount, &DynamicCast, &FinalReleaseUnmanagedResources), + refCount = 1, + shader = new(pixelShader), + sampler = new(samplerState), + }; + return mem; } - public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in MyGuid)); + public static TexturePipeline* Create( + ID3D11Device* device, + ReadOnlySpan ps, + in D3D11_SAMPLER_DESC samplerDesc) + { + using var shader = default(ComPtr); + fixed (byte* pArray = ps) + device->CreatePixelShader(pArray, (nuint)ps.Length, null, shader.GetAddressOf()).ThrowHr(); - public static TexturePipeline* CreateNew( + using var sampler = default(ComPtr); + fixed (D3D11_SAMPLER_DESC* pSamplerDesc = &samplerDesc) + device->CreateSamplerState(pSamplerDesc, sampler.GetAddressOf()).ThrowHr(); + + return Create(shader, sampler); + } + + public static TexturePipeline* Create( ID3D11Device* device, - ReadOnlySpan ps) => CreateNew( + ReadOnlySpan ps) => Create( device, ps, new() @@ -63,67 +85,30 @@ static TexturePipeline() MaxLOD = 0, }); - public static TexturePipeline* CreateNew( - ID3D11Device* device, - ReadOnlySpan ps, - in D3D11_SAMPLER_DESC samplerDesc) - { - using var shader = default(ComPtr); - fixed (byte* pArray = ps) - device->CreatePixelShader(pArray, (nuint)ps.Length, null, shader.GetAddressOf()).ThrowHr(); - - using var sampler = default(ComPtr); - fixed (D3D11_SAMPLER_DESC* pSamplerDesc = &samplerDesc) - device->CreateSamplerState(pSamplerDesc, sampler.GetAddressOf()).ThrowHr(); - - var mem = (TexturePipeline*)Marshal.AllocHGlobal(sizeof(TexturePipeline)); - if (mem is null) - throw new OutOfMemoryException(); - *mem = new() { vtbl = Unsafe.AsPointer(ref Vtbl[0]), refCount = 1 }; - shader.Swap(ref mem->shader); - sampler.Swap(ref mem->sampler); - return mem; - } - public void BindTo(ID3D11DeviceContext* ctx) { ctx->PSSetShader(this.shader, null, 0); ctx->PSSetSamplers(0, 1, this.sampler.GetAddressOf()); } - public HRESULT QueryInterface(Guid* riid, void** ppvObject) - { - if (riid == null) - return E.E_INVALIDARG; - if (ppvObject == null) - return E.E_POINTER; - if (*riid == IID.IID_IUnknown || *riid == MyGuid) - ppvObject[0] = Unsafe.AsPointer(ref this); - else - return E.E_NOINTERFACE; - return S.S_OK; - } + /// + HRESULT IUnknown.Interface.QueryInterface(Guid* piid, void** ppvObject) => + this.combase.AsPunk->QueryInterface(piid, ppvObject); + + /// + uint IUnknown.Interface.AddRef() => this.combase.AsPunk->AddRef(); + + /// + uint IUnknown.Interface.Release() => this.combase.AsPunk->Release(); - public uint AddRef() => Interlocked.Increment(ref this.refCount); + private static void* DynamicCast(void* self, in Guid guid) => guid == MyGuid ? self : null; - public uint Release() + private static void FinalReleaseUnmanagedResources(void* pThisVoid) { - var r = Interlocked.Decrement(ref this.refCount); - if (r != 0) - return r; - - this.shader.Reset(); - this.sampler.Reset(); - Marshal.FreeHGlobal((nint)Unsafe.AsPointer(ref this)); - return 0; + var pThis = (TexturePipeline*)pThisVoid; + pThis->shader.Reset(); + pThis->sampler.Reset(); } - - private static HRESULT StaticQueryInterface(TexturePipeline* self, Guid* riid, void** ppvObject) => - self->QueryInterface(riid, ppvObject); - - private static uint StaticAddRef(TexturePipeline* self) => self->AddRef(); - - private static uint StaticRelease(TexturePipeline* self) => self->Release(); } [Guid("72fe3f82-3ffc-4be9-b008-4aef7a942f55")] @@ -133,50 +118,40 @@ private struct TextureData : IUnknown.Interface public static readonly Guid MyGuid = new(0x72fe3f82, 0x3ffc, 0x4be9, 0xb0, 0x08, 0x4a, 0xef, 0x7a, 0x94, 0x2f, 0x55); - private static readonly nint[] Vtbl; - - private void* vtbl; + private UnknownComBase combase; private uint refCount; private ComPtr srv; private ComPtr customPipeline; - static TextureData() - { - Vtbl = GC.AllocateArray(3, true); - Vtbl[0] = (nint)(delegate*)&StaticQueryInterface; - Vtbl[1] = (nint)(delegate*)&StaticAddRef; - Vtbl[2] = (nint)(delegate*)&StaticRelease; - } - public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in MyGuid)); public int Width { get; private init; } public int Height { get; private init; } - public ComPtr CustomPipeline + public TexturePipeline* CustomPipeline { - get => this.customPipeline.IsEmpty() ? default : new(this.customPipeline); + get => this.customPipeline.Get(); set { if (this.customPipeline.Get() == value) return; this.customPipeline.Reset(); - if (!value.IsEmpty()) - value.CopyTo(ref this.customPipeline); + if (value is not null) + this.customPipeline = new(value); } } public ID3D11ShaderResourceView* ShaderResourceView => this.srv; - public static TextureData* CreateNew(ID3D11ShaderResourceView* srv, int width, int height) + public static TextureData* Create(ID3D11ShaderResourceView* srv, int width, int height) { var mem = (TextureData*)Marshal.AllocHGlobal(sizeof(TextureData)); if (mem is null) throw new OutOfMemoryException(); *mem = new() { - vtbl = Unsafe.AsPointer(ref Vtbl[0]), + combase = new(mem, &mem->refCount, &DynamicCast, &FinalReleaseUnmanagedResources), refCount = 1, srv = new(srv), Width = width, @@ -185,54 +160,71 @@ public ComPtr CustomPipeline return mem; } - public HRESULT QueryInterface(Guid* riid, void** ppvObject) + /// + HRESULT IUnknown.Interface.QueryInterface(Guid* piid, void** ppvObject) => + this.combase.AsPunk->QueryInterface(piid, ppvObject); + + /// + uint IUnknown.Interface.AddRef() => this.combase.AsPunk->AddRef(); + + /// + uint IUnknown.Interface.Release() => this.combase.AsPunk->Release(); + + private static void* DynamicCast(void* self, in Guid guid) => guid == MyGuid ? self : null; + + private static void FinalReleaseUnmanagedResources(void* pThisVoid) { - if (riid == null) - return E.E_INVALIDARG; - if (ppvObject == null) - return E.E_POINTER; - if (*riid == IID.IID_IUnknown || *riid == MyGuid) - ppvObject[0] = Unsafe.AsPointer(ref this); - else - return E.E_NOINTERFACE; - return S.S_OK; + var pThis = (TextureData*)pThisVoid; + pThis->srv.Reset(); + pThis->customPipeline.Reset(); } + } + + private class TexturePipelineWrap : ITexturePipelineWrap + { + private ComPtr data; + + public TexturePipelineWrap(TexturePipeline* coreData) => this.data = new(coreData); + + ~TexturePipelineWrap() => this.ReleaseUnmanagedResources(); + + public TexturePipeline* Data => + this.IsDisposed ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data.Get(); - public uint AddRef() => Interlocked.Increment(ref this.refCount); + public bool IsDisposed => this.data.IsEmpty(); - public uint Release() + public void Dispose() { - var r = Interlocked.Decrement(ref this.refCount); - if (r != 0) - return r; - - this.srv.Reset(); - this.customPipeline.Reset(); - Marshal.FreeHGlobal((nint)Unsafe.AsPointer(ref this)); - return 0; + this.ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); } - private static HRESULT StaticQueryInterface(TextureData* self, Guid* riid, void** ppvObject) => - self->QueryInterface(riid, ppvObject); + public ITexturePipelineWrap Clone() => new TexturePipelineWrap( + this.data.IsEmpty() ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data); - private static uint StaticAddRef(TextureData* self) => self->AddRef(); + object ICloneable.Clone() => this.Clone(); - private static uint StaticRelease(TextureData* self) => self->Release(); + private void ReleaseUnmanagedResources() => this.data.Reset(); } private class TextureWrap : IDalamudTextureWrap, ICloneable { private ComPtr data; - internal TextureWrap(TextureData* data) => this.data = new(data); + public TextureWrap(TextureData* coreData) => this.data = new(coreData); ~TextureWrap() => this.ReleaseUnmanagedResources(); - public nint ImGuiHandle => (nint)this.data.Get(); + public TextureData* Data => + this.IsDisposed ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data.Get(); - public int Width => this.data.Get()->Width; + public bool IsDisposed => this.data.IsEmpty(); - public int Height => this.data.Get()->Height; + public nint ImGuiHandle => (nint)this.Data; + + public int Width => this.Data->Width; + + public int Height => this.Data->Height; public void Dispose() { @@ -240,16 +232,11 @@ public void Dispose() GC.SuppressFinalize(this); } - public IDalamudTextureWrap Clone() - { - var data2 = this.data; - ObjectDisposedException.ThrowIf(data2.IsEmpty(), this); - data2.Get()->AddRef(); - return new TextureWrap(data2); - } + public IDalamudTextureWrap Clone() => + new TextureWrap(this.data.IsEmpty() ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data); object ICloneable.Clone() => this.Clone(); - private void ReleaseUnmanagedResources() => this.data.Dispose(); + private void ReleaseUnmanagedResources() => this.data.Reset(); } } diff --git a/Dalamud/ImGuiScene/Implementations/Dx11Renderer.ViewportHandler.cs b/Dalamud/ImGuiScene/Implementations/Dx11Renderer.ViewportHandler.cs index 433a29a794..34560772be 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx11Renderer.ViewportHandler.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx11Renderer.ViewportHandler.cs @@ -85,23 +85,17 @@ private void OnDestroyWindow(ImGuiViewportPtr viewport) private void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) { var data = (ViewportData*)viewport.RendererUserData; - data->View.Reset(); - data->SwapChain.Get()->ResizeBuffers(1, (uint)size.X, (uint)size.Y, DXGI_FORMAT.DXGI_FORMAT_UNKNOWN, 0) - .ThrowHr(); - data->CreateView(this.renderer.device); + data->ResetBuffers(); + data->ResizeBuffers((int)size.X, (int)size.Y, true); } private void OnRenderWindow(ImGuiViewportPtr viewport, IntPtr v) { var data = (ViewportData*)viewport.RendererUserData; - this.renderer.context.Get()->OMSetRenderTargets(1, data->View.GetAddressOf(), null); - if ((viewport.Flags & ImGuiViewportFlags.NoRendererClear) != ImGuiViewportFlags.NoRendererClear) - { - var color = default(Vector4); - this.renderer.context.Get()->ClearRenderTargetView(data->View, (float*)&color); - } - - this.renderer.RenderDrawData(viewport.DrawData); + this.renderer.RenderDrawDataInternal( + data->RenderTargetView, + viewport.DrawData, + (viewport.Flags & ImGuiViewportFlags.NoRendererClear) == 0); } private void OnSwapBuffers(ImGuiViewportPtr viewport, IntPtr v) @@ -109,81 +103,153 @@ private void OnSwapBuffers(ImGuiViewportPtr viewport, IntPtr v) var data = (ViewportData*)viewport.RendererUserData; data->SwapChain.Get()->Present(0, 0).ThrowHr(); } + } + + private struct ViewportData : IDisposable + { + public ComPtr SwapChain; + public ComPtr Device; + public ComPtr RenderTarget; + public ComPtr RenderTargetView; + public DXGI_FORMAT RtvFormat; - private struct ViewportData : IDisposable + private int width; + private int height; + + public static ViewportData* Create(IDXGISwapChain* swapChain, ID3D11Device* device) { - public ComPtr SwapChain; - public ComPtr View; + DXGI_SWAP_CHAIN_DESC desc; + swapChain->GetDesc(&desc).ThrowHr(); - public static ViewportData* Create(ComPtr device, HWND hWnd) + var data = (ViewportData*)Marshal.AllocHGlobal(Marshal.SizeOf()); + if (data is null) + throw new OutOfMemoryException(); + *data = new() { - using var dxgiDevice = default(ComPtr); - using var dxgiAdapter = default(ComPtr); - using var dxgiFactory = default(ComPtr); - device.CopyTo(&dxgiDevice).ThrowHr(); - dxgiDevice.Get()->GetAdapter(dxgiAdapter.GetAddressOf()).ThrowHr(); - fixed (Guid* piid = &IID.IID_IDXGIFactory) - dxgiAdapter.Get()->GetParent(piid, (void**)dxgiFactory.GetAddressOf()).ThrowHr(); - - var data = (ViewportData*)Marshal.AllocHGlobal(Marshal.SizeOf()); - *data = default; - try - { - // Create swapchain - var desc = new DXGI_SWAP_CHAIN_DESC - { - BufferDesc = - { - Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM, - }, - SampleDesc = new(1, 0), - BufferUsage = DXGI.DXGI_USAGE_RENDER_TARGET_OUTPUT, - BufferCount = 1, - OutputWindow = hWnd, - Windowed = true, - SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_DISCARD, - }; - dxgiFactory.Get()->CreateSwapChain( - (IUnknown*)dxgiDevice.Get(), - &desc, - data->SwapChain.GetAddressOf()) - .ThrowHr(); - - // Create the render target view - data->CreateView(device); - return data; - } - catch - { - data->Dispose(); - Marshal.FreeHGlobal((nint)data); - throw; - } + SwapChain = new(swapChain), + Device = new(device), + RtvFormat = desc.BufferDesc.Format, + width = (int)desc.BufferDesc.Width, + height = (int)desc.BufferDesc.Height, + }; + try + { + data->CreateRenderTarget(); + return data; + } + catch + { + Free(data); + throw; } + } - public static void Free(ViewportData* instance) + public static ViewportData* Create(ID3D11Device* device, HWND hWnd) + { + using var dxgiFactory = default(ComPtr); + fixed (Guid* piidFactory = &IID.IID_IDXGIFactory) { - instance->Dispose(); - Marshal.FreeHGlobal((nint)instance); +#if DEBUG + DirectX.CreateDXGIFactory2( + DXGI.DXGI_CREATE_FACTORY_DEBUG, + piidFactory, + (void**)dxgiFactory.GetAddressOf()).ThrowHr(); +#else + DirectX.CreateDXGIFactory(piidFactory, (void**)dxgiFactory.GetAddressOf()).ThrowHr(); +#endif } - public void CreateView(ComPtr device) + // Create swapchain + using var swapChain = default(ComPtr); + var desc = new DXGI_SWAP_CHAIN_DESC { - using var buffer = default(ComPtr); - var psw = this.SwapChain; - fixed (Guid* piid = &IID.IID_ID3D11Texture2D) - psw.Get()->GetBuffer(0, piid, (void**)buffer.GetAddressOf()).ThrowHr(); - device.Get()->CreateRenderTargetView( - (ID3D11Resource*)buffer.Get(), - null, - this.View.GetAddressOf()).ThrowHr(); + BufferDesc = + { + Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM, + }, + SampleDesc = new(1, 0), + BufferUsage = DXGI.DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount = 1, + OutputWindow = hWnd, + Windowed = true, + SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_DISCARD, + }; + dxgiFactory.Get()->CreateSwapChain((IUnknown*)device, &desc, swapChain.GetAddressOf()).ThrowHr(); + + return Create(swapChain, device); + } + + public static void Free(ViewportData* instance) + { + instance->Dispose(); + Marshal.FreeHGlobal((nint)instance); + } + + public void ResetBuffers() + { + this.RenderTargetView.Reset(); + this.RenderTarget.Reset(); + } + + public void ResizeBuffers(int newWidth, int newHeight, bool resizeSwapChain) + { + this.width = newWidth; + this.height = newHeight; + if (resizeSwapChain && !this.SwapChain.IsEmpty()) + { + DXGI_SWAP_CHAIN_DESC desc; + this.SwapChain.Get()->GetDesc(&desc).ThrowHr(); + this.SwapChain.Get()->ResizeBuffers( + desc.BufferCount, + (uint)newWidth, + (uint)newHeight, + DXGI_FORMAT.DXGI_FORMAT_UNKNOWN, + desc.Flags).ThrowHr(); } - public void Dispose() + this.CreateRenderTarget(); + } + + public void Dispose() + { + this.SwapChain.Reset(); + this.Device.Reset(); + this.RenderTarget.Reset(); + this.RenderTargetView.Reset(); + } + + private void CreateRenderTarget() + { + if (this.SwapChain.IsEmpty()) { - this.SwapChain.Reset(); - this.View.Reset(); + var desc = new D3D11_TEXTURE2D_DESC + { + Width = (uint)this.width, + Height = (uint)this.height, + MipLevels = 1, + ArraySize = 1, + Format = this.RtvFormat, + SampleDesc = new(1, 0), + Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT, + BindFlags = (uint)D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE, + CPUAccessFlags = 0, + MiscFlags = (uint)D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_SHARED_NTHANDLE, + }; + this.Device.Get()->CreateTexture2D(&desc, null, this.RenderTarget.ReleaseAndGetAddressOf()).ThrowHr(); } + else + { + fixed (Guid* piid = &IID.IID_ID3D11Texture2D) + { + this.SwapChain.Get()->GetBuffer(0, piid, (void**)this.RenderTarget.ReleaseAndGetAddressOf()) + .ThrowHr(); + } + } + + this.Device.Get()->CreateRenderTargetView( + (ID3D11Resource*)this.RenderTarget.Get(), + null, + this.RenderTargetView.GetAddressOf()).ThrowHr(); } } } diff --git a/Dalamud/ImGuiScene/Implementations/Dx11Renderer.cs b/Dalamud/ImGuiScene/Implementations/Dx11Renderer.cs index 93e371d664..46db761925 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx11Renderer.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx11Renderer.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text; using Dalamud.ImGuiScene.Helpers; using Dalamud.Interface.Internal; @@ -35,10 +36,12 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer private readonly ViewportHandler viewportHandler; private readonly nint renderNamePtr; + private bool releaseUnmanagedResourceCalled; + private ComPtr device; private ComPtr context; private ComPtr vertexShader; - private ComPtr defaultTexturePipeline; + private ComPtr defaultPipeline; private ComPtr inputLayout; private ComPtr vertexConstantBuffer; private ComPtr blendState; @@ -49,12 +52,15 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer private int vertexBufferSize; private int indexBufferSize; + private ViewportData* mainViewportData; + /// /// Initializes a new instance of the class. /// + /// The swap chain. /// A pointer to an instance of . /// A pointer to an instance of . - public Dx11Renderer(ID3D11Device* device, ID3D11DeviceContext* context) + public Dx11Renderer(IDXGISwapChain* swapChain, ID3D11Device* device, ID3D11DeviceContext* context) { var io = ImGui.GetIO(); if (ImGui.GetIO().NativePtr->BackendRendererName is not null) @@ -74,6 +80,9 @@ public Dx11Renderer(ID3D11Device* device, ID3D11DeviceContext* context) if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)) this.viewportHandler = new(this); + + this.mainViewportData = ViewportData.Create(swapChain, device); + ImGui.GetPlatformIO().Viewports[0].RendererUserData = (nint)this.mainViewportData; } catch { @@ -103,22 +112,182 @@ public void OnNewFrame() /// public void OnPreResize() { - throw new NotImplementedException(); + ObjectDisposedException.ThrowIf(this.mainViewportData is null, this); + this.mainViewportData->ResetBuffers(); } /// public void OnPostResize(int width, int height) { - throw new NotImplementedException(); + ObjectDisposedException.ThrowIf(this.mainViewportData is null, this); + this.mainViewportData->ResizeBuffers(width, height, false); } /// public void RenderDrawData(ImDrawDataPtr drawData) + { + ObjectDisposedException.ThrowIf(this.mainViewportData is null, this); + var noSwapChain = this.mainViewportData->SwapChain.IsEmpty(); + this.RenderDrawDataInternal(this.mainViewportData->RenderTargetView, drawData, noSwapChain); + } + + /// + /// Creates a new texture pipeline. + /// + /// The pixel shader data. + /// The sampler description. + /// The handle to the new texture pipeline. + public ITexturePipelineWrap CreateTexturePipeline(ReadOnlySpan ps, in D3D11_SAMPLER_DESC samplerDesc) + { + using var tp = default(ComPtr); + tp.Attach(TexturePipeline.Create(this.device, ps, samplerDesc)); + return new TexturePipelineWrap(tp); + } + + /// + public ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap texture) + { + var punk = default(ComPtr); + punk.Attach((IUnknown*)texture.ImGuiHandle); + + using var tw = default(ComPtr); + punk.CopyTo(&tw).ThrowHr(); + + var cp = tw.Get()->CustomPipeline; + return cp is null ? null : new TexturePipelineWrap(cp); + } + + /// + public void SetTexturePipeline(IDalamudTextureWrap texture, ITexturePipelineWrap? pipeline) + { + var punk = default(ComPtr); + punk.Attach((IUnknown*)texture.ImGuiHandle); + + using var tw = default(ComPtr); + punk.CopyTo(&tw).ThrowHr(); + + tw.Get()->CustomPipeline = pipeline switch + { + TexturePipelineWrap tpw => tpw.Data, + null => default, + _ => throw new ArgumentException("Not a compatible texture pipeline wrap.", nameof(pipeline)), + }; + } + + /// + public nint AddDrawCmdUserCallback(IImGuiRenderer.DrawCmdUserCallbackDelegate @delegate) + { + if (this.userCallbacks.FirstOrDefault(x => x.Value == @delegate).Key is not 0 and var key) + return key; + + key = Marshal.GetFunctionPointerForDelegate(@delegate); + this.userCallbacks.Add(key, @delegate); + return key; + } + + /// + public void RemoveDrawCmdUserCallback(IImGuiRenderer.DrawCmdUserCallbackDelegate @delegate) + { + foreach (var key in this.userCallbacks + .Where(x => x.Value == @delegate) + .Select(x => x.Key) + .ToArray()) + { + this.userCallbacks.Remove(key); + } + } + + /// + /// Rebuilds font texture. + /// + public void RebuildFontTexture() + { + foreach (var fontResourceView in this.fontTextures) + fontResourceView.Dispose(); + this.fontTextures.Clear(); + + this.CreateFontsTexture(); + } + + /// + public IDalamudTextureWrap LoadTexture( + ReadOnlySpan data, + int pitch, + int width, + int height, + int format, + [CallerMemberName] string debugName = "") + { + var texd = new D3D11_TEXTURE2D_DESC + { + Width = (uint)width, + Height = (uint)height, + MipLevels = 1, + ArraySize = 1, + Format = (DXGI_FORMAT)format, + SampleDesc = new(1, 0), + Usage = D3D11_USAGE.D3D11_USAGE_IMMUTABLE, + BindFlags = (uint)D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE, + CPUAccessFlags = 0, + MiscFlags = 0, + }; + using var texture = default(ComPtr); + fixed (void* dataPtr = data) + { + var subrdata = new D3D11_SUBRESOURCE_DATA { pSysMem = dataPtr, SysMemPitch = (uint)pitch }; + Marshal.ThrowExceptionForHR(this.device.Get()->CreateTexture2D(&texd, &subrdata, texture.GetAddressOf())); + } + + SetDebugName((ID3D11DeviceChild*)texture.Get(), $"Texture:{debugName}:Tex2D"); + + var viewd = new D3D11_SHADER_RESOURCE_VIEW_DESC + { + Format = texd.Format, + ViewDimension = D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D, + Texture2D = new() { MipLevels = texd.MipLevels }, + }; + using var view = default(ComPtr); + Marshal.ThrowExceptionForHR( + this.device.Get()->CreateShaderResourceView((ID3D11Resource*)texture.Get(), &viewd, view.GetAddressOf())); + + SetDebugName((ID3D11DeviceChild*)view.Get(), $"Texture:{debugName}:SRV"); + + using var texData = default(ComPtr); + texData.Attach(TextureData.Create(view, width, height)); + return new TextureWrap(texData); + } + + private static void SetDebugName(ID3D11DeviceChild* child, string name) + { + var len = Encoding.UTF8.GetByteCount(name); + var buf = stackalloc byte[len + 1]; + Encoding.UTF8.GetBytes(name, new(buf, len + 1)); + buf[len] = 0; + fixed (Guid* pId = &DirectX.WKPDID_D3DDebugObjectName) + child->SetPrivateData(pId, (uint)(len + 1), buf).ThrowHr(); + } + + private void RenderDrawDataInternal( + ID3D11RenderTargetView* renderTargetView, + ImDrawDataPtr drawData, + bool clearRenderTarget) { // Avoid rendering when minimized if (drawData.DisplaySize.X <= 0 || drawData.DisplaySize.Y <= 0) return; + using var oldState = new D3D11DeviceContextStateBackup(this.featureLevel, this.context.Get()); + + // Setup desired DX state + this.SetupRenderState(drawData); + + this.context.Get()->OMSetRenderTargets(1, &renderTargetView, null); + if (clearRenderTarget) + { + var color = default(Vector4); + this.context.Get()->ClearRenderTargetView(renderTargetView, (float*)&color); + } + if (!drawData.Valid || drawData.CmdListsCount == 0) return; @@ -218,11 +387,6 @@ public void RenderDrawData(ImDrawDataPtr drawData) this.context.Get()->Unmap((ID3D11Resource*)this.vertexConstantBuffer.Get(), 0); } - using var oldState = new D3D11DeviceContextStateBackup(this.featureLevel, this.context.Get()); - - // Setup desired DX state - this.SetupRenderState(drawData); - // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) var vertexOffset = 0; @@ -242,11 +406,10 @@ public void RenderDrawData(ImDrawDataPtr drawData) // Bind texture and draw var ptcd = (TextureData*)cmd.TextureId; - using var pipeline = ptcd->CustomPipeline; - if (pipeline.IsEmpty()) - this.defaultTexturePipeline.CopyTo(&pipeline); - - pipeline.Get()->BindTo(this.context); + if (ptcd->CustomPipeline is not null) + ptcd->CustomPipeline->BindTo(this.context); + else + this.defaultPipeline.Get()->BindTo(this.context); var srv = ptcd->ShaderResourceView; this.context.Get()->PSSetShaderResources(0, 1, &srv); @@ -267,130 +430,6 @@ public void RenderDrawData(ImDrawDataPtr drawData) } } - /// - /// Creates a new texture pipeline. Dispose using . - /// - /// The pixel shader data. - /// The sampler description. - /// The handle to the new texture pipeline. - public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D11_SAMPLER_DESC samplerDesc) => - (nint)TexturePipeline.CreateNew(this.device, ps, samplerDesc); - - /// - public nint GetTexturePipeline(nint textureHandle) - { - if (textureHandle == default) - throw new NullReferenceException(); - using var ptcd = default(ComPtr); - ((ComPtr)(IUnknown*)textureHandle).CopyTo(&ptcd).ThrowHr(); - return (nint)ptcd.Get()->CustomPipeline.Get(); - } - - /// - public void SetTexturePipeline(nint textureHandle, nint pipelineHandle) - { - if (textureHandle == default) - throw new NullReferenceException(); - using var ptcd = default(ComPtr); - ((ComPtr)(IUnknown*)textureHandle).CopyTo(&ptcd).ThrowHr(); - - if (pipelineHandle == default) - { - ptcd.Get()->CustomPipeline = default; - } - else - { - using var ppsh = default(ComPtr); - ((ComPtr)(IUnknown*)pipelineHandle).CopyTo(&ppsh).ThrowHr(); - ptcd.Get()->CustomPipeline = ppsh; - } - } - - /// - public void ReleaseTexturePipeline(IntPtr pipelineHandle) - { - if (pipelineHandle == default) - throw new NullReferenceException(); - - // We call Release twice, since type checking effectively involves a call to AddRef. - using var ppsh = default(ComPtr); - ((ComPtr)(IUnknown*)pipelineHandle).CopyTo(&ppsh).ThrowHr(); - ppsh.Get()->Release(); - } - - /// - public nint AddDrawCmdUserCallback(IImGuiRenderer.DrawCmdUserCallbackDelegate @delegate) - { - if (this.userCallbacks.FirstOrDefault(x => x.Value == @delegate).Key is not 0 and var key) - return key; - - key = Marshal.GetFunctionPointerForDelegate(@delegate); - this.userCallbacks.Add(key, @delegate); - return key; - } - - /// - public void RemoveDrawCmdUserCallback(IImGuiRenderer.DrawCmdUserCallbackDelegate @delegate) - { - foreach (var key in this.userCallbacks - .Where(x => x.Value == @delegate) - .Select(x => x.Key) - .ToArray()) - { - this.userCallbacks.Remove(key); - } - } - - /// - /// Rebuilds font texture. - /// - public void RebuildFontTexture() - { - foreach (var fontResourceView in this.fontTextures) - fontResourceView.Dispose(); - this.fontTextures.Clear(); - - this.CreateFontsTexture(); - } - - /// - public IDalamudTextureWrap LoadTexture(ReadOnlySpan data, int pitch, int width, int height, int format) - { - var texd = new D3D11_TEXTURE2D_DESC - { - Width = (uint)width, - Height = (uint)height, - MipLevels = 1, - ArraySize = 1, - Format = (DXGI_FORMAT)format, - SampleDesc = new(1, 0), - Usage = D3D11_USAGE.D3D11_USAGE_IMMUTABLE, - BindFlags = (uint)D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE, - CPUAccessFlags = 0, - MiscFlags = 0, - }; - using var texture = default(ComPtr); - fixed (void* dataPtr = data) - { - var subrdata = new D3D11_SUBRESOURCE_DATA { pSysMem = dataPtr, SysMemPitch = (uint)pitch }; - Marshal.ThrowExceptionForHR(this.device.Get()->CreateTexture2D(&texd, &subrdata, texture.GetAddressOf())); - } - - var viewd = new D3D11_SHADER_RESOURCE_VIEW_DESC - { - Format = texd.Format, - ViewDimension = D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D, - Texture2D = new() { MipLevels = texd.MipLevels }, - }; - using var view = default(ComPtr); - Marshal.ThrowExceptionForHR( - this.device.Get()->CreateShaderResourceView((ID3D11Resource*)texture.Get(), &viewd, view.GetAddressOf())); - - using var texData = default(ComPtr); - texData.Attach(TextureData.CreateNew(view, width, height)); - return new TextureWrap(texData); - } - /// /// Builds fonts as necessary, and uploads the built data onto the GPU.
/// No-op if it has already been done. @@ -424,7 +463,8 @@ private void CreateFontsTexture() width * bytespp, width, height, - (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM); + (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM, + $"Font#{textureIndex}"); io.Fonts.SetTexID(textureIndex, tex.ImGuiHandle); this.fontTextures.Add(tex); } @@ -533,13 +573,13 @@ private void EnsureDeviceObjects() } // Create the default texture pipeline - if (this.defaultTexturePipeline.IsEmpty()) + if (this.defaultPipeline.IsEmpty()) { using var stream = assembly.GetManifestResourceStream("imgui-frag.hlsl.bytes")!; var array = ArrayPool.Shared.Rent((int)stream.Length); var psspan = array.AsSpan(0, (int)stream.Length); stream.ReadExactly(psspan); - this.defaultTexturePipeline.Attach(TexturePipeline.CreateNew(this.device, psspan)); + this.defaultPipeline.Attach(TexturePipeline.Create(this.device, psspan)); ArrayPool.Shared.Return(array); } @@ -616,8 +656,18 @@ private void EnsureDeviceObjects() private void ReleaseUnmanagedResources() { - if (this.device.IsEmpty()) + if (this.releaseUnmanagedResourceCalled) return; + this.releaseUnmanagedResourceCalled = true; + + if (this.mainViewportData is not null) + { + ViewportData.Free(this.mainViewportData); + this.mainViewportData = null; + } + + ImGui.GetPlatformIO().Viewports[0].RendererUserData = nint.Zero; + ImGui.DestroyPlatformWindows(); ImGui.DestroyPlatformWindows(); @@ -637,7 +687,7 @@ private void ReleaseUnmanagedResources() this.device.Reset(); this.context.Reset(); - this.defaultTexturePipeline.Reset(); + this.defaultPipeline.Reset(); this.vertexShader.Reset(); this.inputLayout.Reset(); this.vertexConstantBuffer.Reset(); diff --git a/Dalamud/ImGuiScene/Implementations/Dx11Win32Scene.cs b/Dalamud/ImGuiScene/Implementations/Dx11Win32Scene.cs index 11458f5a50..30018f77c4 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx11Win32Scene.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx11Win32Scene.cs @@ -31,7 +31,6 @@ internal sealed unsafe class Dx11Win32Scene : IWin32Scene private ComPtr swapChain; private ComPtr device; private ComPtr deviceContext; - private ComPtr rtv; private int targetWidth; private int targetHeight; @@ -59,9 +58,6 @@ public Dx11Win32Scene(IDXGISwapChain* swapChain) fixed (Guid* guid = &IID.IID_ID3D11Resource) this.swapChain.Get()->GetBuffer(0, guid, (void**)buffer.GetAddressOf()).ThrowHr(); - fixed (ID3D11RenderTargetView** pp = &this.rtv.GetPinnableReference()) - this.device.Get()->CreateRenderTargetView(buffer.Get(), null, pp).ThrowHr(); - var desc = default(DXGI_SWAP_CHAIN_DESC); this.swapChain.Get()->GetDesc(&desc).ThrowHr(); this.targetWidth = (int)desc.BufferDesc.Width; @@ -75,7 +71,7 @@ public Dx11Win32Scene(IDXGISwapChain* swapChain) ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable; - this.imguiRenderer = new(this.Device, this.DeviceContext); + this.imguiRenderer = new(this.SwapChain, this.Device, this.DeviceContext); this.imguiInput = new(this.WindowHandle); } catch @@ -165,32 +161,19 @@ public void Render() ImGui.Render(); - var prtv = this.rtv.Get(); - this.DeviceContext->OMSetRenderTargets(1, &prtv, null); this.imguiRenderer.RenderDrawData(ImGui.GetDrawData()); - this.DeviceContext->OMSetRenderTargets(0, null, null); ImGui.UpdatePlatformWindows(); ImGui.RenderPlatformWindowsDefault(); } /// - public void OnPreResize() - { - this.rtv.Reset(); - } + public void OnPreResize() => this.imguiRenderer.OnPreResize(); /// public void OnPostResize(int newWidth, int newHeight) { - using var buffer = default(ComPtr); - fixed (Guid* guid = &IID.IID_ID3D11Resource) - this.SwapChain->GetBuffer(0, guid, (void**)buffer.ReleaseAndGetAddressOf()).ThrowHr(); - - using var rtvTemp = default(ComPtr); - this.Device->CreateRenderTargetView(buffer.Get(), null, rtvTemp.GetAddressOf()).ThrowHr(); - this.rtv.Swap(&rtvTemp); - + this.imguiRenderer.OnPostResize(newWidth, newHeight); this.targetWidth = newWidth; this.targetHeight = newHeight; } @@ -234,21 +217,17 @@ public IDalamudTextureWrap LoadImageFormat(ReadOnlySpan data, int pitch, i this.imguiRenderer.LoadTexture(data, pitch, width, height, format); /// - public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D11_SAMPLER_DESC samplerDesc) => + public ITexturePipelineWrap CreateTexturePipeline(ReadOnlySpan ps, in D3D11_SAMPLER_DESC samplerDesc) => this.imguiRenderer.CreateTexturePipeline(ps, samplerDesc); /// - public void SetTexturePipeline(IntPtr textureHandle, IntPtr pipelineHandle) => + public void SetTexturePipeline(IDalamudTextureWrap textureHandle, ITexturePipelineWrap? pipelineHandle) => this.imguiRenderer.SetTexturePipeline(textureHandle, pipelineHandle); /// - public IntPtr GetTexturePipeline(IntPtr textureHandle) => + public ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap textureHandle) => this.imguiRenderer.GetTexturePipeline(textureHandle); - /// - public void ReleaseTexturePipeline(IntPtr pipelineHandle) => - this.imguiRenderer.ReleaseTexturePipeline(pipelineHandle); - /// public bool IsImGuiCursor(nint cursorHandle) => this.imguiInput.IsImGuiCursor(cursorHandle); @@ -329,7 +308,6 @@ private void ReleaseUnmanagedResources() ImGui.DestroyContext(); - this.rtv.Dispose(); this.swapChain.Dispose(); this.deviceContext.Dispose(); this.device.Dispose(); diff --git a/Dalamud/ImGuiScene/Implementations/Dx12OnDx11Win32Scene.cs b/Dalamud/ImGuiScene/Implementations/Dx12OnDx11Win32Scene.cs index b864980d62..547621d127 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx12OnDx11Win32Scene.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx12OnDx11Win32Scene.cs @@ -272,21 +272,17 @@ public IDalamudTextureWrap LoadImageFormat(ReadOnlySpan data, int pitch, i this.scene12.LoadImageFormat(data, pitch, width, height, format); /// - public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER_DESC samplerDesc) => - this.scene12.CreateTexturePipeline(ps, samplerDesc); + public ITexturePipelineWrap CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER_DESC samplerDesc) + => this.scene12.CreateTexturePipeline(ps, samplerDesc); /// - public void SetTexturePipeline(IntPtr textureHandle, IntPtr pipelineHandle) => + public void SetTexturePipeline(IDalamudTextureWrap textureHandle, ITexturePipelineWrap? pipelineHandle) => this.scene12.SetTexturePipeline(textureHandle, pipelineHandle); /// - public IntPtr GetTexturePipeline(IntPtr textureHandle) => + public ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap textureHandle) => this.scene12.GetTexturePipeline(textureHandle); - /// - public void ReleaseTexturePipeline(IntPtr pipelineHandle) => - this.scene12.ReleaseTexturePipeline(pipelineHandle); - private void ReleaseUnmanagedResources() { this.swapChain.Reset(); diff --git a/Dalamud/ImGuiScene/Implementations/Dx12Renderer.Textures.cs b/Dalamud/ImGuiScene/Implementations/Dx12Renderer.Textures.cs index 9c9271c333..d6639fa362 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx12Renderer.Textures.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx12Renderer.Textures.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; +using Dalamud.ImGuiScene.Helpers; using Dalamud.Interface.Internal; using Dalamud.Utility; @@ -32,55 +33,35 @@ private struct TexturePipeline : IUnknown.Interface public static readonly Guid MyGuid = new(0xa29f9ceb, 0xf89f, 0x4f35, 0xbc, 0x1c, 0xa5, 0xf7, 0xdc, 0x8a, 0xf0, 0xff); - private static readonly nint[] Vtbl; - - private void* vtbl; + private UnknownComBase combase; private uint refCount; private ComPtr rootSignature; private ComPtr pipelineState; - static TexturePipeline() - { - Vtbl = GC.AllocateArray(3, true); - Vtbl[0] = (nint)(delegate*)&StaticQueryInterface; - Vtbl[1] = (nint)(delegate*)&StaticAddRef; - Vtbl[2] = (nint)(delegate*)&StaticRelease; - } - public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in MyGuid)); - public static TexturePipeline* CreateNew( - ID3D12Device* device, - DXGI_FORMAT rtvFormat, - ReadOnlySpan vs, - ReadOnlySpan ps) => CreateNew( - device, - rtvFormat, - vs, - ps, - new() + public static TexturePipeline* Create(ID3D12RootSignature* rootSignature, ID3D12PipelineState* pipelineState) + { + var mem = (TexturePipeline*)Marshal.AllocHGlobal(sizeof(TexturePipeline)); + if (mem is null) + throw new OutOfMemoryException(); + *mem = new() { - Filter = D3D12_FILTER.D3D12_FILTER_MIN_MAG_MIP_LINEAR, - AddressU = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, - AddressV = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, - AddressW = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, - MipLODBias = 0, - MaxAnisotropy = 0, - ComparisonFunc = D3D12_COMPARISON_FUNC.D3D12_COMPARISON_FUNC_ALWAYS, - BorderColor = D3D12_STATIC_BORDER_COLOR.D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, - MinLOD = 0, - MaxLOD = 0, - ShaderRegister = 0, - RegisterSpace = 0, - ShaderVisibility = D3D12_SHADER_VISIBILITY.D3D12_SHADER_VISIBILITY_PIXEL, - }); + combase = new(mem, &mem->refCount, &DynamicCast, &FinalReleaseUnmanagedResources), + refCount = 1, + rootSignature = new(rootSignature), + pipelineState = new(pipelineState), + }; + return mem; + } - public static TexturePipeline* CreateNew( + public static TexturePipeline* Create( ID3D12Device* device, DXGI_FORMAT rtvFormat, ReadOnlySpan vs, ReadOnlySpan ps, - in D3D12_STATIC_SAMPLER_DESC samplerDesc) + in D3D12_STATIC_SAMPLER_DESC samplerDesc, + string debugName) { var descRange = new D3D12_DESCRIPTOR_RANGE { @@ -107,7 +88,7 @@ static TexturePipeline() }, }; - using var rootSignatureTemp = default(ComPtr); + using var rootSignature = default(ComPtr); using (var successBlob = default(ComPtr)) using (var errorBlob = default(ComPtr)) { @@ -147,11 +128,14 @@ static TexturePipeline() successBlob.Get()->GetBufferPointer(), successBlob.Get()->GetBufferSize(), piid, - (void**)rootSignatureTemp.GetAddressOf()).ThrowHr(); + (void**)rootSignature.GetAddressOf()).ThrowHr(); } } - var pipelineStateTemp = default(ComPtr); + fixed (void* pName = $"{debugName}:RootSignature") + rootSignature.Get()->SetName((ushort*)pName).ThrowHr(); + + using var pipelineState = default(ComPtr); fixed (void* pvs = vs) fixed (void* pps = ps) fixed (Guid* piidPipelineState = &IID.IID_ID3D12PipelineState) @@ -187,7 +171,7 @@ static TexturePipeline() { NodeMask = 1, PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE.D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, - pRootSignature = rootSignatureTemp, + pRootSignature = rootSignature, SampleMask = uint.MaxValue, NumRenderTargets = 1, RTVFormats = new() { e0 = rtvFormat }, @@ -254,57 +238,67 @@ static TexturePipeline() device->CreateGraphicsPipelineState( &pipelineDesc, piidPipelineState, - (void**)pipelineStateTemp.GetAddressOf()).ThrowHr(); + (void**)pipelineState.GetAddressOf()).ThrowHr(); } - var mem = (TexturePipeline*)Marshal.AllocHGlobal(sizeof(TexturePipeline)); - if (mem is null) - throw new OutOfMemoryException(); - *mem = new() { vtbl = Unsafe.AsPointer(ref Vtbl[0]), refCount = 1 }; - rootSignatureTemp.Swap(ref mem->rootSignature); - pipelineStateTemp.Swap(ref mem->pipelineState); - return mem; + fixed (void* pName = $"{debugName}:PipelineState") + pipelineState.Get()->SetName((ushort*)pName).ThrowHr(); + + return Create(rootSignature, pipelineState); } + public static TexturePipeline* Create( + ID3D12Device* device, + DXGI_FORMAT rtvFormat, + ReadOnlySpan vs, + ReadOnlySpan ps, + string debugName) => Create( + device, + rtvFormat, + vs, + ps, + new() + { + Filter = D3D12_FILTER.D3D12_FILTER_MIN_MAG_MIP_LINEAR, + AddressU = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, + AddressV = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, + AddressW = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, + MipLODBias = 0, + MaxAnisotropy = 0, + ComparisonFunc = D3D12_COMPARISON_FUNC.D3D12_COMPARISON_FUNC_ALWAYS, + BorderColor = D3D12_STATIC_BORDER_COLOR.D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, + MinLOD = 0, + MaxLOD = 0, + ShaderRegister = 0, + RegisterSpace = 0, + ShaderVisibility = D3D12_SHADER_VISIBILITY.D3D12_SHADER_VISIBILITY_PIXEL, + }, + debugName); + public void BindTo(ID3D12GraphicsCommandList* ctx) { ctx->SetPipelineState(this.pipelineState); ctx->SetGraphicsRootSignature(this.rootSignature); } - public HRESULT QueryInterface(Guid* riid, void** ppvObject) - { - if (riid == null) - return E.E_INVALIDARG; - if (ppvObject == null) - return E.E_POINTER; - if (*riid == IID.IID_IUnknown || *riid == MyGuid) - ppvObject[0] = Unsafe.AsPointer(ref this); - else - return E.E_NOINTERFACE; - return S.S_OK; - } - - public uint AddRef() => Interlocked.Increment(ref this.refCount); - - public uint Release() - { - var r = Interlocked.Decrement(ref this.refCount); - if (r != 0) - return r; - - this.rootSignature.Reset(); - this.pipelineState.Reset(); - Marshal.FreeHGlobal((nint)Unsafe.AsPointer(ref this)); - return 0; - } + /// + HRESULT IUnknown.Interface.QueryInterface(Guid* piid, void** ppvObject) => + this.combase.AsPunk->QueryInterface(piid, ppvObject); - private static HRESULT StaticQueryInterface(TexturePipeline* self, Guid* riid, void** ppvObject) => - self->QueryInterface(riid, ppvObject); + /// + uint IUnknown.Interface.AddRef() => this.combase.AsPunk->AddRef(); + + /// + uint IUnknown.Interface.Release() => this.combase.AsPunk->Release(); - private static uint StaticAddRef(TexturePipeline* self) => self->AddRef(); + private static void* DynamicCast(void* self, in Guid guid) => guid == MyGuid ? self : null; - private static uint StaticRelease(TexturePipeline* self) => self->Release(); + private static void FinalReleaseUnmanagedResources(void* pThisVoid) + { + var pThis = (TexturePipeline*)pThisVoid; + pThis->rootSignature.Reset(); + pThis->pipelineState.Reset(); + } } [Guid("f58175a6-37da-4daa-82fd-5993f6847643")] @@ -314,22 +308,12 @@ private struct TextureData : IUnknown.Interface public static readonly Guid MyGuid = new(0xf58175a6, 0x37da, 0x4daa, 0x82, 0xfd, 0x59, 0x93, 0xf6, 0x84, 0x76, 0x43); - private static readonly nint[] Vtbl; - - private void* vtbl; + private UnknownComBase combase; private uint refCount; private ComPtr texture; private ComPtr uploadBuffer; private ComPtr customPipeline; - - static TextureData() - { - Vtbl = GC.AllocateArray(3, true); - Vtbl[0] = (nint)(delegate*)&StaticQueryInterface; - Vtbl[1] = (nint)(delegate*)&StaticAddRef; - Vtbl[2] = (nint)(delegate*)&StaticRelease; - } - + public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in MyGuid)); public DXGI_FORMAT Format { get; private init; } @@ -349,26 +333,26 @@ static TextureData() pResource = this.texture, Subresource = D3D12.D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, StateBefore = D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST, - StateAfter = D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COMMON, + StateAfter = D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, }, }; - public ComPtr CustomPipeline + public TexturePipeline* CustomPipeline { - get => this.customPipeline.IsEmpty() ? default : new(this.customPipeline); + get => this.customPipeline.IsEmpty() ? default : this.customPipeline; set { if (this.customPipeline.Get() == value) return; this.customPipeline.Reset(); - if (!value.IsEmpty()) - value.CopyTo(ref this.customPipeline); + if (value is not null) + this.customPipeline = new(value); } } public ID3D12Resource* Texture => this.texture; - public static TextureData* CreateNew( + public static TextureData* Create( DXGI_FORMAT format, int width, int height, @@ -381,7 +365,7 @@ public ComPtr CustomPipeline throw new OutOfMemoryException(); *mem = new() { - vtbl = Unsafe.AsPointer(ref Vtbl[0]), + combase = new(mem, &mem->refCount, &DynamicCast, &FinalReleaseUnmanagedResources), refCount = 1, texture = new(texture), uploadBuffer = new(uploadBuffer), @@ -424,40 +408,25 @@ public void WriteCopyCommand(ID3D12GraphicsCommandList* cmdList) cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, null); } - public HRESULT QueryInterface(Guid* riid, void** ppvObject) - { - if (riid == null) - return E.E_INVALIDARG; - if (ppvObject == null) - return E.E_POINTER; - if (*riid == IID.IID_IUnknown || *riid == MyGuid) - ppvObject[0] = Unsafe.AsPointer(ref this); - else - return E.E_NOINTERFACE; - return S.S_OK; - } + /// + HRESULT IUnknown.Interface.QueryInterface(Guid* piid, void** ppvObject) => + this.combase.AsPunk->QueryInterface(piid, ppvObject); - public uint AddRef() => Interlocked.Increment(ref this.refCount); + /// + uint IUnknown.Interface.AddRef() => this.combase.AsPunk->AddRef(); + + /// + uint IUnknown.Interface.Release() => this.combase.AsPunk->Release(); - public uint Release() + private static void* DynamicCast(void* self, in Guid guid) => guid == MyGuid ? self : null; + + private static void FinalReleaseUnmanagedResources(void* pThisVoid) { - var r = Interlocked.Decrement(ref this.refCount); - if (r != 0) - return r; - - this.texture.Reset(); - this.uploadBuffer.Reset(); - this.customPipeline.Reset(); - Marshal.FreeHGlobal((nint)Unsafe.AsPointer(ref this)); - return 0; + var pThis = (TextureData*)pThisVoid; + pThis->texture.Reset(); + pThis->uploadBuffer.Reset(); + pThis->customPipeline.Reset(); } - - private static HRESULT StaticQueryInterface(TextureData* self, Guid* riid, void** ppvObject) => - self->QueryInterface(riid, ppvObject); - - private static uint StaticAddRef(TextureData* self) => self->AddRef(); - - private static uint StaticRelease(TextureData* self) => self->Release(); } private class TextureManager : IDisposable @@ -493,6 +462,9 @@ public TextureManager(ID3D12Device* device) NodeMask = 1, }; device->CreateCommandQueue(&queueDesc, piid, (void**)ppQueue).ThrowHr(); + + fixed (void* pName = $"{nameof(TextureManager)}:{nameof(this.commandQueue)}") + this.commandQueue.Get()->SetName((ushort*)pName).ThrowHr(); } fixed (Guid* piid = &IID.IID_ID3D12CommandAllocator) @@ -502,6 +474,9 @@ public TextureManager(ID3D12Device* device) D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COPY, piid, (void**)ppAllocator).ThrowHr(); + + fixed (void* pName = $"{nameof(TextureManager)}:{nameof(this.commandAllocator)}") + this.commandAllocator.Get()->SetName((ushort*)pName).ThrowHr(); } fixed (Guid* piid = &IID.IID_ID3D12GraphicsCommandList) @@ -515,6 +490,9 @@ public TextureManager(ID3D12Device* device) piid, (void**)ppList).ThrowHr(); this.commandList.Get()->Close().ThrowHr(); + + fixed (void* pName = $"{nameof(TextureManager)}:{nameof(this.commandList)}") + this.commandList.Get()->SetName((ushort*)pName).ThrowHr(); } fixed (Guid* piid = &IID.IID_ID3D12Fence) @@ -525,6 +503,9 @@ public TextureManager(ID3D12Device* device) D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, piid, (void**)ppFence).ThrowHr(); + + fixed (void* pName = $"{nameof(TextureManager)}:Fence") + this.fence.Get()->SetName((ushort*)pName).ThrowHr(); } } catch @@ -547,7 +528,8 @@ public TextureWrap CreateTexture( int pitch, int width, int height, - DXGI_FORMAT format) + DXGI_FORMAT format, + string debugName) { uint numRows; ulong cbRow; @@ -591,6 +573,9 @@ public TextureWrap CreateTexture( null, piid, (void**)texture.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"Texture:{debugName}") + texture.Get()->SetName((ushort*)pName).ThrowHr(); } var uploadPitch = ((checked((int)cbRow) + D3D12.D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) - 1) & @@ -628,6 +613,9 @@ public TextureWrap CreateTexture( null, piid, (void**)uploadBuffer.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"Texture:{debugName}:UploadBuffer") + uploadBuffer.Get()->SetName((ushort*)pName).ThrowHr(); } try @@ -650,15 +638,14 @@ public TextureWrap CreateTexture( } using var texData = default(ComPtr); - texData.Attach(TextureData.CreateNew(format, width, height, uploadPitch, texture, uploadBuffer)); - texData.Get()->AddRef(); + texData.Attach(TextureData.Create(format, width, height, uploadPitch, texture, uploadBuffer)); lock (this.uploadListLock) - this.pendingUploads.Add(texData); + this.pendingUploads.Add(new(texData)); return new(texData); } - public void FlushPendingTextureUploads() + public void FlushPendingTextureUploads(ViewportData* transitionWith) { lock (this.uploadListLock) { @@ -666,12 +653,21 @@ public void FlushPendingTextureUploads() this.pendingUploads.Clear(); } - var cmdAlloc = this.commandAllocator.Get(); - var cmdList = this.commandList.Get(); - var cmdQueue = this.commandQueue.Get(); + // copy queue really only supportys copying + var copyCmdAlloc = this.commandAllocator.Get(); + var copyCmdList = this.commandList.Get(); + var copyCmdQueue = this.commandQueue.Get(); + + copyCmdAlloc->Reset().ThrowHr(); + copyCmdList->Reset(copyCmdAlloc, null).ThrowHr(); + + // need to use another queue to change state to pixel shader resource + var transitionCmdAlloc = transitionWith->CurrentViewportFrame.CommandAllocator.Get(); + var transitionCmdList = transitionWith->CommandList.Get(); + var transitionCmdQueue = transitionWith->CommandQueue.Get(); - cmdAlloc->Reset().ThrowHr(); - cmdList->Reset(cmdAlloc, null).ThrowHr(); + transitionCmdAlloc->Reset().ThrowHr(); + transitionCmdList->Reset(transitionCmdAlloc, null).ThrowHr(); var barriers = stackalloc D3D12_RESOURCE_BARRIER[UploadBatchSize]; for (var i = 0; i < this.recycler.Count; i += UploadBatchSize) @@ -680,20 +676,23 @@ public void FlushPendingTextureUploads() var dataSpan = CollectionsMarshal.AsSpan(this.recycler).Slice(i, count); for (var j = 0; j < count; j++) { - dataSpan[j].Get()->WriteCopyCommand(cmdList); + dataSpan[j].Get()->WriteCopyCommand(copyCmdList); barriers[j] = dataSpan[j].Get()->Barrier; } - cmdList->ResourceBarrier((uint)count, barriers); + transitionCmdList->ResourceBarrier((uint)count, barriers); } - cmdList->Close().ThrowHr(); + copyCmdList->Close().ThrowHr(); + transitionCmdList->Close().ThrowHr(); this.fenceValue = Interlocked.Increment(ref this.fenceValue); - cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList**)&cmdList); - cmdQueue->Signal(this.fence, this.fenceValue).ThrowHr(); + copyCmdQueue->ExecuteCommandLists(1, (ID3D12CommandList**)©CmdList); + copyCmdQueue->Signal(this.fence, this.fenceValue).ThrowHr(); this.WaitUploadInternal(); + transitionCmdQueue->ExecuteCommandLists(1, (ID3D12CommandList**)(&transitionCmdList)); + foreach (ref var v in CollectionsMarshal.AsSpan(this.recycler)) { v.Get()->ClearUploadBuffer(); @@ -701,7 +700,7 @@ public void FlushPendingTextureUploads() } this.recycler.Clear(); - this.WaitUploadInternal(); + transitionWith->WaitForPendingOperations(); } private void WaitUploadInternal() @@ -751,48 +750,51 @@ private void ReleaseUnmanagedResources() } } - [SuppressMessage( - "StyleCop.CSharp.MaintainabilityRules", - "SA1401:Fields should be private", - Justification = "Internal")] - private class TextureWrap : IDalamudTextureWrap + private class TexturePipelineWrap : ITexturePipelineWrap { - private ComPtr textureData; + private ComPtr data; + + public TexturePipelineWrap(TexturePipeline* coreData) => this.data = new(coreData); + + ~TexturePipelineWrap() => this.ReleaseUnmanagedResources(); + + public TexturePipeline* Data => + this.IsDisposed ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data.Get(); - public TextureWrap(TextureData* coreData) + public bool IsDisposed => this.data.IsEmpty(); + + public void Dispose() { - coreData->AddRef(); - this.textureData.Attach(coreData); + this.ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); } + public ITexturePipelineWrap Clone() => new TexturePipelineWrap( + this.data.IsEmpty() ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data); + + object ICloneable.Clone() => this.Clone(); + + private void ReleaseUnmanagedResources() => this.data.Reset(); + } + + private class TextureWrap : IDalamudTextureWrap, ICloneable + { + private ComPtr data; + + public TextureWrap(TextureData* coreData) => this.data = new(coreData); + ~TextureWrap() => this.ReleaseUnmanagedResources(); - public nint ImGuiHandle - { - get - { - ObjectDisposedException.ThrowIf(this.textureData.IsEmpty(), this); - return (nint)this.textureData.Get(); - } - } + public TextureData* Data => + this.IsDisposed ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data.Get(); - public int Width - { - get - { - ObjectDisposedException.ThrowIf(this.textureData.IsEmpty(), this); - return this.textureData.Get()->Width; - } - } + public bool IsDisposed => this.data.IsEmpty(); - public int Height - { - get - { - ObjectDisposedException.ThrowIf(this.textureData.IsEmpty(), this); - return this.textureData.Get()->Height; - } - } + public nint ImGuiHandle => (nint)this.Data; + + public int Width => this.Data->Width; + + public int Height => this.Data->Height; public void Dispose() { @@ -800,6 +802,11 @@ public void Dispose() GC.SuppressFinalize(this); } - private void ReleaseUnmanagedResources() => this.textureData.Reset(); + public IDalamudTextureWrap Clone() => + new TextureWrap(this.data.IsEmpty() ? throw new ObjectDisposedException(nameof(TextureWrap)) : this.data); + + object ICloneable.Clone() => this.Clone(); + + private void ReleaseUnmanagedResources() => this.data.Reset(); } } diff --git a/Dalamud/ImGuiScene/Implementations/Dx12Renderer.ViewportHandler.cs b/Dalamud/ImGuiScene/Implementations/Dx12Renderer.ViewportHandler.cs index d968196409..46fe72b87c 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx12Renderer.ViewportHandler.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx12Renderer.ViewportHandler.cs @@ -6,6 +6,7 @@ using System.Threading; using Dalamud.ImGuiScene.Helpers; +using Dalamud.Interface.Utility; using Dalamud.Utility; using ImGuiNET; @@ -87,7 +88,8 @@ private void OnCreateWindow(ImGuiViewportPtr viewport) this.renderer.mainViewportData->Device, (HWND)hWnd, this.renderer.mainViewportData->RtvFormat, - this.renderer.mainViewportData->NumBackBuffers); + this.renderer.mainViewportData->NumBackBuffers, + $"ID#{viewport.ID}"); } private void OnDestroyWindow(ImGuiViewportPtr viewport) @@ -148,12 +150,16 @@ private struct ViewportData : IDisposable private int width; private int height; + private ImVector debugName; + // we're special private ViewportFrame frame0; private ViewportFrame frame1; public ref ViewportFrame CurrentViewportFrame => ref this.Frames[this.FrameIndex]; + public ImVectorWrapper DebugNameVector => new(ref this.debugName); + public Span Frames => new( Unsafe.AsPointer(ref this.frame0), this.NumBackBuffers); @@ -161,7 +167,8 @@ private struct ViewportData : IDisposable public static ViewportData* Create( IDXGISwapChain3* swapChain3, ID3D12Device* device, - ID3D12CommandQueue* commandQueue) + ID3D12CommandQueue* commandQueue, + string debugName) { DXGI_SWAP_CHAIN_DESC desc; swapChain3->GetDesc(&desc).ThrowHr(); @@ -172,14 +179,16 @@ private struct ViewportData : IDisposable desc.BufferDesc.Format, (int)desc.BufferDesc.Width, (int)desc.BufferDesc.Height, - (int)desc.BufferCount); + (int)desc.BufferCount, + debugName); } public static ViewportData* Create( ID3D12Device* device, HWND hWnd, DXGI_FORMAT rtvFormat, - int numBackBuffers) + int numBackBuffers, + string debugName) { // Create command queue. using var commandQueue = default(ComPtr); @@ -195,6 +204,9 @@ private struct ViewportData : IDisposable &queueDesc, piid, (void**)commandQueue.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{debugName}].{nameof(CommandQueue)}") + commandQueue.Get()->SetName((ushort*)pName).ThrowHr(); } // Create swap chain. @@ -239,7 +251,7 @@ private struct ViewportData : IDisposable swapChainTmp.Get()->QueryInterface(piidSwapChain3, (void**)swapChain3.GetAddressOf()).ThrowHr(); } - return Create(swapChain3, device, commandQueue); + return Create(swapChain3, device, commandQueue, debugName); } public static ViewportData* Create( @@ -247,7 +259,8 @@ private struct ViewportData : IDisposable int width, int height, DXGI_FORMAT rtvFormat, - int numBackBuffers) + int numBackBuffers, + string debugName) { // Create command queue. using var commandQueue = default(ComPtr); @@ -263,9 +276,12 @@ private struct ViewportData : IDisposable &queueDesc, piid, (void**)commandQueue.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{debugName}].{nameof(CommandQueue)}") + commandQueue.Get()->SetName((ushort*)pName).ThrowHr(); } - return CreateInternal(null, device, commandQueue, rtvFormat, width, height, numBackBuffers); + return CreateInternal(null, device, commandQueue, rtvFormat, width, height, numBackBuffers, debugName); } public static void Free(ViewportData* instance) @@ -288,6 +304,8 @@ public void Dispose() this.FenceEvent = default; foreach (ref var x in this.Frames) x.Dispose(); + this.DebugNameVector.Clear(); + this.DebugNameVector.SetCapacity(0); } public void Draw(Dx12Renderer renderer, ImDrawDataPtr drawData, bool clearRenderTarget) @@ -407,7 +425,8 @@ public void WaitForPendingOperations() DXGI_FORMAT rtvFormat, int width, int height, - int numBackBuffers) + int numBackBuffers, + string debugName) { if (numBackBuffers < 2) { @@ -423,6 +442,7 @@ public void WaitForPendingOperations() var dataSize = baseDataSize + (numBackBuffers * paddedSize); var data = (ViewportData*)Marshal.AllocHGlobal(dataSize); new Span(data, (int)dataSize).Clear(); + try { // Set up basic information. @@ -431,6 +451,9 @@ public void WaitForPendingOperations() data->RtvFormat = rtvFormat; data->NumBackBuffers = numBackBuffers; data->FrameIndex = data->NumBackBuffers - 1; + data->DebugNameVector.AddRange(debugName.AsSpan()); + for (var i = 0; i < numBackBuffers; i++) + data->Frames[i].Parent = data; // Attach device. device->AddRef(); @@ -456,6 +479,9 @@ public void WaitForPendingOperations() D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_DIRECT, piid, (void**)data->Frames[i].CommandAllocator.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{debugName}].{nameof(CommandList)}[{i}]") + data->Frames[i].CommandAllocator.Get()->SetName((ushort*)pName).ThrowHr(); } } @@ -470,6 +496,9 @@ public void WaitForPendingOperations() piid, (void**)data->CommandList.GetAddressOf()).ThrowHr(); data->CommandList.Get()->Close(); + + fixed (void* pName = $"{nameof(ViewportData)}[{debugName}].{nameof(CommandList)}") + data->CommandList.Get()->SetName((ushort*)pName).ThrowHr(); } // Create fence. @@ -480,6 +509,9 @@ public void WaitForPendingOperations() D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, piid, (void**)data->Fence.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{debugName}].{nameof(Fence)}") + data->Fence.Get()->SetName((ushort*)pName).ThrowHr(); } // Create the render targets. @@ -498,6 +530,9 @@ public void WaitForPendingOperations() piidDescHeap, (void**)data->RtvDescHeap.GetAddressOf()).ThrowHr(); + fixed (void* pName = $"{nameof(ViewportData)}[{debugName}].{nameof(RtvDescHeap)}") + data->RtvDescHeap.Get()->SetName((ushort*)pName).ThrowHr(); + var rtvDescriptorSize = device->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE.D3D12_DESCRIPTOR_HEAP_TYPE_RTV); var rtvHandle = data->RtvDescHeap.Get()->GetCPUDescriptorHandleForHeapStart(); @@ -530,39 +565,36 @@ private void CreateRenderTargets() using var backBuffer = default(ComPtr); if (this.SwapChain.IsEmpty()) { - fixed (Guid* piid = &IID.IID_ID3D12Resource) + var props = new D3D12_HEAP_PROPERTIES + { + Type = D3D12_HEAP_TYPE.D3D12_HEAP_TYPE_DEFAULT, + CPUPageProperty = D3D12_CPU_PAGE_PROPERTY.D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + MemoryPoolPreference = D3D12_MEMORY_POOL.D3D12_MEMORY_POOL_UNKNOWN, + }; + var desc = new D3D12_RESOURCE_DESC { - var props = new D3D12_HEAP_PROPERTIES - { - Type = D3D12_HEAP_TYPE.D3D12_HEAP_TYPE_DEFAULT, - CPUPageProperty = D3D12_CPU_PAGE_PROPERTY.D3D12_CPU_PAGE_PROPERTY_UNKNOWN, - MemoryPoolPreference = D3D12_MEMORY_POOL.D3D12_MEMORY_POOL_UNKNOWN, - }; - var desc = new D3D12_RESOURCE_DESC - { - Dimension = D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE2D, - Alignment = 0, - Width = (ulong)this.width, - Height = (uint)this.height, - DepthOrArraySize = 1, - MipLevels = 1, - Format = this.RtvFormat, - SampleDesc = { Count = 1, Quality = 0 }, - Layout = D3D12_TEXTURE_LAYOUT.D3D12_TEXTURE_LAYOUT_UNKNOWN, - Flags = D3D12_RESOURCE_FLAGS.D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | - D3D12_RESOURCE_FLAGS.D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, - }; - var clearColor = ClearColor; - var clearValue = new D3D12_CLEAR_VALUE(this.RtvFormat, (float*)&clearColor); - this.Device.Get()->CreateCommittedResource( - &props, - D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_SHARED, - &desc, - D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST, - &clearValue, - piid, - (void**)backBuffer.GetAddressOf()).ThrowHr(); - } + Dimension = D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE2D, + Alignment = 0, + Width = (ulong)this.width, + Height = (uint)this.height, + DepthOrArraySize = 1, + MipLevels = 1, + Format = this.RtvFormat, + SampleDesc = { Count = 1, Quality = 0 }, + Layout = D3D12_TEXTURE_LAYOUT.D3D12_TEXTURE_LAYOUT_UNKNOWN, + Flags = D3D12_RESOURCE_FLAGS.D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | + D3D12_RESOURCE_FLAGS.D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, + }; + var clearColor = ClearColor; + var clearValue = new D3D12_CLEAR_VALUE(this.RtvFormat, (float*)&clearColor); + this.Device.Get()->CreateCommittedResource( + &props, + D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_SHARED, + &desc, + D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COMMON, + &clearValue, + piidResource, + (void**)backBuffer.GetAddressOf()).ThrowHr(); } else { @@ -572,6 +604,9 @@ private void CreateRenderTargets() (void**)backBuffer.GetAddressOf()).ThrowHr(); } + fixed (void* pName = $"{nameof(ViewportData)}[{new string(this.DebugNameVector.DataSpan)}].{nameof(frameData.RenderTarget)}") + backBuffer.Get()->SetName((ushort*)pName).ThrowHr(); + this.Device.Get()->CreateRenderTargetView( backBuffer, null, @@ -585,6 +620,8 @@ private void CreateRenderTargets() [StructLayout(LayoutKind.Sequential)] private struct ViewportFrame : IDisposable { + public ViewportData* Parent; + // Buffers used during the rendering of a frame. public ComPtr IndexBuffer; public ComPtr VertexBuffer; @@ -653,6 +690,9 @@ public void EnsureVertexBufferCapacity(ID3D12Device* device, int capacity) null, piid, (void**)this.VertexBuffer.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{new string(this.Parent->DebugNameVector.DataSpan)}].{nameof(this.VertexBuffer)}") + this.VertexBuffer.Get()->SetName((ushort*)pName).ThrowHr(); } } @@ -691,6 +731,9 @@ public void EnsureIndexBufferCapacity(ID3D12Device* device, int capacity) null, piid, (void**)this.IndexBuffer.GetAddressOf()).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{new string(this.Parent->DebugNameVector.DataSpan)}].{nameof(this.IndexBuffer)}") + this.IndexBuffer.Get()->SetName((ushort*)pName).ThrowHr(); } } @@ -735,6 +778,9 @@ public void EnsureHeapCapacity(ID3D12Device* device, int capacity) fixed (Guid* guid = &IID.IID_ID3D12DescriptorHeap) fixed (ID3D12DescriptorHeap** ppHeap = &this.heap.GetPinnableReference()) device->CreateDescriptorHeap(&desc, guid, (void**)ppHeap).ThrowHr(); + + fixed (void* pName = $"{nameof(ViewportData)}[{new string(this.Parent->DebugNameVector.DataSpan)}].{nameof(this.heap)}") + this.heap.Get()->SetName((ushort*)pName).ThrowHr(); this.heapCapacity = newCapacity; } diff --git a/Dalamud/ImGuiScene/Implementations/Dx12Renderer.cs b/Dalamud/ImGuiScene/Implementations/Dx12Renderer.cs index 6e0a16411c..86090305e1 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx12Renderer.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx12Renderer.cs @@ -37,6 +37,8 @@ internal unsafe partial class Dx12Renderer : IImGuiRenderer private readonly ViewportHandler viewportHandler; private readonly nint renderNamePtr; + private bool releaseUnmanagedResourceCalled; + private ComPtr defaultPipeline; private TextureManager? textureManager; private ViewportData* mainViewportData; @@ -71,7 +73,7 @@ public Dx12Renderer(IDXGISwapChain3* swapChain, ID3D12Device* device, ID3D12Comm if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)) this.viewportHandler = new(this); - this.mainViewportData = ViewportData.Create(swapChain, device, commandQueue); + this.mainViewportData = ViewportData.Create(swapChain, device, commandQueue, nameof(this.mainViewportData)); ImGui.GetPlatformIO().Viewports[0].RendererUserData = (nint)this.mainViewportData; } catch @@ -117,7 +119,13 @@ public Dx12Renderer(ID3D12Device* device, DXGI_FORMAT rtvFormat, int numBackBuff if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)) this.viewportHandler = new(this); - this.mainViewportData = ViewportData.Create(device, width, height, rtvFormat, numBackBuffers); + this.mainViewportData = ViewportData.Create( + device, + width, + height, + rtvFormat, + numBackBuffers, + nameof(this.mainViewportData)); ImGui.GetPlatformIO().Viewports[0].RendererUserData = (nint)this.mainViewportData; } catch @@ -178,19 +186,23 @@ public void RenderDrawData(ImDrawDataPtr drawData) { ObjectDisposedException.ThrowIf(this.mainViewportData is null, this); var noSwapChain = this.mainViewportData->SwapChain.IsEmpty(); - this.textureManager?.FlushPendingTextureUploads(); + this.textureManager?.FlushPendingTextureUploads(this.mainViewportData); this.mainViewportData->Draw(this, drawData, noSwapChain); if (noSwapChain) this.mainViewportData->WaitForPendingOperations(); } /// - /// Creates a new texture pipeline. Dispose using . + /// Creates a new texture pipeline. /// /// The pixel shader data. /// The sampler description. + /// Name for debugging. /// The handle to the new texture pipeline. - public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER_DESC samplerDesc) + public ITexturePipelineWrap CreateTexturePipeline( + ReadOnlySpan ps, + in D3D12_STATIC_SAMPLER_DESC samplerDesc, + [CallerMemberName] string debugName = "") { var assembly = Assembly.GetExecutingAssembly(); using var streamVs = assembly.GetManifestResourceStream("imgui-vertex.hlsl.bytes")!; @@ -198,12 +210,16 @@ public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER try { streamVs.ReadExactly(vs, 0, (int)streamVs.Length); - return (nint)TexturePipeline.CreateNew( - this.mainViewportData->Device, - this.mainViewportData->RtvFormat, - vs.AsSpan(0, (int)streamVs.Length), - ps, - samplerDesc); + using var tp = default(ComPtr); + tp.Attach( + TexturePipeline.Create( + this.mainViewportData->Device, + this.mainViewportData->RtvFormat, + vs.AsSpan(0, (int)streamVs.Length), + ps, + samplerDesc, + debugName)); + return new TexturePipelineWrap(tp); } finally { @@ -212,45 +228,33 @@ public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER } /// - public nint GetTexturePipeline(nint textureHandle) + public ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap texture) { - if (textureHandle == default) - throw new NullReferenceException(); - using var ptcd = default(ComPtr); - ((ComPtr)(IUnknown*)textureHandle).CopyTo(&ptcd).ThrowHr(); - return (nint)ptcd.Get()->CustomPipeline.Get(); + var punk = default(ComPtr); + punk.Attach((IUnknown*)texture.ImGuiHandle); + + using var tw = default(ComPtr); + punk.CopyTo(&tw).ThrowHr(); + + var cp = tw.Get()->CustomPipeline; + return cp is null ? null : new TexturePipelineWrap(cp); } /// - public void SetTexturePipeline(nint textureHandle, nint pipelineHandle) + public void SetTexturePipeline(IDalamudTextureWrap texture, ITexturePipelineWrap? pipeline) { - if (textureHandle == default) - throw new NullReferenceException(); - using var ptcd = default(ComPtr); - ((ComPtr)(IUnknown*)textureHandle).CopyTo(&ptcd).ThrowHr(); - - if (pipelineHandle == default) - { - ptcd.Get()->CustomPipeline = default; - } - else - { - using var ppsh = default(ComPtr); - ((ComPtr)(IUnknown*)pipelineHandle).CopyTo(&ppsh).ThrowHr(); - ptcd.Get()->CustomPipeline = ppsh; - } - } + var punk = default(ComPtr); + punk.Attach((IUnknown*)texture.ImGuiHandle); - /// - public void ReleaseTexturePipeline(nint pipelineHandle) - { - if (pipelineHandle == default) - throw new NullReferenceException(); + using var tw = default(ComPtr); + punk.CopyTo(&tw).ThrowHr(); - // We call Release twice, since type checking effectively involves a call to AddRef. - using var ppsh = default(ComPtr); - ((ComPtr)(IUnknown*)pipelineHandle).CopyTo(&ppsh).ThrowHr(); - ppsh.Get()->Release(); + tw.Get()->CustomPipeline = pipeline switch + { + TexturePipelineWrap tpw => tpw.Data, + null => default, + _ => throw new ArgumentException("Not a compatible texture pipeline wrap.", nameof(pipeline)), + }; } /// @@ -294,13 +298,14 @@ public IDalamudTextureWrap LoadTexture( int pitch, int width, int height, - int format) + int format, + [CallerMemberName] string debugName = "") { ObjectDisposedException.ThrowIf(this.mainViewportData is null, this); try { return (this.textureManager ??= new(this.mainViewportData->Device)) - .CreateTexture(data, pitch, width, height, (DXGI_FORMAT)format); + .CreateTexture(data, pitch, width, height, (DXGI_FORMAT)format, debugName); } catch (COMException e) when (e.HResult == unchecked((int)0x887a0005)) { @@ -402,10 +407,10 @@ private void RenderDrawDataInternal(ImDrawDataPtr drawData, ID3D12GraphicsComman // Bind texture and draw var ptcd = (TextureData*)cmd.TextureId; - using var pipeline = ptcd->CustomPipeline; - if (pipeline.IsEmpty()) - this.defaultPipeline.CopyTo(&pipeline); - pipeline.Get()->BindTo(ctx); + if (ptcd->CustomPipeline is not null) + ptcd->CustomPipeline->BindTo(ctx); + else + this.defaultPipeline.Get()->BindTo(ctx); ctx->SetGraphicsRoot32BitConstants(0, 16, &projMtx, 0); frameData.BindResourceUsingHeap(this.mainViewportData->Device, ctx, ptcd->Texture); @@ -460,7 +465,8 @@ private void CreateFontsTexture() width * bytespp, width, height, - (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM); + (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM, + $"Font#{textureIndex}"); io.Fonts.SetTexID(textureIndex, tex.ImGuiHandle); this.fontTextures.Add(tex); } @@ -522,11 +528,12 @@ private void EnsureDeviceObjects() var ps = ArrayPool.Shared.Rent((int)streamPs.Length); streamVs.ReadExactly(vs, 0, (int)streamVs.Length); streamPs.ReadExactly(ps, 0, (int)streamPs.Length); - this.defaultPipeline.Attach(TexturePipeline.CreateNew( + this.defaultPipeline.Attach(TexturePipeline.Create( this.mainViewportData->Device, this.mainViewportData->RtvFormat, vs.AsSpan(0, (int)streamVs.Length), - ps.AsSpan(0, (int)streamPs.Length))); + ps.AsSpan(0, (int)streamPs.Length), + nameof(this.defaultPipeline))); ArrayPool.Shared.Return(vs); ArrayPool.Shared.Return(ps); } @@ -538,6 +545,10 @@ private void EnsureDeviceObjects() private void ReleaseUnmanagedResources() { + if (this.releaseUnmanagedResourceCalled) + return; + this.releaseUnmanagedResourceCalled = true; + if (this.mainViewportData is not null) { ViewportData.Free(this.mainViewportData); diff --git a/Dalamud/ImGuiScene/Implementations/Dx12Win32Scene.cs b/Dalamud/ImGuiScene/Implementations/Dx12Win32Scene.cs index ace33fc827..15e95ef1ad 100644 --- a/Dalamud/ImGuiScene/Implementations/Dx12Win32Scene.cs +++ b/Dalamud/ImGuiScene/Implementations/Dx12Win32Scene.cs @@ -268,21 +268,17 @@ public IDalamudTextureWrap LoadImageFormat(ReadOnlySpan data, int pitch, i } /// - public nint CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER_DESC samplerDesc) => - this.imguiRenderer.CreateTexturePipeline(ps, samplerDesc); + public ITexturePipelineWrap CreateTexturePipeline(ReadOnlySpan ps, in D3D12_STATIC_SAMPLER_DESC samplerDesc) + => this.imguiRenderer.CreateTexturePipeline(ps, samplerDesc); /// - public void SetTexturePipeline(IntPtr textureHandle, IntPtr pipelineHandle) => + public void SetTexturePipeline(IDalamudTextureWrap textureHandle, ITexturePipelineWrap? pipelineHandle) => this.imguiRenderer.SetTexturePipeline(textureHandle, pipelineHandle); /// - public IntPtr GetTexturePipeline(IntPtr textureHandle) => + public ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap textureHandle) => this.imguiRenderer.GetTexturePipeline(textureHandle); - /// - public void ReleaseTexturePipeline(IntPtr pipelineHandle) => - this.imguiRenderer.ReleaseTexturePipeline(pipelineHandle); - /// public bool IsImGuiCursor(nint cursorHandle) => this.imguiInput.IsImGuiCursor(cursorHandle); diff --git a/Dalamud/Interface/Internal/DalamudTextureWrap.cs b/Dalamud/Interface/Internal/DalamudTextureWrap.cs index 7ee7eb34d2..a0ae2c6110 100644 --- a/Dalamud/Interface/Internal/DalamudTextureWrap.cs +++ b/Dalamud/Interface/Internal/DalamudTextureWrap.cs @@ -1,8 +1,5 @@ using System.Numerics; -using TerraFX.Interop.DirectX; -using TerraFX.Interop.Windows; - namespace Dalamud.Interface.Internal; /// @@ -10,6 +7,11 @@ namespace Dalamud.Interface.Internal; /// public interface IDalamudTextureWrap : IDisposable { + /// + /// Gets a value indicating whether this instance of has been disposed. + /// + bool IsDisposed { get; } + /// /// Gets a texture handle suitable for direct use with ImGui functions. /// @@ -45,19 +47,16 @@ public class DalamudTextureWrap : IDalamudTextureWrap /// The pointer to an instance of . internal DalamudTextureWrap(IDalamudTextureWrap inner) => this.inner = inner; - /// - /// Gets the ImGui handle of the texture. - /// + /// + public bool IsDisposed => this.inner.IsDisposed; + + /// public nint ImGuiHandle => this.inner.ImGuiHandle; - /// - /// Gets the width of the texture. - /// + /// public int Width => this.inner.Width; - /// - /// Gets the height of the texture. - /// + /// public int Height => this.inner.Height; /// diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 1fc7dacf77..59dfc8a6ae 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -264,11 +264,55 @@ public IDalamudTextureWrap LoadImage(string filePath) => ?? throw new InvalidOperationException("Scene isn't ready.")); /// - public IDalamudTextureWrap LoadImageFormat(Span data, int pitch, int width, int height, int format) => - new DalamudTextureWrap( + public IDalamudTextureWrap LoadImageFormat(Span data, int pitch, int width, int height, int format) + { + var t = new DalamudTextureWrap( this.scene?.LoadImageFormat(data, pitch, width, height, format) ?? throw new InvalidOperationException("Scene isn't ready.")); + using var tph = this.scene switch + { + Dx12OnDx11Win32Scene a => a.CreateTexturePipeline( + File.ReadAllBytes("Z:/test.fxc"), + new() + { + Filter = D3D12_FILTER.D3D12_FILTER_MIN_MAG_MIP_LINEAR, + AddressU = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, + AddressV = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, + AddressW = D3D12_TEXTURE_ADDRESS_MODE.D3D12_TEXTURE_ADDRESS_MODE_WRAP, + MipLODBias = 0, + MaxAnisotropy = 0, + ComparisonFunc = D3D12_COMPARISON_FUNC.D3D12_COMPARISON_FUNC_ALWAYS, + BorderColor = D3D12_STATIC_BORDER_COLOR.D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, + MinLOD = 0, + MaxLOD = 0, + ShaderRegister = 0, + RegisterSpace = 0, + ShaderVisibility = D3D12_SHADER_VISIBILITY.D3D12_SHADER_VISIBILITY_PIXEL, + }), + Dx11Win32Scene b => b.CreateTexturePipeline( + File.ReadAllBytes("Z:/test.fxc"), + new() + { + Filter = D3D11_FILTER.D3D11_FILTER_MIN_MAG_MIP_LINEAR, + AddressU = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP, + AddressV = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP, + AddressW = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP, + MipLODBias = 0, + MaxAnisotropy = 0, + ComparisonFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS, + MinLOD = 0, + MaxLOD = 0, + }), + _ => null, + }; + + if (tph != default) + this.scene.SetTexturePipeline(t, tph); + + return t; + } + /// /// Sets up a deferred invocation of font rebuilding, before the next render frame. /// diff --git a/Dalamud/Interface/Internal/TextureManager.cs b/Dalamud/Interface/Internal/TextureManager.cs index f814fe710a..2e549886fb 100644 --- a/Dalamud/Interface/Internal/TextureManager.cs +++ b/Dalamud/Interface/Internal/TextureManager.cs @@ -501,7 +501,7 @@ internal TextureManagerTextureWrap(string path, Vector2 extents, TextureManager /// Gets a value indicating whether or not this wrap has already been disposed. /// If true, the handle may be invalid. /// - internal bool IsDisposed { get; private set; } + public bool IsDisposed { get; private set; } /// public void Dispose() diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs index dcae6e6893..adab393579 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs @@ -125,7 +125,7 @@ private void DrawIcon(int iconId) if (Service.Get().GetIcon((uint)iconId) is { } texture) { - ImGui.Image(texture.ImGuiHandle, this.iconSize); + ImGui.Image(texture.ImGuiHandle, this.iconSize, new(1, 0), new(2, 1)); // If we have the option to show a tooltip image, draw the image, but make sure it's not too big. if (ImGui.IsItemHovered() && this.showTooltipImage) diff --git a/Dalamud/Interface/Utility/ImVectorWrapper.cs b/Dalamud/Interface/Utility/ImVectorWrapper.cs index b4b0ea8668..86c73a6eb6 100644 --- a/Dalamud/Interface/Utility/ImVectorWrapper.cs +++ b/Dalamud/Interface/Utility/ImVectorWrapper.cs @@ -408,7 +408,7 @@ public void AddRange(IEnumerable items) } /// - public void AddRange(Span items) + public void AddRange(ReadOnlySpan items) { this.EnsureCapacityExponential(this.LengthUnsafe + items.Length); foreach (var item in items) @@ -572,7 +572,7 @@ public void InsertRange(int index, IEnumerable items) } /// - public void InsertRange(int index, Span items) + public void InsertRange(int index, ReadOnlySpan items) { this.EnsureCapacityExponential(this.LengthUnsafe + items.Length); var num = this.LengthUnsafe - index;