Add ForwardingTextureWrap

This commit is contained in:
Soreepeong 2024-03-03 22:24:15 +09:00
parent 6c8c42ca05
commit 5fd7457df4
5 changed files with 120 additions and 77 deletions

View file

@ -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;
/// <summary>Base class for implementations of <see cref="IDalamudTextureWrap"/> that forwards to another.</summary>
public abstract class ForwardingTextureWrap : IDalamudTextureWrap
{
/// <inheritdoc/>
public IntPtr ImGuiHandle
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.GetWrap().ImGuiHandle;
}
/// <inheritdoc/>
public int Width
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.GetWrap().Width;
}
/// <inheritdoc/>
public int Height
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.GetWrap().Height;
}
/// <inheritdoc/>
public Vector2 Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(this.Width, this.Height);
}
/// <inheritdoc/>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public override string ToString() => $"{this.GetType()}({(this.TryGetWrap(out var wrap) ? wrap : null)})";
/// <summary>Called on <see cref="IDisposable.Dispose"/>.</summary>
/// <param name="disposing"><c>true</c> if called from <see cref="IDisposable.Dispose"/>.</param>
/// <remarks>
/// <para>Base implementation will not dispose the result of <see cref="TryGetWrap"/>.</para>
/// <para>If you need to implement a finalizer, then make it call this function with <c>false</c>.</para>
/// </remarks>
protected virtual void Dispose(bool disposing)
{
}
/// <summary>Gets the inner wrap.</summary>
/// <param name="wrap">The inner wrap.</param>
/// <returns><c>true</c> if not disposed and <paramref name="wrap"/> is available.</returns>
protected abstract bool TryGetWrap([NotNullWhen(true)] out IDalamudTextureWrap? wrap);
/// <summary>Gets the inner wrap.</summary>
/// <returns>The inner wrap.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected IDalamudTextureWrap GetWrap() =>
this.TryGetWrap(out var wrap) ? wrap : throw new ObjectDisposedException(this.GetType().Name);
}

View file

