diff --git a/Dalamud/Interface/Textures/ForwardingTextureWrap.cs b/Dalamud/Interface/Textures/ForwardingTextureWrap.cs new file mode 100644 index 000000000..c398fc55c --- /dev/null +++ b/Dalamud/Interface/Textures/ForwardingTextureWrap.cs @@ -0,0 +1,81 @@ +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; + +using Dalamud.Interface.Internal; +using Dalamud.Interface.Textures.Internal; + +using TerraFX.Interop.Windows; + +namespace Dalamud.Interface.Textures; + +/// Base class for implementations of that forwards to another. +public abstract class ForwardingTextureWrap : IDalamudTextureWrap +{ + /// + public IntPtr ImGuiHandle + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.GetWrap().ImGuiHandle; + } + + /// + public int Width + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.GetWrap().Width; + } + + /// + public int Height + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.GetWrap().Height; + } + + /// + public Vector2 Size + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(this.Width, this.Height); + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + public virtual 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); + } + + /// + public override string ToString() => $"{this.GetType()}({(this.TryGetWrap(out var wrap) ? wrap : null)})"; + + /// Called on . + /// true if called from . + /// + /// Base implementation will not dispose the result of . + /// If you need to implement a finalizer, then make it call this function with false. + /// + protected virtual void Dispose(bool disposing) + { + } + + /// Gets the inner wrap. + /// The inner wrap. + /// true if not disposed and is available. + protected abstract bool TryGetWrap([NotNullWhen(true)] out IDalamudTextureWrap? wrap); + + /// Gets the inner wrap. + /// The inner wrap. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected IDalamudTextureWrap GetWrap() => + this.TryGetWrap(out var wrap) ? wrap : throw new ObjectDisposedException(this.GetType().Name); +} diff --git a/Dalamud/Interface/Textures/IDalamudTextureWrap.cs b/Dalamud/Interface/Textures/IDalamudTextureWrap.cs index dad453cb9..c51a69f06 100644 --- a/Dalamud/Interface/Textures/IDalamudTextureWrap.cs +++ b/Dalamud/Interface/Textures/IDalamudTextureWrap.cs @@ -1,5 +1,6 @@ using System.Numerics; +using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.Internal; using TerraFX.Interop.Windows; @@ -12,6 +13,8 @@ namespace Dalamud.Interface.Internal; /// Base TextureWrap interface for all Dalamud-owned texture wraps. /// Used to avoid referencing ImGuiScene. /// +/// If you want to implement this, see if you're actually wrapping an existing instance of +/// ; if you are, then use . public interface IDalamudTextureWrap : IDisposable { /// Gets a texture handle suitable for direct use with ImGui functions. diff --git a/Dalamud/Interface/Textures/Internal/DisposeSuppressingTextureWrap.cs b/Dalamud/Interface/Textures/Internal/DisposeSuppressingTextureWrap.cs index 17a88e270..88d8d9ca0 100644 --- a/Dalamud/Interface/Textures/Internal/DisposeSuppressingTextureWrap.cs +++ b/Dalamud/Interface/Textures/Internal/DisposeSuppressingTextureWrap.cs @@ -2,31 +2,19 @@ using Dalamud.Interface.Internal; namespace Dalamud.Interface.Textures.Internal; -/// -/// A texture wrap that ignores calls. -/// -internal sealed class DisposeSuppressingTextureWrap : IDalamudTextureWrap +/// A texture wrap that ignores calls. +internal class DisposeSuppressingTextureWrap : ForwardingTextureWrap { private readonly IDalamudTextureWrap innerWrap; - /// - /// Initializes a new instance of the class. - /// + /// Initializes a new instance of the class. /// The inner wrap. public DisposeSuppressingTextureWrap(IDalamudTextureWrap wrap) => this.innerWrap = wrap; /// - public IntPtr ImGuiHandle => this.innerWrap.ImGuiHandle; - - /// - public int Width => this.innerWrap.Width; - - /// - public int Height => this.innerWrap.Height; - - /// - public void Dispose() + protected override bool TryGetWrap(out IDalamudTextureWrap? wrap) { - // suppressed + wrap = this.innerWrap; + return true; } } diff --git a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs index f730637e4..4a6289ceb 100644 --- a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs @@ -363,46 +363,35 @@ internal abstract class SharedImmediateTexture } } - private sealed class NotOwnedTextureWrap : IDalamudTextureWrap + /// Same with , but with a custom implementation of + /// . + private sealed class NotOwnedTextureWrap : DisposeSuppressingTextureWrap { - private readonly IDalamudTextureWrap innerWrap; private readonly IRefCountable owner; /// Initializes a new instance of the class. /// The inner wrap. /// The reference counting owner. public NotOwnedTextureWrap(IDalamudTextureWrap wrap, IRefCountable owner) + : base(wrap) { - this.innerWrap = wrap; this.owner = owner; } /// - public IntPtr ImGuiHandle => this.innerWrap.ImGuiHandle; - - /// - public int Width => this.innerWrap.Width; - - /// - public int Height => this.innerWrap.Height; - - /// - public IDalamudTextureWrap CreateWrapSharingLowLevelResource() + public override IDalamudTextureWrap CreateWrapSharingLowLevelResource() { + var wrap = this.GetWrap(); this.owner.AddRef(); - return new RefCountableWrappingTextureWrap(this.innerWrap, this.owner); - } - - /// - public void Dispose() - { + return new RefCountableWrappingTextureWrap(wrap, this.owner); } /// public override string ToString() => $"{nameof(NotOwnedTextureWrap)}({this.owner})"; } - private sealed class RefCountableWrappingTextureWrap : IDalamudTextureWrap + /// Reference counting texture wrap, to be used with . + private sealed class RefCountableWrappingTextureWrap : ForwardingTextureWrap { private IDalamudTextureWrap? innerWrap; private IRefCountable? owner; @@ -416,22 +405,11 @@ internal abstract class SharedImmediateTexture this.owner = owner; } - ~RefCountableWrappingTextureWrap() => this.Dispose(); + /// Finalizes an instance of the class. + ~RefCountableWrappingTextureWrap() => this.Dispose(false); /// - public IntPtr ImGuiHandle => this.InnerWrapNonDisposed.ImGuiHandle; - - /// - public int Width => this.InnerWrapNonDisposed.Width; - - /// - public int Height => this.InnerWrapNonDisposed.Height; - - private IDalamudTextureWrap InnerWrapNonDisposed => - this.innerWrap ?? throw new ObjectDisposedException(nameof(RefCountableWrappingTextureWrap)); - - /// - public IDalamudTextureWrap CreateWrapSharingLowLevelResource() + public override IDalamudTextureWrap CreateWrapSharingLowLevelResource() { var ownerCopy = this.owner; var wrapCopy = this.innerWrap; @@ -443,7 +421,13 @@ internal abstract class SharedImmediateTexture } /// - public void Dispose() + public override string ToString() => $"{nameof(RefCountableWrappingTextureWrap)}({this.owner})"; + + /// + protected override bool TryGetWrap(out IDalamudTextureWrap? wrap) => (wrap = this.innerWrap) is not null; + + /// + protected override void Dispose(bool disposing) { while (true) { @@ -455,32 +439,22 @@ internal abstract class SharedImmediateTexture // Note: do not dispose this; life of the wrap is managed by the owner. this.innerWrap = null; ownerCopy.Release(); - GC.SuppressFinalize(this); } } - - /// - public override string ToString() => $"{nameof(RefCountableWrappingTextureWrap)}({this.owner})"; } + /// A texture wrap that revives and waits for the underlying texture as needed on every access. [Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)] - private sealed class AvailableOnAccessTextureWrap : IDalamudTextureWrap + private sealed class AvailableOnAccessTextureWrap : ForwardingTextureWrap { private readonly SharedImmediateTexture inner; + /// Initializes a new instance of the class. + /// The shared texture. public AvailableOnAccessTextureWrap(SharedImmediateTexture inner) => this.inner = inner; /// - public IntPtr ImGuiHandle => this.WaitGet().ImGuiHandle; - - /// - public int Width => this.WaitGet().Width; - - /// - public int Height => this.WaitGet().Height; - - /// - public IDalamudTextureWrap CreateWrapSharingLowLevelResource() + public override IDalamudTextureWrap CreateWrapSharingLowLevelResource() { this.inner.AddRef(); try @@ -506,22 +480,18 @@ internal abstract class SharedImmediateTexture } } - /// - public void Dispose() - { - // ignore - } - /// public override string ToString() => $"{nameof(AvailableOnAccessTextureWrap)}({this.inner})"; - private IDalamudTextureWrap WaitGet() + /// + protected override bool TryGetWrap(out IDalamudTextureWrap? wrap) { if (this.inner.TryGetWrapCore(out var t, out _)) - return t; + wrap = t; this.inner.UnderlyingWrap?.Wait(); - return this.inner.nonOwningWrap ?? Service.Get().Empty4X4; + wrap = this.inner.nonOwningWrap ?? Service.Get().Empty4X4; + return true; } } } diff --git a/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs b/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs index ca914a234..272a2d8ee 100644 --- a/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs +++ b/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs @@ -195,6 +195,7 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos _ = this.FirstUpdateTask.Exception; this.tex.Reset(); this.srv.Reset(); + this.rtv.Reset(); } private static unsafe ComPtr GetImGuiViewportBackBuffer(uint viewportId)