From f6be80a5fb309a0aaf64feac786d7f11944dabeb Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Sun, 25 Feb 2024 21:21:50 +0900 Subject: [PATCH 1/2] Make IDalamudTextureWrap ICloneable --- .../Interface/Internal/DalamudTextureWrap.cs | 33 +------- .../Interface/Internal/IDalamudTextureWrap.cs | 47 +++++++++++ .../Interface/Internal/InterfaceManager.cs | 4 +- .../Interface/Internal/UnknownTextureWrap.cs | 77 +++++++++++++++++++ .../Windows/Data/Widgets/TexWidget.cs | 4 + Dalamud/Utility/IDeferredDisposable.cs | 12 +++ 6 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 Dalamud/Interface/Internal/IDalamudTextureWrap.cs create mode 100644 Dalamud/Interface/Internal/UnknownTextureWrap.cs create mode 100644 Dalamud/Utility/IDeferredDisposable.cs diff --git a/Dalamud/Interface/Internal/DalamudTextureWrap.cs b/Dalamud/Interface/Internal/DalamudTextureWrap.cs index 9737d9f7b..b49c6f07b 100644 --- a/Dalamud/Interface/Internal/DalamudTextureWrap.cs +++ b/Dalamud/Interface/Internal/DalamudTextureWrap.cs @@ -1,41 +1,14 @@ -using System.Numerics; +using Dalamud.Utility; using ImGuiScene; namespace Dalamud.Interface.Internal; -/// -/// Base TextureWrap interface for all Dalamud-owned texture wraps. -/// Used to avoid referencing ImGuiScene. -/// -public interface IDalamudTextureWrap : IDisposable -{ - /// - /// Gets a texture handle suitable for direct use with ImGui functions. - /// - IntPtr ImGuiHandle { get; } - - /// - /// Gets the width of the texture. - /// - int Width { get; } - - /// - /// Gets the height of the texture. - /// - int Height { get; } - - /// - /// Gets the size vector of the texture using Width, Height. - /// - Vector2 Size => new(this.Width, this.Height); -} - /// /// Safety harness for ImGuiScene textures that will defer destruction until /// the end of the frame. /// -public class DalamudTextureWrap : IDalamudTextureWrap +public class DalamudTextureWrap : IDalamudTextureWrap, IDeferredDisposable { private readonly TextureWrap wrappedWrap; @@ -83,7 +56,7 @@ public class DalamudTextureWrap : IDalamudTextureWrap /// /// Actually dispose the wrapped texture. /// - internal void RealDispose() + void IDeferredDisposable.RealDispose() { this.wrappedWrap.Dispose(); } diff --git a/Dalamud/Interface/Internal/IDalamudTextureWrap.cs b/Dalamud/Interface/Internal/IDalamudTextureWrap.cs new file mode 100644 index 000000000..60d96534d --- /dev/null +++ b/Dalamud/Interface/Internal/IDalamudTextureWrap.cs @@ -0,0 +1,47 @@ +using System.Numerics; + +using TerraFX.Interop.Windows; + +namespace Dalamud.Interface.Internal; + +/// +/// Base TextureWrap interface for all Dalamud-owned texture wraps. +/// Used to avoid referencing ImGuiScene. +/// +public interface IDalamudTextureWrap : IDisposable, ICloneable +{ + /// + /// Gets a texture handle suitable for direct use with ImGui functions. + /// + IntPtr ImGuiHandle { get; } + + /// + /// Gets the width of the texture. + /// + int Width { get; } + + /// + /// Gets the height of the texture. + /// + int Height { get; } + + /// + /// Gets the size vector of the texture using Width, Height. + /// + Vector2 Size => new(this.Width, this.Height); + + /// + /// Creates a new reference to this texture wrap. + /// + /// The new reference to this texture wrap. + /// The default implementation will treat as an . + new unsafe IDalamudTextureWrap Clone() + { + // Dalamud specific: IDalamudTextureWrap always points to an ID3D11ShaderResourceView. + var handle = (IUnknown*)this.ImGuiHandle; + return new UnknownTextureWrap(handle, this.Width, this.Height, true); + } + + /// + object ICloneable.Clone() => this.Clone(); +} diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 6d93b4bd7..3db799be0 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -62,7 +62,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f; - private readonly ConcurrentBag deferredDisposeTextures = new(); + private readonly ConcurrentBag deferredDisposeTextures = new(); private readonly ConcurrentBag deferredDisposeImFontLockeds = new(); [ServiceManager.ServiceDependency] @@ -402,7 +402,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// Enqueue a texture to be disposed at the end of the frame. /// /// The texture. - public void EnqueueDeferredDispose(DalamudTextureWrap wrap) + public void EnqueueDeferredDispose(IDeferredDisposable wrap) { this.deferredDisposeTextures.Add(wrap); } diff --git a/Dalamud/Interface/Internal/UnknownTextureWrap.cs b/Dalamud/Interface/Internal/UnknownTextureWrap.cs new file mode 100644 index 000000000..41164f2c3 --- /dev/null +++ b/Dalamud/Interface/Internal/UnknownTextureWrap.cs @@ -0,0 +1,77 @@ +using System.Threading; + +using Dalamud.Utility; + +using TerraFX.Interop.Windows; + +namespace Dalamud.Interface.Internal; + +/// +/// A texture wrap that is created by cloning the underlying . +/// +internal sealed unsafe class UnknownTextureWrap : IDalamudTextureWrap, IDeferredDisposable +{ + private IntPtr imGuiHandle; + + /// + /// Initializes a new instance of the class. + /// + /// The pointer to that is suitable for use with + /// . + /// The width of the texture. + /// The height of the texture. + /// If true, call . + public UnknownTextureWrap(IUnknown* unknown, int width, int height, bool callAddRef) + { + ObjectDisposedException.ThrowIf(unknown is null, typeof(IUnknown)); + this.imGuiHandle = (nint)unknown; + this.Width = width; + this.Height = height; + if (callAddRef) + unknown->AddRef(); + } + + /// + /// Finalizes an instance of the class. + /// + ~UnknownTextureWrap() => this.Dispose(false); + + /// + public nint ImGuiHandle => + this.imGuiHandle == nint.Zero + ? throw new ObjectDisposedException(nameof(UnknownTextureWrap)) + : this.imGuiHandle; + + /// + public int Width { get; } + + /// + public int Height { get; } + + /// + /// Queue the texture to be disposed once the frame ends. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Actually dispose the wrapped texture. + /// + void IDeferredDisposable.RealDispose() + { + var handle = Interlocked.Exchange(ref this.imGuiHandle, nint.Zero); + if (handle != nint.Zero) + ((IUnknown*)handle)->Release(); + } + + private void Dispose(bool disposing) + { + if (disposing) + Service.GetNullable()?.EnqueueDeferredDispose(this); + else + ((IDeferredDisposable)this).RealDispose(); + } +} diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs index 0cbc401e7..173e5409a 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs @@ -119,6 +119,10 @@ internal class TexWidget : IDataWindowWidget if (ImGui.Button($"X##{i}")) toRemove = tex; + + ImGui.SameLine(); + if (ImGui.Button($"Clone##{i}")) + this.addedTextures.Add(tex.Clone()); } } diff --git a/Dalamud/Utility/IDeferredDisposable.cs b/Dalamud/Utility/IDeferredDisposable.cs new file mode 100644 index 000000000..41a7dd8d3 --- /dev/null +++ b/Dalamud/Utility/IDeferredDisposable.cs @@ -0,0 +1,12 @@ +namespace Dalamud.Utility; + +/// +/// An extension of which makes queue +/// to be called at a later time. +/// +internal interface IDeferredDisposable : IDisposable +{ + /// Actually dispose the object. + /// Not to be called from the code that uses the end object. + void RealDispose(); +} From 9629a555be670fe87db8c2d51bceb285fe44776d Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Mon, 26 Feb 2024 03:20:28 +0900 Subject: [PATCH 2/2] Rename to CreateWrapSharingLowLevelResource --- .../Interface/Internal/IDalamudTextureWrap.cs | 22 +++++++++++++------ .../Windows/Data/Widgets/TexWidget.cs | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Dalamud/Interface/Internal/IDalamudTextureWrap.cs b/Dalamud/Interface/Internal/IDalamudTextureWrap.cs index 60d96534d..8e2e56c26 100644 --- a/Dalamud/Interface/Internal/IDalamudTextureWrap.cs +++ b/Dalamud/Interface/Internal/IDalamudTextureWrap.cs @@ -8,7 +8,7 @@ namespace Dalamud.Interface.Internal; /// Base TextureWrap interface for all Dalamud-owned texture wraps. /// Used to avoid referencing ImGuiScene. /// -public interface IDalamudTextureWrap : IDisposable, ICloneable +public interface IDalamudTextureWrap : IDisposable { /// /// Gets a texture handle suitable for direct use with ImGui functions. @@ -31,17 +31,25 @@ public interface IDalamudTextureWrap : IDisposable, ICloneable Vector2 Size => new(this.Width, this.Height); /// - /// Creates a new reference to this texture wrap. + /// Creates a new reference to the resource being pointed by this instance of . /// /// The new reference to this texture wrap. - /// The default implementation will treat as an . - new unsafe IDalamudTextureWrap Clone() + /// + /// On calling this function, a new instance of will be returned, but with + /// the same . The new instance must be d, as the backing + /// resource will stay alive until all the references are released. The old instance may be disposed as needed, + /// once this function returns; the new instance will stay alive regardless of whether the old instance has been + /// disposed.
+ /// Primary purpose of this function is to share textures across plugin boundaries. When texture wraps get passed + /// across plugin boundaries for use for an indeterminate duration, the receiver should call this function to + /// obtain a new reference to the texture received, so that it gets its own "copy" of the texture and the caller + /// may dispose the texture anytime without any care for the receiver.
+ /// The default implementation will treat as an . + ///
+ unsafe IDalamudTextureWrap CreateWrapSharingLowLevelResource() { // Dalamud specific: IDalamudTextureWrap always points to an ID3D11ShaderResourceView. var handle = (IUnknown*)this.ImGuiHandle; return new UnknownTextureWrap(handle, this.Width, this.Height, true); } - - /// - object ICloneable.Clone() => this.Clone(); } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs index 173e5409a..8d6879ac1 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs @@ -122,7 +122,7 @@ internal class TexWidget : IDataWindowWidget ImGui.SameLine(); if (ImGui.Button($"Clone##{i}")) - this.addedTextures.Add(tex.Clone()); + this.addedTextures.Add(tex.CreateWrapSharingLowLevelResource()); } }