@ -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.
/// </summary>
/// <remarks>If you want to implement this, see if you're actually wrapping an existing instance of
/// <see cref="IDalamudTextureWrap"/>; if you are, then use <see cref="ForwardingTextureWrap"/>.</remarks>
public interface IDalamudTextureWrap : IDisposable
{
/// <summary>Gets a texture handle suitable for direct use with ImGui functions.</summary>

View file

@ -2,31 +2,19 @@ using Dalamud.Interface.Internal;
namespace Dalamud.Interface.Textures.Internal;
/// <summary>
/// A texture wrap that ignores <see cref="IDisposable.Dispose"/> calls.
/// </summary>
internal sealed class DisposeSuppressingTextureWrap : IDalamudTextureWrap
/// <summary>A texture wrap that ignores <see cref="IDisposable.Dispose"/> calls.</summary>
internal class DisposeSuppressingTextureWrap : ForwardingTextureWrap
{
private readonly IDalamudTextureWrap innerWrap;
/// <summary>
/// Initializes a new instance of the <see cref="DisposeSuppressingTextureWrap"/> class.
/// </summary>
/// <summary>Initializes a new instance of the <see cref="DisposeSuppressingTextureWrap"/> class.</summary>
/// <param name="wrap">The inner wrap.</param>
public DisposeSuppressingTextureWrap(IDalamudTextureWrap wrap) => this.innerWrap = wrap;
/// <inheritdoc/>
public IntPtr ImGuiHandle => this.innerWrap.ImGuiHandle;
/// <inheritdoc/>
public int Width => this.innerWrap.Width;
/// <inheritdoc/>
public int Height => this.innerWrap.Height;
/// <inheritdoc/>
public void Dispose()
protected override bool TryGetWrap(out IDalamudTextureWrap? wrap)
{
// suppressed
wrap = this.innerWrap;
return true;
}
}

View file

@ -363,46 +363,35 @@ internal abstract class SharedImmediateTexture
}
}
private sealed class NotOwnedTextureWrap : IDalamudTextureWrap
/// <summary>Same with <see cref="DisposeSuppressingTextureWrap"/>, but with a custom implementation of
/// <see cref="CreateWrapSharingLowLevelResource"/>.</summary>
private sealed class NotOwnedTextureWrap : DisposeSuppressingTextureWrap
{
private readonly IDalamudTextureWrap innerWrap;
private readonly IRefCountable owner;
/// <summary>Initializes a new instance of the <see cref="NotOwnedTextureWrap"/> class.</summary>
/// <param name="wrap">The inner wrap.</param>
/// <param name="owner">The reference counting owner.</param>
public NotOwnedTextureWrap(IDalamudTextureWrap wrap, IRefCountable owner)
: base(wrap)
{
this.innerWrap = wrap;
this.owner = owner;
}
/// <inheritdoc/>
public IntPtr ImGuiHandle => this.innerWrap.ImGuiHandle;
/// <inheritdoc/>
public int Width => this.innerWrap.Width;
/// <inheritdoc/>
public int Height => this.innerWrap.Height;
/// <inheritdoc/>
public IDalamudTextureWrap CreateWrapSharingLowLevelResource()
public override IDalamudTextureWrap CreateWrapSharingLowLevelResource()
{
var wrap = this.GetWrap();
this.owner.AddRef();
return new RefCountableWrappingTextureWrap(this.innerWrap, this.owner);
}
/// <inheritdoc/>
public void Dispose()
{
return new RefCountableWrappingTextureWrap(wrap, this.owner);
}
/// <inheritdoc/>
public override string ToString() => $"{nameof(NotOwnedTextureWrap)}({this.owner})";
}
private sealed class RefCountableWrappingTextureWrap : IDalamudTextureWrap
/// <summary>Reference counting texture wrap, to be used with <see cref="RentAsync"/>.</summary>
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();
/// <summary>Finalizes an instance of the <see cref="RefCountableWrappingTextureWrap"/> class.</summary>
~RefCountableWrappingTextureWrap() => this.Dispose(false);
/// <inheritdoc/>
public IntPtr ImGuiHandle => this.InnerWrapNonDisposed.ImGuiHandle;
/// <inheritdoc/>
public int Width => this.InnerWrapNonDisposed.Width;
/// <inheritdoc/>
public int Height => this.InnerWrapNonDisposed.Height;
private IDalamudTextureWrap InnerWrapNonDisposed =>
this.innerWrap ?? throw new ObjectDisposedException(nameof(RefCountableWrappingTextureWrap));
/// <inheritdoc/>
public IDalamudTextureWrap CreateWrapSharingLowLevelResource()
public override IDalamudTextureWrap CreateWrapSharingLowLevelResource()
{
var ownerCopy = this.owner;
var wrapCopy = this.innerWrap;
@ -443,7 +421,13 @@ internal abstract class SharedImmediateTexture
}
/// <inheritdoc/>
public void Dispose()
public override string ToString() => $"{nameof(RefCountableWrappingTextureWrap)}({this.owner})";
/// <inheritdoc/>
protected override bool TryGetWrap(out IDalamudTextureWrap? wrap) => (wrap = this.innerWrap) is not null;
/// <inheritdoc/>
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);
}
}
}
/// <inheritdoc/>
public override string ToString() => $"{nameof(RefCountableWrappingTextureWrap)}({this.owner})";
}
/// <summary>A texture wrap that revives and waits for the underlying texture as needed on every access.</summary>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
private sealed class AvailableOnAccessTextureWrap : IDalamudTextureWrap
private sealed class AvailableOnAccessTextureWrap : ForwardingTextureWrap
{
private readonly SharedImmediateTexture inner;
/// <summary>Initializes a new instance of the <see cref="AvailableOnAccessTextureWrap"/> class.</summary>
/// <param name="inner">The shared texture.</param>
public AvailableOnAccessTextureWrap(SharedImmediateTexture inner) => this.inner = inner;
/// <inheritdoc/>
public IntPtr ImGuiHandle => this.WaitGet().ImGuiHandle;
/// <inheritdoc/>
public int Width => this.WaitGet().Width;
/// <inheritdoc/>
public int Height => this.WaitGet().Height;
/// <inheritdoc/>
public IDalamudTextureWrap CreateWrapSharingLowLevelResource()
public override IDalamudTextureWrap CreateWrapSharingLowLevelResource()
{
this.inner.AddRef();
try
@ -506,22 +480,18 @@ internal abstract class SharedImmediateTexture
}
}
/// <inheritdoc/>
public void Dispose()
{
// ignore
}
/// <inheritdoc/>
public override string ToString() => $"{nameof(AvailableOnAccessTextureWrap)}({this.inner})";
private IDalamudTextureWrap WaitGet()
/// <inheritdoc/>
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<DalamudAssetManager>.Get().Empty4X4;
wrap = this.inner.nonOwningWrap ?? Service<DalamudAssetManager>.Get().Empty4X4;
return true;
}
}
}

View file

@ -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<ID3D11Texture2D> GetImGuiViewportBackBuffer(uint viewportId)