mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-27 02:49:18 +01:00
changes
This commit is contained in:
parent
aa35052a15
commit
f8492dc06b
18 changed files with 410 additions and 361 deletions
|
|
@ -1,6 +1,6 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Dalamud.Interface.Internal;
|
||||
namespace Dalamud.Interface;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a lookup for a game icon.
|
||||
69
Dalamud/Interface/ISharedImmediateTexture.cs
Normal file
69
Dalamud/Interface/ISharedImmediateTexture.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Interface;
|
||||
|
||||
/// <summary>A texture with a backing instance of <see cref="IDalamudTextureWrap"/> that is shared across multiple
|
||||
/// requesters.</summary>
|
||||
/// <remarks>
|
||||
/// <para>Calling <see cref="IDisposable.Dispose"/> on this interface is a no-op.</para>
|
||||
/// <para><see cref="GetWrap()"/> and <see cref="TryGetWrap"/> may stop returning the intended texture at any point.
|
||||
/// Use <see cref="RentAsync"/> to lock the texture for use in any thread for any duration.</para>
|
||||
/// </remarks>
|
||||
public interface ISharedImmediateTexture
|
||||
{
|
||||
/// <summary>Gets the texture for use with the current frame.</summary>
|
||||
/// <returns>An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for the current
|
||||
/// frame being drawn.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Calling outside the main thread will fail.</para>
|
||||
/// <para>This function does not throw.</para>
|
||||
/// <para><see cref="IDisposable.Dispose"/> will be ignored.</para>
|
||||
/// <para>If the texture is unavailable for any reason, then the returned instance of
|
||||
/// <see cref="IDalamudTextureWrap"/> will point to an empty texture instead.</para>
|
||||
/// </remarks>
|
||||
IDalamudTextureWrap GetWrap();
|
||||
|
||||
/// <summary>Gets the texture for use with the current frame.</summary>
|
||||
/// <param name="defaultWrap">The default wrap to return if the requested texture was not immediately available.
|
||||
/// </param>
|
||||
/// <returns>An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for the current
|
||||
/// frame being drawn.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Calling outside the main thread will fail.</para>
|
||||
/// <para>This function does not throw.</para>
|
||||
/// <para><see cref="IDisposable.Dispose"/> will be ignored.</para>
|
||||
/// <para>If the texture is unavailable for any reason, then <paramref name="defaultWrap"/> will be returned.</para>
|
||||
/// </remarks>
|
||||
[return: NotNullIfNotNull(nameof(defaultWrap))]
|
||||
IDalamudTextureWrap? GetWrap(IDalamudTextureWrap? defaultWrap);
|
||||
|
||||
/// <summary>Attempts to get the texture for use with the current frame.</summary>
|
||||
/// <param name="texture">An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for
|
||||
/// the current frame being drawn, or <c>null</c> if texture is not loaded (yet).</param>
|
||||
/// <param name="exception">The load exception, if any.</param>
|
||||
/// <returns><c>true</c> if <paramref name="texture"/> points to the loaded texture; <c>false</c> if the texture is
|
||||
/// still being loaded, or the load has failed.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Calling outside the main thread will fail.</para>
|
||||
/// <para>This function does not throw.</para>
|
||||
/// <para><see cref="IDisposable.Dispose"/> on the returned <paramref name="texture"/> will be ignored.</para>
|
||||
/// </remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
bool TryGetWrap([NotNullWhen(true)] out IDalamudTextureWrap? texture, out Exception? exception);
|
||||
|
||||
/// <summary>Creates a new instance of <see cref="IDalamudTextureWrap"/> holding a new reference to this texture.
|
||||
/// The returned texture is guaranteed to be available until <see cref="IDisposable.Dispose"/> is called.</summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success.</returns>
|
||||
/// <remarks>
|
||||
/// <see cref="IDisposable.Dispose"/> must be called on the resulting instance of <see cref="IDalamudTextureWrap"/>
|
||||
/// from the returned <see cref="Task{TResult}"/> after use. Consider using
|
||||
/// <see cref="DisposeSafety.ToContentDisposedTask{T}"/> to dispose the result automatically according to the state
|
||||
/// of the task.</remarks>
|
||||
Task<IDalamudTextureWrap> RentAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
|
@ -4,22 +4,22 @@ using System.Threading.Tasks;
|
|||
|
||||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Interface.Internal.SharableTextures;
|
||||
namespace Dalamud.Interface.Internal.SharedImmediateTextures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a sharable texture, based on a file on the system filesystem.
|
||||
/// </summary>
|
||||
internal sealed class FileSystemSharableTexture : SharableTexture
|
||||
internal sealed class FileSystemSharedImmediateTexture : SharedImmediateTexture
|
||||
{
|
||||
private readonly string path;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSystemSharableTexture"/> class.
|
||||
/// Initializes a new instance of the <see cref="FileSystemSharedImmediateTexture"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="holdSelfReference">If set to <c>true</c>, this class will hold a reference to self.
|
||||
/// Otherwise, it is expected that the caller to hold the reference.</param>
|
||||
private FileSystemSharableTexture(string path, bool holdSelfReference)
|
||||
private FileSystemSharedImmediateTexture(string path, bool holdSelfReference)
|
||||
: base(holdSelfReference)
|
||||
{
|
||||
this.path = path;
|
||||
|
|
@ -31,24 +31,24 @@ internal sealed class FileSystemSharableTexture : SharableTexture
|
|||
public override string SourcePathForDebug => this.path;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GamePathSharableTexture"/>.
|
||||
/// Creates a new instance of <see cref="GamePathSharedImmediateTexture"/>.
|
||||
/// The new instance will hold a reference to itself.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
public static SharableTexture CreateImmediate(string path) => new FileSystemSharableTexture(path, true);
|
||||
public static SharedImmediateTexture CreateImmediate(string path) => new FileSystemSharedImmediateTexture(path, true);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GamePathSharableTexture"/>.
|
||||
/// Creates a new instance of <see cref="GamePathSharedImmediateTexture"/>.
|
||||
/// The caller is expected to manage ownership of the new instance.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
public static SharableTexture CreateAsync(string path) => new FileSystemSharableTexture(path, false);
|
||||
public static SharedImmediateTexture CreateAsync(string path) => new FileSystemSharedImmediateTexture(path, false);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() =>
|
||||
$"{nameof(FileSystemSharableTexture)}#{this.InstanceIdForDebug}({this.path})";
|
||||
$"{nameof(FileSystemSharedImmediateTexture)}#{this.InstanceIdForDebug}({this.path})";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ReleaseResources()
|
||||
|
|
@ -7,22 +7,22 @@ using Dalamud.Utility;
|
|||
|
||||
using Lumina.Data.Files;
|
||||
|
||||
namespace Dalamud.Interface.Internal.SharableTextures;
|
||||
namespace Dalamud.Interface.Internal.SharedImmediateTextures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a sharable texture, based on a file in game resources.
|
||||
/// </summary>
|
||||
internal sealed class GamePathSharableTexture : SharableTexture
|
||||
internal sealed class GamePathSharedImmediateTexture : SharedImmediateTexture
|
||||
{
|
||||
private readonly string path;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GamePathSharableTexture"/> class.
|
||||
/// Initializes a new instance of the <see cref="GamePathSharedImmediateTexture"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="holdSelfReference">If set to <c>true</c>, this class will hold a reference to self.
|
||||
/// Otherwise, it is expected that the caller to hold the reference.</param>
|
||||
private GamePathSharableTexture(string path, bool holdSelfReference)
|
||||
private GamePathSharedImmediateTexture(string path, bool holdSelfReference)
|
||||
: base(holdSelfReference)
|
||||
{
|
||||
this.path = path;
|
||||
|
|
@ -34,23 +34,23 @@ internal sealed class GamePathSharableTexture : SharableTexture
|
|||
public override string SourcePathForDebug => this.path;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GamePathSharableTexture"/>.
|
||||
/// Creates a new instance of <see cref="GamePathSharedImmediateTexture"/>.
|
||||
/// The new instance will hold a reference to itself.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
public static SharableTexture CreateImmediate(string path) => new GamePathSharableTexture(path, true);
|
||||
public static SharedImmediateTexture CreateImmediate(string path) => new GamePathSharedImmediateTexture(path, true);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GamePathSharableTexture"/>.
|
||||
/// Creates a new instance of <see cref="GamePathSharedImmediateTexture"/>.
|
||||
/// The caller is expected to manage ownership of the new instance.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
public static SharableTexture CreateAsync(string path) => new GamePathSharableTexture(path, false);
|
||||
public static SharedImmediateTexture CreateAsync(string path) => new GamePathSharedImmediateTexture(path, false);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{nameof(GamePathSharableTexture)}#{this.InstanceIdForDebug}({this.path})";
|
||||
public override string ToString() => $"{nameof(GamePathSharedImmediateTexture)}#{this.InstanceIdForDebug}({this.path})";
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ReleaseResources()
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Storage.Assets;
|
||||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Interface.Internal.SharableTextures;
|
||||
namespace Dalamud.Interface.Internal.SharedImmediateTextures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a texture that may have multiple reference holders (owners).
|
||||
/// </summary>
|
||||
internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IThrottleBasisProvider
|
||||
internal abstract class SharedImmediateTexture
|
||||
: ISharedImmediateTexture, IRefCountable, TextureLoadThrottler.IThrottleBasisProvider
|
||||
{
|
||||
private const int SelfReferenceDurationTicks = 2000;
|
||||
private const long SelfReferenceExpiryExpired = long.MaxValue;
|
||||
|
|
@ -23,14 +25,14 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
private long selfReferenceExpiry;
|
||||
private IDalamudTextureWrap? availableOnAccessWrapForApi9;
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
private DisposeSuppressingTextureWrap? disposeSuppressingWrap;
|
||||
private NotOwnedTextureWrap? nonOwningWrap;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SharableTexture"/> class.
|
||||
/// Initializes a new instance of the <see cref="SharedImmediateTexture"/> class.
|
||||
/// </summary>
|
||||
/// <param name="holdSelfReference">If set to <c>true</c>, this class will hold a reference to self.
|
||||
/// Otherwise, it is expected that the caller to hold the reference.</param>
|
||||
protected SharableTexture(bool holdSelfReference)
|
||||
protected SharedImmediateTexture(bool holdSelfReference)
|
||||
{
|
||||
this.InstanceIdForDebug = Interlocked.Increment(ref instanceCounter);
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
public abstract string SourcePathForDebug { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance of <see cref="SharableTexture"/> supports revival.
|
||||
/// Gets a value indicating whether this instance of <see cref="SharedImmediateTexture"/> supports revival.
|
||||
/// </summary>
|
||||
public bool HasRevivalPossibility => this.RevivalPossibility?.TryGetTarget(out _) is true;
|
||||
|
||||
|
|
@ -99,7 +101,7 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content has been queried,
|
||||
/// i.e. <see cref="CreateNewReference"/> or <see cref="GetImmediate"/> is called.
|
||||
/// i.e. <see cref="TryGetWrap"/> or <see cref="RentAsync"/> is called.
|
||||
/// </summary>
|
||||
public bool ContentQueried { get; private set; }
|
||||
|
||||
|
|
@ -125,7 +127,8 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
public int AddRef() => this.TryAddRef(out var newRefCount) switch
|
||||
{
|
||||
IRefCountable.RefCountResult.StillAlive => newRefCount,
|
||||
IRefCountable.RefCountResult.AlreadyDisposed => throw new ObjectDisposedException(nameof(SharableTexture)),
|
||||
IRefCountable.RefCountResult.AlreadyDisposed => throw new ObjectDisposedException(
|
||||
nameof(SharedImmediateTexture)),
|
||||
IRefCountable.RefCountResult.FinalRelease => throw new InvalidOperationException(),
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
|
|
@ -164,7 +167,7 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
|
||||
this.cancellationTokenSource?.Cancel();
|
||||
this.cancellationTokenSource = null;
|
||||
this.disposeSuppressingWrap = null;
|
||||
this.nonOwningWrap = null;
|
||||
this.ReleaseResources();
|
||||
this.resourceReleased = true;
|
||||
|
||||
|
|
@ -173,7 +176,7 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
}
|
||||
|
||||
case IRefCountable.RefCountResult.AlreadyDisposed:
|
||||
throw new ObjectDisposedException(nameof(SharableTexture));
|
||||
throw new ObjectDisposedException(nameof(SharedImmediateTexture));
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
|
|
@ -206,48 +209,27 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture if immediately available. The texture is guarnateed to be available for the rest of the frame.
|
||||
/// Invocation from non-main thread will exhibit an undefined behavior.
|
||||
/// </summary>
|
||||
/// <returns>The texture if available; <c>null</c> if not.</returns>
|
||||
public IDalamudTextureWrap? GetImmediate()
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap GetWrap() => this.GetWrap(Service<DalamudAssetManager>.Get().Empty4X4);
|
||||
|
||||
/// <inheritdoc/>
|
||||
[return: NotNullIfNotNull(nameof(defaultWrap))]
|
||||
public IDalamudTextureWrap? GetWrap(IDalamudTextureWrap? defaultWrap)
|
||||
{
|
||||
if (this.TryAddRef(out _) != IRefCountable.RefCountResult.StillAlive)
|
||||
{
|
||||
this.ContentQueried = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.ContentQueried = true;
|
||||
this.LatestRequestedTick = Environment.TickCount64;
|
||||
var nexp = Environment.TickCount64 + SelfReferenceDurationTicks;
|
||||
while (true)
|
||||
{
|
||||
var exp = this.selfReferenceExpiry;
|
||||
if (exp != Interlocked.CompareExchange(ref this.selfReferenceExpiry, nexp, exp))
|
||||
continue;
|
||||
|
||||
// If below condition is met, the additional reference from above is for the self-reference.
|
||||
if (exp == SelfReferenceExpiryExpired)
|
||||
_ = this.AddRef();
|
||||
|
||||
// Release the reference for rendering, after rendering ImGui.
|
||||
Service<InterfaceManager>.Get().EnqueueDeferredDispose(this);
|
||||
|
||||
return this.UnderlyingWrap?.IsCompletedSuccessfully is true
|
||||
? this.disposeSuppressingWrap ??= new(this.UnderlyingWrap.Result)
|
||||
: null;
|
||||
}
|
||||
if (!this.TryGetWrap(out var texture, out _))
|
||||
texture = null;
|
||||
return texture ?? defaultWrap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new reference to this texture. The texture is guaranteed to be available until
|
||||
/// <see cref="IDisposable.Dispose"/> is called.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The task containing the texture.</returns>
|
||||
public async Task<IDalamudTextureWrap> CreateNewReference(CancellationToken cancellationToken)
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetWrap([NotNullWhen(true)] out IDalamudTextureWrap? texture, out Exception? exception)
|
||||
{
|
||||
ThreadSafety.AssertMainThread();
|
||||
return this.TryGetWrapCore(out texture, out exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IDalamudTextureWrap> RentAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
|
|
@ -312,10 +294,8 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
if (this.RevivalPossibility?.TryGetTarget(out this.availableOnAccessWrapForApi9) is true)
|
||||
return this.availableOnAccessWrapForApi9;
|
||||
|
||||
var newRefTask = this.CreateNewReference(default);
|
||||
// Cancellation is not expected for this API
|
||||
// ReSharper disable once MethodSupportsCancellation
|
||||
newRefTask.Wait();
|
||||
var newRefTask = this.RentAsync(this.LoadCancellationToken);
|
||||
newRefTask.Wait(this.LoadCancellationToken);
|
||||
if (!newRefTask.IsCompletedSuccessfully)
|
||||
return null;
|
||||
newRefTask.Result.Dispose();
|
||||
|
|
@ -328,7 +308,7 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up this instance of <see cref="SharableTexture"/>.
|
||||
/// Cleans up this instance of <see cref="SharedImmediateTexture"/>.
|
||||
/// </summary>
|
||||
protected abstract void ReleaseResources();
|
||||
|
||||
|
|
@ -378,14 +358,95 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary><see cref="ISharedImmediateTexture.TryGetWrap"/>, but without checking for thread.</summary>
|
||||
private bool TryGetWrapCore(
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception)
|
||||
{
|
||||
if (this.TryAddRef(out _) != IRefCountable.RefCountResult.StillAlive)
|
||||
{
|
||||
this.ContentQueried = true;
|
||||
texture = null;
|
||||
exception = new ObjectDisposedException(this.GetType().Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.ContentQueried = true;
|
||||
this.LatestRequestedTick = Environment.TickCount64;
|
||||
|
||||
var nexp = Environment.TickCount64 + SelfReferenceDurationTicks;
|
||||
while (true)
|
||||
{
|
||||
var exp = this.selfReferenceExpiry;
|
||||
if (exp != Interlocked.CompareExchange(ref this.selfReferenceExpiry, nexp, exp))
|
||||
continue;
|
||||
|
||||
// If below condition is met, the additional reference from above is for the self-reference.
|
||||
if (exp == SelfReferenceExpiryExpired)
|
||||
_ = this.AddRef();
|
||||
|
||||
// Release the reference for rendering, after rendering ImGui.
|
||||
Service<InterfaceManager>.Get().EnqueueDeferredDispose(this);
|
||||
|
||||
var uw = this.UnderlyingWrap;
|
||||
if (uw?.IsCompletedSuccessfully is true)
|
||||
{
|
||||
texture = this.nonOwningWrap ??= new(uw.Result, this);
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
texture = null;
|
||||
exception = uw?.Exception;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NotOwnedTextureWrap : IDalamudTextureWrap
|
||||
{
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
this.owner.AddRef();
|
||||
return new RefCountableWrappingTextureWrap(this.innerWrap, this.owner);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{nameof(NotOwnedTextureWrap)}({this.owner})";
|
||||
}
|
||||
|
||||
private sealed class RefCountableWrappingTextureWrap : IDalamudTextureWrap
|
||||
{
|
||||
private IDalamudTextureWrap? innerWrap;
|
||||
private IRefCountable? owner;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RefCountableWrappingTextureWrap"/> class.
|
||||
/// </summary>
|
||||
/// <summary>Initializes a new instance of the <see cref="RefCountableWrappingTextureWrap"/> class.</summary>
|
||||
/// <param name="wrap">The inner wrap.</param>
|
||||
/// <param name="owner">The reference counting owner.</param>
|
||||
public RefCountableWrappingTextureWrap(IDalamudTextureWrap wrap, IRefCountable owner)
|
||||
|
|
@ -408,6 +469,18 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
private IDalamudTextureWrap InnerWrapNonDisposed =>
|
||||
this.innerWrap ?? throw new ObjectDisposedException(nameof(RefCountableWrappingTextureWrap));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap CreateWrapSharingLowLevelResource()
|
||||
{
|
||||
var ownerCopy = this.owner;
|
||||
var wrapCopy = this.innerWrap;
|
||||
if (ownerCopy is null || wrapCopy is null)
|
||||
throw new ObjectDisposedException(nameof(RefCountableWrappingTextureWrap));
|
||||
|
||||
ownerCopy.AddRef();
|
||||
return new RefCountableWrappingTextureWrap(wrapCopy, ownerCopy);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
@ -417,6 +490,8 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
return;
|
||||
if (ownerCopy != Interlocked.CompareExchange(ref this.owner, null, ownerCopy))
|
||||
continue;
|
||||
|
||||
// Note: do not dispose this; life of the wrap is managed by the owner.
|
||||
this.innerWrap = null;
|
||||
ownerCopy.Release();
|
||||
GC.SuppressFinalize(this);
|
||||
|
|
@ -427,20 +502,48 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
public override string ToString() => $"{nameof(RefCountableWrappingTextureWrap)}({this.owner})";
|
||||
}
|
||||
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
private sealed class AvailableOnAccessTextureWrap : IDalamudTextureWrap
|
||||
{
|
||||
private readonly SharableTexture inner;
|
||||
private readonly SharedImmediateTexture inner;
|
||||
|
||||
public AvailableOnAccessTextureWrap(SharableTexture inner) => this.inner = inner;
|
||||
public AvailableOnAccessTextureWrap(SharedImmediateTexture inner) => this.inner = inner;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr ImGuiHandle => this.GetActualTexture().ImGuiHandle;
|
||||
public IntPtr ImGuiHandle => this.WaitGet().ImGuiHandle;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Width => this.GetActualTexture().Width;
|
||||
public int Width => this.WaitGet().Width;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Height => this.GetActualTexture().Height;
|
||||
public int Height => this.WaitGet().Height;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap CreateWrapSharingLowLevelResource()
|
||||
{
|
||||
this.inner.AddRef();
|
||||
try
|
||||
{
|
||||
if (!this.inner.TryGetWrapCore(out var wrap, out _))
|
||||
{
|
||||
this.inner.UnderlyingWrap?.Wait();
|
||||
|
||||
if (!this.inner.TryGetWrapCore(out wrap, out _))
|
||||
{
|
||||
// Calling dispose on Empty4x4 is a no-op, so we can just return that.
|
||||
this.inner.Release();
|
||||
return Service<DalamudAssetManager>.Get().Empty4X4;
|
||||
}
|
||||
}
|
||||
|
||||
return new RefCountableWrappingTextureWrap(wrap, this.inner);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.inner.Release();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
|
|
@ -451,13 +554,13 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
|||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{nameof(AvailableOnAccessTextureWrap)}({this.inner})";
|
||||
|
||||
private IDalamudTextureWrap GetActualTexture()
|
||||
private IDalamudTextureWrap WaitGet()
|
||||
{
|
||||
if (this.inner.GetImmediate() is { } t)
|
||||
if (this.inner.TryGetWrapCore(out var t, out _))
|
||||
return t;
|
||||
|
||||
this.inner.UnderlyingWrap?.Wait();
|
||||
return this.inner.disposeSuppressingWrap ?? Service<DalamudAssetManager>.Get().Empty4X4;
|
||||
return this.inner.nonOwningWrap ?? Service<DalamudAssetManager>.Get().Empty4X4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Interface.Internal.SharableTextures;
|
||||
namespace Dalamud.Interface.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing texture loads.
|
||||
|
|
@ -9,7 +9,7 @@ using BitFaster.Caching.Lru;
|
|||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Internal.SharableTextures;
|
||||
using Dalamud.Interface.Internal.SharedImmediateTextures;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
|
|
@ -66,9 +66,9 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
private readonly TextureLoadThrottler textureLoadThrottler = Service<TextureLoadThrottler>.Get();
|
||||
|
||||
private readonly ConcurrentLru<GameIconLookup, string> lookupToPath = new(PathLookupLruCount);
|
||||
private readonly ConcurrentDictionary<string, SharableTexture> gamePathTextures = new();
|
||||
private readonly ConcurrentDictionary<string, SharableTexture> fileSystemTextures = new();
|
||||
private readonly HashSet<SharableTexture> invalidatedTextures = new();
|
||||
private readonly ConcurrentDictionary<string, SharedImmediateTexture> gamePathTextures = new();
|
||||
private readonly ConcurrentDictionary<string, SharedImmediateTexture> fileSystemTextures = new();
|
||||
private readonly HashSet<SharedImmediateTexture> invalidatedTextures = new();
|
||||
|
||||
private bool disposing;
|
||||
|
||||
|
|
@ -84,12 +84,12 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
/// <summary>
|
||||
/// Gets all the loaded textures from the game resources. Debug use only.
|
||||
/// </summary>
|
||||
public ICollection<SharableTexture> GamePathTextures => this.gamePathTextures.Values;
|
||||
public ICollection<SharedImmediateTexture> GamePathTextures => this.gamePathTextures.Values;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the loaded textures from the game resources. Debug use only.
|
||||
/// </summary>
|
||||
public ICollection<SharableTexture> FileSystemTextures => this.fileSystemTextures.Values;
|
||||
public ICollection<SharedImmediateTexture> FileSystemTextures => this.fileSystemTextures.Values;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the loaded textures that are invalidated from <see cref="InvalidatePaths"/>. Debug use only.
|
||||
|
|
@ -99,7 +99,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
"ReSharper",
|
||||
"InconsistentlySynchronizedField",
|
||||
Justification = "Debug use only; users are expected to lock around this")]
|
||||
public ICollection<SharableTexture> InvalidatedTextures => this.invalidatedTextures;
|
||||
public ICollection<SharedImmediateTexture> InvalidatedTextures => this.invalidatedTextures;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
|
|
@ -118,14 +118,12 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
this.fileSystemTextures.Clear();
|
||||
}
|
||||
|
||||
#region API9 compat
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
/// <inheritdoc/>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete("See interface definition.")]
|
||||
public string? GetIconPath(
|
||||
uint iconId,
|
||||
ITextureProvider.IconFlags flags = ITextureProvider.IconFlags.HiRes,
|
||||
ClientLanguage? language = null)
|
||||
string? ITextureProvider.GetIconPath(uint iconId, ITextureProvider.IconFlags flags, ClientLanguage? language)
|
||||
=> this.TryGetIconPath(
|
||||
new(
|
||||
iconId,
|
||||
|
|
@ -139,109 +137,56 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
/// <inheritdoc/>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete("See interface definition.")]
|
||||
public IDalamudTextureWrap? GetIcon(
|
||||
IDalamudTextureWrap? ITextureProvider.GetIcon(
|
||||
uint iconId,
|
||||
ITextureProvider.IconFlags flags = ITextureProvider.IconFlags.HiRes,
|
||||
ClientLanguage? language = null,
|
||||
bool keepAlive = false) =>
|
||||
this.GetTextureFromGame(
|
||||
this.lookupToPath.GetOrAdd(
|
||||
ITextureProvider.IconFlags flags,
|
||||
ClientLanguage? language,
|
||||
bool keepAlive) =>
|
||||
this.GetFromGameIcon(
|
||||
new(
|
||||
iconId,
|
||||
(flags & ITextureProvider.IconFlags.ItemHighQuality) != 0,
|
||||
(flags & ITextureProvider.IconFlags.HiRes) != 0,
|
||||
language),
|
||||
this.GetIconPathByValue));
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete("See interface definition.")]
|
||||
public IDalamudTextureWrap? GetTextureFromGame(string path, bool keepAlive = false) =>
|
||||
this.gamePathTextures.GetOrAdd(path, GamePathSharableTexture.CreateImmediate)
|
||||
language))
|
||||
.GetAvailableOnAccessWrapForApi9();
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete("See interface definition.")]
|
||||
public IDalamudTextureWrap? GetTextureFromFile(FileInfo file, bool keepAlive = false) =>
|
||||
this.fileSystemTextures.GetOrAdd(file.FullName, FileSystemSharableTexture.CreateImmediate)
|
||||
.GetAvailableOnAccessWrapForApi9();
|
||||
IDalamudTextureWrap? ITextureProvider.GetTextureFromGame(string path, bool keepAlive) =>
|
||||
this.GetFromGame(path).GetAvailableOnAccessWrapForApi9();
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete("See interface definition.")]
|
||||
IDalamudTextureWrap? ITextureProvider.GetTextureFromFile(FileInfo file, bool keepAlive) =>
|
||||
this.GetFromFile(file.FullName).GetAvailableOnAccessWrapForApi9();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromGameIcon"/>
|
||||
public SharedImmediateTexture GetFromGameIcon(in GameIconLookup lookup) =>
|
||||
this.GetFromGame(this.lookupToPath.GetOrAdd(lookup, this.GetIconPathByValue));
|
||||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromGame"/>
|
||||
public SharedImmediateTexture GetFromGame(string path) =>
|
||||
this.gamePathTextures.GetOrAdd(path, GamePathSharedImmediateTexture.CreateImmediate);
|
||||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromFile"/>
|
||||
public SharedImmediateTexture GetFromFile(string path) =>
|
||||
this.fileSystemTextures.GetOrAdd(path, FileSystemSharedImmediateTexture.CreateImmediate);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap ImmediateGetFromGameIcon(in GameIconLookup lookup) =>
|
||||
this.ImmediateGetFromGame(this.lookupToPath.GetOrAdd(lookup, this.GetIconPathByValue));
|
||||
ISharedImmediateTexture ITextureProvider.GetFromGameIcon(in GameIconLookup lookup) => this.GetFromGameIcon(lookup);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap ImmediateGetFromGame(string path) =>
|
||||
this.ImmediateTryGetFromGame(path, out var texture, out _)
|
||||
? texture
|
||||
: this.dalamudAssetManager.Empty4X4;
|
||||
ISharedImmediateTexture ITextureProvider.GetFromGame(string path) => this.GetFromGame(path);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap ImmediateGetFromFile(string file) =>
|
||||
this.ImmediateTryGetFromFile(file, out var texture, out _)
|
||||
? texture
|
||||
: this.dalamudAssetManager.Empty4X4;
|
||||
ISharedImmediateTexture ITextureProvider.GetFromFile(string path) => this.GetFromFile(path);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ImmediateTryGetFromGameIcon(
|
||||
in GameIconLookup lookup,
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception) =>
|
||||
this.ImmediateTryGetFromGame(
|
||||
this.lookupToPath.GetOrAdd(lookup, this.GetIconPathByValue),
|
||||
out texture,
|
||||
out exception);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ImmediateTryGetFromGame(
|
||||
string path,
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception)
|
||||
{
|
||||
ThreadSafety.AssertMainThread();
|
||||
var t = this.gamePathTextures.GetOrAdd(path, GamePathSharableTexture.CreateImmediate);
|
||||
texture = t.GetImmediate();
|
||||
exception = t.UnderlyingWrap?.Exception;
|
||||
return texture is not null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ImmediateTryGetFromFile(
|
||||
string file,
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception)
|
||||
{
|
||||
ThreadSafety.AssertMainThread();
|
||||
var t = this.fileSystemTextures.GetOrAdd(file, FileSystemSharableTexture.CreateImmediate);
|
||||
texture = t.GetImmediate();
|
||||
exception = t.UnderlyingWrap?.Exception;
|
||||
return texture is not null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromGameIconAsync(
|
||||
in GameIconLookup lookup,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.GetFromGameAsync(this.lookupToPath.GetOrAdd(lookup, this.GetIconPathByValue), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromGameAsync(
|
||||
string path,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.gamePathTextures.GetOrAdd(path, GamePathSharableTexture.CreateAsync)
|
||||
.CreateNewReference(cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromFileAsync(
|
||||
string file,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.fileSystemTextures.GetOrAdd(file, FileSystemSharableTexture.CreateAsync)
|
||||
.CreateNewReference(cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromImageAsync(
|
||||
public Task<IDalamudTextureWrap> CreateFromImageAsync(
|
||||
ReadOnlyMemory<byte> bytes,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.textureLoadThrottler.CreateLoader(
|
||||
|
|
@ -250,7 +195,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromImageAsync(
|
||||
public Task<IDalamudTextureWrap> CreateFromImageAsync(
|
||||
Stream stream,
|
||||
bool leaveOpen = false,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
|
|
@ -260,7 +205,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
{
|
||||
await using var ms = stream.CanSeek ? new MemoryStream((int)stream.Length) : new();
|
||||
await stream.CopyToAsync(ms, ct).ConfigureAwait(false);
|
||||
return await this.GetFromImageAsync(ms.GetBuffer(), ct);
|
||||
return await this.CreateFromImageAsync(ms.GetBuffer(), ct);
|
||||
},
|
||||
cancellationToken)
|
||||
.ContinueWith(
|
||||
|
|
@ -274,7 +219,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
.Unwrap();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap GetFromRaw(
|
||||
public IDalamudTextureWrap CreateFromRaw(
|
||||
RawImageSpecification specs,
|
||||
ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
|
|
@ -304,31 +249,34 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
};
|
||||
|
||||
using var texture = new Texture2D(scene.Device, texDesc, new DataRectangle(new(pData), specs.Pitch));
|
||||
resView = new(scene.Device, texture, new()
|
||||
{
|
||||
Format = texDesc.Format,
|
||||
Dimension = ShaderResourceViewDimension.Texture2D,
|
||||
Texture2D = { MipLevels = texDesc.MipLevels },
|
||||
});
|
||||
resView = new(
|
||||
scene.Device,
|
||||
texture,
|
||||
new()
|
||||
{
|
||||
Format = texDesc.Format,
|
||||
Dimension = ShaderResourceViewDimension.Texture2D,
|
||||
Texture2D = { MipLevels = texDesc.MipLevels },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
|
||||
return new DalamudTextureWrap(new D3DTextureWrap(resView, specs.Width, specs.Height));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromRawAsync(
|
||||
public Task<IDalamudTextureWrap> CreateFromRawAsync(
|
||||
RawImageSpecification specs,
|
||||
ReadOnlyMemory<byte> bytes,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.textureLoadThrottler.CreateLoader(
|
||||
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
|
||||
_ => Task.FromResult(this.GetFromRaw(specs, bytes.Span)),
|
||||
_ => Task.FromResult(this.CreateFromRaw(specs, bytes.Span)),
|
||||
cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromRawAsync(
|
||||
public Task<IDalamudTextureWrap> CreateFromRawAsync(
|
||||
RawImageSpecification specs,
|
||||
Stream stream,
|
||||
bool leaveOpen = false,
|
||||
|
|
@ -339,7 +287,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
{
|
||||
await using var ms = stream.CanSeek ? new MemoryStream((int)stream.Length) : new();
|
||||
await stream.CopyToAsync(ms, ct).ConfigureAwait(false);
|
||||
return await this.GetFromRawAsync(specs, ms.GetBuffer(), ct);
|
||||
return await this.CreateFromRawAsync(specs, ms.GetBuffer(), ct);
|
||||
},
|
||||
cancellationToken)
|
||||
.ContinueWith(
|
||||
|
|
@ -353,17 +301,17 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
.Unwrap();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap GetTexture(TexFile file) => this.GetFromTexFileAsync(file).Result;
|
||||
public IDalamudTextureWrap CreateFromTexFile(TexFile file) => this.CreateFromTexFileAsync(file).Result;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> GetFromTexFileAsync(
|
||||
public Task<IDalamudTextureWrap> CreateFromTexFileAsync(
|
||||
TexFile file,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.textureLoadThrottler.CreateLoader(
|
||||
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
|
||||
ct => Task.Run(() => this.NoThrottleGetFromTexFile(file), ct),
|
||||
cancellationToken);
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SupportsDxgiFormat(int dxgiFormat)
|
||||
{
|
||||
|
|
@ -489,7 +437,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
|
||||
/// <summary>
|
||||
/// Gets a texture from the given image. Skips the load throttler; intended to be used from implementation of
|
||||
/// <see cref="SharableTexture"/>s.
|
||||
/// <see cref="SharedImmediateTexture"/>s.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The data.</param>
|
||||
/// <returns>The loaded texture.</returns>
|
||||
|
|
@ -508,7 +456,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
|
||||
/// <summary>
|
||||
/// Gets a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used from
|
||||
/// implementation of <see cref="SharableTexture"/>s.
|
||||
/// implementation of <see cref="SharedImmediateTexture"/>s.
|
||||
/// </summary>
|
||||
/// <param name="file">The data.</param>
|
||||
/// <returns>The loaded texture.</returns>
|
||||
|
|
@ -522,7 +470,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8);
|
||||
}
|
||||
|
||||
return this.GetFromRaw(
|
||||
return this.CreateFromRaw(
|
||||
RawImageSpecification.From(buffer.Width, buffer.Height, dxgiFormat),
|
||||
buffer.RawData);
|
||||
}
|
||||
|
|
@ -567,7 +515,7 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
|||
|
||||
return;
|
||||
|
||||
static bool TextureFinalReleasePredicate(SharableTexture v) =>
|
||||
static bool TextureFinalReleasePredicate(SharedImmediateTexture v) =>
|
||||
v.ContentQueried && v.ReleaseSelfReference(false) == 0 && !v.HasRevivalPossibility;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ public class IconBrowserWidget : IDataWindowWidget
|
|||
var texm = Service<TextureManager>.Get();
|
||||
var cursor = ImGui.GetCursorScreenPos();
|
||||
|
||||
if (texm.ImmediateTryGetFromGameIcon(new((uint)iconId), out var texture, out var exc))
|
||||
if (texm.GetFromGameIcon(new((uint)iconId)).TryGetWrap(out var texture, out var exc))
|
||||
{
|
||||
ImGui.Image(texture.ImGuiHandle, this.iconSize);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Internal.SharableTextures;
|
||||
using Dalamud.Interface.Internal.SharedImmediateTextures;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Storage.Assets;
|
||||
|
|
@ -42,6 +42,8 @@ internal class TexWidget : IDataWindowWidget
|
|||
/// <inheritdoc/>
|
||||
public bool Ready { get; set; }
|
||||
|
||||
private ITextureProvider TextureManagerForApi9 => this.textureManager!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Load()
|
||||
{
|
||||
|
|
@ -145,7 +147,7 @@ internal class TexWidget : IDataWindowWidget
|
|||
}
|
||||
}
|
||||
|
||||
private unsafe void DrawLoadedTextures(ICollection<SharableTexture> textures)
|
||||
private unsafe void DrawLoadedTextures(ICollection<SharedImmediateTexture> textures)
|
||||
{
|
||||
var im = Service<InterfaceManager>.Get();
|
||||
if (!ImGui.BeginTable("##table", 6))
|
||||
|
|
@ -226,7 +228,7 @@ internal class TexWidget : IDataWindowWidget
|
|||
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiComponents.IconButton(FontAwesomeIcon.Image);
|
||||
if (ImGui.IsItemHovered() && texture.GetImmediate() is { } immediate)
|
||||
if (ImGui.IsItemHovered() && texture.GetWrap(null) is { } immediate)
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Image(immediate.ImGuiHandle, immediate.Size);
|
||||
|
|
@ -274,7 +276,7 @@ internal class TexWidget : IDataWindowWidget
|
|||
flags |= ITextureProvider.IconFlags.ItemHighQuality;
|
||||
if (this.hiRes)
|
||||
flags |= ITextureProvider.IconFlags.HiRes;
|
||||
this.addedTextures.Add(new(Api9: this.textureManager.GetIcon(uint.Parse(this.iconId), flags)));
|
||||
this.addedTextures.Add(new(Api9: this.TextureManagerForApi9.GetIcon(uint.Parse(this.iconId), flags)));
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
|
|
@ -283,8 +285,9 @@ internal class TexWidget : IDataWindowWidget
|
|||
{
|
||||
this.addedTextures.Add(
|
||||
new(
|
||||
Api10: this.textureManager.GetFromGameIconAsync(
|
||||
new(uint.Parse(this.iconId), this.hq, this.hiRes))));
|
||||
Api10: this.textureManager
|
||||
.GetFromGameIcon(new(uint.Parse(this.iconId), this.hq, this.hiRes))
|
||||
.RentAsync()));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
|
@ -300,12 +303,12 @@ internal class TexWidget : IDataWindowWidget
|
|||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (ImGui.Button("Load Tex (API9)"))
|
||||
this.addedTextures.Add(new(Api9: this.textureManager.GetTextureFromGame(this.inputTexPath)));
|
||||
this.addedTextures.Add(new(Api9: this.TextureManagerForApi9.GetTextureFromGame(this.inputTexPath)));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load Tex (Async)"))
|
||||
this.addedTextures.Add(new(Api10: this.textureManager.GetFromGameAsync(this.inputTexPath)));
|
||||
this.addedTextures.Add(new(Api10: this.textureManager.GetFromGame(this.inputTexPath).RentAsync()));
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load Tex (Immediate)"))
|
||||
|
|
@ -320,12 +323,12 @@ internal class TexWidget : IDataWindowWidget
|
|||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (ImGui.Button("Load File (API9)"))
|
||||
this.addedTextures.Add(new(Api9: this.textureManager.GetTextureFromFile(new(this.inputFilePath))));
|
||||
this.addedTextures.Add(new(Api9: this.TextureManagerForApi9.GetTextureFromFile(new(this.inputFilePath))));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load File (Async)"))
|
||||
this.addedTextures.Add(new(Api10: this.textureManager.GetFromFileAsync(this.inputFilePath)));
|
||||
this.addedTextures.Add(new(Api10: this.textureManager.GetFromFile(this.inputFilePath).RentAsync()));
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Load File (Immediate)"))
|
||||
|
|
@ -430,11 +433,11 @@ internal class TexWidget : IDataWindowWidget
|
|||
if (this.Api10 is not null)
|
||||
return this.Api10.IsCompletedSuccessfully ? this.Api10.Result : null;
|
||||
if (this.Api10ImmGameIcon is not null)
|
||||
return tp.ImmediateGetFromGameIcon(this.Api10ImmGameIcon.Value);
|
||||
return tp.GetFromGameIcon(this.Api10ImmGameIcon.Value).GetWrap();
|
||||
if (this.Api10ImmGamePath is not null)
|
||||
return tp.ImmediateGetFromGame(this.Api10ImmGamePath);
|
||||
return tp.GetFromGame(this.Api10ImmGamePath).GetWrap();
|
||||
if (this.Api10ImmFile is not null)
|
||||
return tp.ImmediateGetFromFile(this.Api10ImmFile);
|
||||
return tp.GetFromFile(this.Api10ImmFile).GetWrap();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ internal class PluginImageCache : IDisposable, IServiceType
|
|||
// FIXME(goat): This is a hack around this call failing randomly in certain situations. Might be related to not being called on the main thread.
|
||||
try
|
||||
{
|
||||
image = await textureManager.GetFromImageAsync(bytes);
|
||||
image = await textureManager.CreateFromImageAsync(bytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1745,7 +1745,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
|||
|
||||
if (!this.testerIconPath.IsNullOrEmpty())
|
||||
{
|
||||
this.testerIcon = tm.GetFromFileAsync(this.testerIconPath);
|
||||
this.testerIcon = tm.GetFromFile(this.testerIconPath).RentAsync();
|
||||
}
|
||||
|
||||
this.testerImages = new Task<IDalamudTextureWrap>?[this.testerImagePaths.Length];
|
||||
|
|
@ -1756,7 +1756,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
|||
continue;
|
||||
|
||||
_ = this.testerImages[i]?.ToContentDisposedTask();
|
||||
this.testerImages[i] = tm.GetFromFileAsync(this.testerImagePaths[i]);
|
||||
this.testerImages[i] = tm.GetFromFile(this.testerImagePaths[i]).RentAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -592,7 +592,7 @@ internal sealed partial class FontAtlasFactory
|
|||
}
|
||||
else if (texture.TexPixelsRGBA32 is not null)
|
||||
{
|
||||
var wrap = this.factory.TextureManager.GetFromRaw(
|
||||
var wrap = this.factory.TextureManager.CreateFromRaw(
|
||||
RawImageSpecification.Rgba32(width, height),
|
||||
new(texture.TexPixelsRGBA32, width * height * 4));
|
||||
this.data.AddExistingTexture(wrap);
|
||||
|
|
@ -632,7 +632,7 @@ internal sealed partial class FontAtlasFactory
|
|||
}
|
||||
}
|
||||
|
||||
var wrap = this.factory.TextureManager.GetFromRaw(
|
||||
var wrap = this.factory.TextureManager.CreateFromRaw(
|
||||
new(
|
||||
width,
|
||||
height,
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ internal sealed partial class FontAtlasFactory
|
|||
}
|
||||
|
||||
return this.scopedFinalizer.Add(
|
||||
this.TextureManager.GetFromRaw(
|
||||
this.TextureManager.CreateFromRaw(
|
||||
new(
|
||||
texFile.Header.Width,
|
||||
texFile.Header.Height,
|
||||
|
|
|
|||
|
|
@ -387,8 +387,9 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <param name="filePath">The full filepath to the image.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
|
||||
public IDalamudTextureWrap LoadImage(string filePath) => this.TextureProvider.GetFromFileAsync(filePath).Result;
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromFile)}.")]
|
||||
public IDalamudTextureWrap LoadImage(string filePath) =>
|
||||
this.TextureProvider.GetFromFile(filePath).RentAsync().Result;
|
||||
|
||||
/// <summary>
|
||||
/// Loads an image from a byte stream, such as a png downloaded into memory.
|
||||
|
|
@ -396,8 +397,9 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <param name="imageData">A byte array containing the raw image data.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
|
||||
public IDalamudTextureWrap LoadImage(byte[] imageData) => this.TextureProvider.GetFromImageAsync(imageData).Result;
|
||||
[Obsolete($"Use {nameof(ITextureProvider.CreateFromImageAsync)}.")]
|
||||
public IDalamudTextureWrap LoadImage(byte[] imageData) =>
|
||||
this.TextureProvider.CreateFromImageAsync(imageData).Result;
|
||||
|
||||
/// <summary>
|
||||
/// Loads an image from raw unformatted pixel data, with no type or header information. To load formatted data, use <see cref="LoadImage(byte[])"/>.
|
||||
|
|
@ -408,11 +410,11 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <param name="numChannels">The number of channels (bytes per pixel) of the image contained in <paramref name="imageData"/>. This should usually be 4.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromRaw)} or {nameof(ITextureProvider.GetFromRawAsync)}.")]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.CreateFromRaw)} or {nameof(ITextureProvider.CreateFromRawAsync)}.")]
|
||||
public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) =>
|
||||
numChannels switch
|
||||
{
|
||||
4 => this.TextureProvider.GetFromRaw(RawImageSpecification.Rgba32(width, height), imageData),
|
||||
4 => this.TextureProvider.CreateFromRaw(RawImageSpecification.Rgba32(width, height), imageData),
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
|
||||
|
|
@ -430,8 +432,9 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <param name="filePath">The full filepath to the image.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromFileAsync)}.")]
|
||||
public Task<IDalamudTextureWrap> LoadImageAsync(string filePath) => this.TextureProvider.GetFromFileAsync(filePath);
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromFile)}.")]
|
||||
public Task<IDalamudTextureWrap> LoadImageAsync(string filePath) =>
|
||||
this.TextureProvider.GetFromFile(filePath).RentAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously loads an image from a byte stream, such as a png downloaded into memory, when it's possible to do so.
|
||||
|
|
@ -439,9 +442,9 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <param name="imageData">A byte array containing the raw image data.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromImageAsync)}.")]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.CreateFromImageAsync)}.")]
|
||||
public Task<IDalamudTextureWrap> LoadImageAsync(byte[] imageData) =>
|
||||
this.TextureProvider.GetFromImageAsync(imageData);
|
||||
this.TextureProvider.CreateFromImageAsync(imageData);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously loads an image from raw unformatted pixel data, with no type or header information, when it's possible to do so. To load formatted data, use <see cref="LoadImage(byte[])"/>.
|
||||
|
|
@ -452,11 +455,11 @@ public sealed class UiBuilder : IDisposable
|
|||
/// <param name="numChannels">The number of channels (bytes per pixel) of the image contained in <paramref name="imageData"/>. This should usually be 4.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> object wrapping the created image. Use <see cref="TextureWrap.ImGuiHandle"/> inside ImGui.Image().</returns>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.GetFromRawAsync)}.")]
|
||||
[Obsolete($"Use {nameof(ITextureProvider.CreateFromRawAsync)}.")]
|
||||
public Task<IDalamudTextureWrap> LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) =>
|
||||
numChannels switch
|
||||
{
|
||||
4 => this.TextureProvider.GetFromRawAsync(RawImageSpecification.Rgba32(width, height), imageData),
|
||||
4 => this.TextureProvider.CreateFromRawAsync(RawImageSpecification.Rgba32(width, height), imageData),
|
||||
_ => Task.FromException<IDalamudTextureWrap>(new NotSupportedException()),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ public class UldWrapper : IDisposable
|
|||
inputSlice.CopyTo(outputSlice);
|
||||
}
|
||||
|
||||
return this.textureManager.GetFromRaw(RawImageSpecification.Rgba32(part.W, part.H), imageData);
|
||||
return this.textureManager.CreateFromRaw(RawImageSpecification.Rgba32(part.W, part.H), imageData);
|
||||
}
|
||||
|
||||
private (uint Id, int Width, int Height, bool HD, byte[] RgbaData)? GetTexture(string texturePath)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public partial interface ITextureProvider
|
|||
/// Null, if the icon does not exist in the specified configuration, or a texture wrap that can be used
|
||||
/// to render the icon.
|
||||
/// </returns>
|
||||
[Obsolete($"Use {nameof(ImmediateGetFromGameIcon)} or {nameof(GetFromGameIconAsync)}.")]
|
||||
[Obsolete($"Use {nameof(GetFromGameIcon)}.")]
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
public IDalamudTextureWrap? GetIcon(uint iconId, IconFlags flags = IconFlags.HiRes, ClientLanguage? language = null, bool keepAlive = false);
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ public partial interface ITextureProvider
|
|||
/// <param name="path">The path to the texture in the game's VFS.</param>
|
||||
/// <param name="keepAlive">Not used. This parameter is ignored.</param>
|
||||
/// <returns>Null, if the icon does not exist, or a texture wrap that can be used to render the texture.</returns>
|
||||
[Obsolete($"Use {nameof(ImmediateGetFromGame)} or {nameof(GetFromGameAsync)}.")]
|
||||
[Obsolete($"Use {nameof(GetFromGame)}.")]
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
public IDalamudTextureWrap? GetTextureFromGame(string path, bool keepAlive = false);
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ public partial interface ITextureProvider
|
|||
/// <param name="file">The FileInfo describing the image or texture file.</param>
|
||||
/// <param name="keepAlive">Not used. This parameter is ignored.</param>
|
||||
/// <returns>Null, if the file does not exist, or a texture wrap that can be used to render the texture.</returns>
|
||||
[Obsolete($"Use {nameof(ImmediateGetFromFile)} or {nameof(GetFromFileAsync)}.")]
|
||||
[Obsolete($"Use {nameof(GetFromFile)}.")]
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
public IDalamudTextureWrap? GetTextureFromFile(FileInfo file, bool keepAlive = false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,138 +1,52 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal;
|
||||
|
||||
using Lumina.Data.Files;
|
||||
|
||||
namespace Dalamud.Plugin.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service that grants you access to textures you may render via ImGui.
|
||||
/// </summary>
|
||||
/// <summary>Service that grants you access to textures you may render via ImGui.</summary>
|
||||
/// <remarks>
|
||||
/// <b>Immediate functions</b><br />
|
||||
/// Immediate functions do not throw, unless they are called outside the UI thread.<br />
|
||||
/// Instances of <see cref="IDalamudTextureWrap"/> returned from Immediate functions do not have to be disposed.
|
||||
/// Calling <see cref="IDisposable.Dispose"/> on them is a no-op; it is safe to call <see cref="IDisposable.Dispose"/>
|
||||
/// on them, as it will do nothing.<br />
|
||||
/// Use <see cref="ImmediateGetFromGameIcon"/> and alike if you don't care about the load state and dimensions of the
|
||||
/// texture to be loaded. These functions will return a valid texture that is empty (fully transparent).<br />
|
||||
/// Use <see cref="ImmediateTryGetFromGameIcon"/> and alike if you do. These functions will return the load state,
|
||||
/// loaded texture if available, and the load exception on failure.<br />
|
||||
/// <br />
|
||||
/// <b>All other functions</b><br />
|
||||
/// Instances of <see cref="IDalamudTextureWrap"/> or <see cref="Task"/><<see cref="IDalamudTextureWrap"/>>
|
||||
/// returned from all other functions must be <see cref="IDisposable.Dispose"/>d after use.
|
||||
/// <para>
|
||||
/// <b>Get</b> functions will return a shared texture, and the returnd instance of <see cref="ISharedImmediateTexture"/>
|
||||
/// do not require calling <see cref="IDisposable.Dispose"/>, unless a new reference has been created by calling
|
||||
/// <see cref="ISharedImmediateTexture.RentAsync"/>.<br />
|
||||
/// Use <see cref="ISharedImmediateTexture.TryGetWrap"/> and alike to obtain a reference of
|
||||
/// <see cref="IDalamudTextureWrap"/> that will stay valid for the rest of the frame.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Create</b> functions will return a new texture, and the returned instance of <see cref="IDalamudTextureWrap"/>
|
||||
/// must be disposed after use.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public partial interface ITextureProvider
|
||||
{
|
||||
/// <summary>Gets the corresponding game icon for use with the current frame.</summary>
|
||||
/// <param name="lookup">The icon specifier.</param>
|
||||
/// <returns>An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for the current
|
||||
/// frame being drawn.</returns>
|
||||
/// <remarks><see cref="IDisposable.Dispose"/> will be ignored.<br />
|
||||
/// If the file is unavailable, then the returned instance of <see cref="IDalamudTextureWrap"/> will point to an
|
||||
/// empty texture instead.</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
IDalamudTextureWrap ImmediateGetFromGameIcon(in GameIconLookup lookup);
|
||||
/// <summary>Gets a shared texture corresponding to the given game resource icon specifier.</summary>
|
||||
/// <param name="lookup">A game icon specifier.</param>
|
||||
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
|
||||
ISharedImmediateTexture GetFromGameIcon(in GameIconLookup lookup);
|
||||
|
||||
/// <summary>Gets a texture from a file shipped as a part of the game resources for use with the current frame.
|
||||
/// </summary>
|
||||
/// <param name="path">The game-internal path to a .tex, .atex, or an image file such as .png.</param>
|
||||
/// <returns>An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for the current
|
||||
/// frame being drawn.</returns>
|
||||
/// <remarks><see cref="IDisposable.Dispose"/> will be ignored.<br />
|
||||
/// If the file is unavailable, then the returned instance of <see cref="IDalamudTextureWrap"/> will point to an
|
||||
/// empty texture instead.</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
IDalamudTextureWrap ImmediateGetFromGame(string path);
|
||||
/// <summary>Gets a shared texture corresponding to the given path to a game resource.</summary>
|
||||
/// <param name="path">A path to a game resource.</param>
|
||||
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
|
||||
ISharedImmediateTexture GetFromGame(string path);
|
||||
|
||||
/// <summary>Gets a texture from a file on the filesystem for use with the current frame.</summary>
|
||||
/// <param name="file">The filesystem path to a .tex, .atex, or an image file such as .png.</param>
|
||||
/// <returns>An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for the current
|
||||
/// frame being drawn.</returns>
|
||||
/// <remarks><see cref="IDisposable.Dispose"/> will be ignored.<br />
|
||||
/// If the file is unavailable, then the returned instance of <see cref="IDalamudTextureWrap"/> will point to an
|
||||
/// empty texture instead.</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
IDalamudTextureWrap ImmediateGetFromFile(string file);
|
||||
|
||||
/// <summary>Gets the corresponding game icon for use with the current frame.</summary>
|
||||
/// <param name="lookup">The icon specifier.</param>
|
||||
/// <param name="texture">An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for
|
||||
/// the current frame being drawn, or <c>null</c> if texture is not loaded (yet).</param>
|
||||
/// <param name="exception">The load exception, if any.</param>
|
||||
/// <returns><c>true</c> if <paramref name="texture"/> points to the loaded texture; <c>false</c> if the texture is
|
||||
/// still being loaded, or the load has failed.</returns>
|
||||
/// <remarks><see cref="IDisposable.Dispose"/> on the returned <paramref name="texture"/> will be ignored.</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
bool ImmediateTryGetFromGameIcon(
|
||||
in GameIconLookup lookup,
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception);
|
||||
|
||||
/// <summary>Gets a texture from a file shipped as a part of the game resources for use with the current frame.
|
||||
/// </summary>
|
||||
/// <param name="path">The game-internal path to a .tex, .atex, or an image file such as .png.</param>
|
||||
/// <param name="texture">An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for
|
||||
/// the current frame being drawn, or <c>null</c> if texture is not loaded (yet).</param>
|
||||
/// <param name="exception">The load exception, if any.</param>
|
||||
/// <returns><c>true</c> if <paramref name="texture"/> points to the loaded texture; <c>false</c> if the texture is
|
||||
/// still being loaded, or the load has failed.</returns>
|
||||
/// <remarks><see cref="IDisposable.Dispose"/> on the returned <paramref name="texture"/> will be ignored.</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
bool ImmediateTryGetFromGame(
|
||||
string path,
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception);
|
||||
|
||||
/// <summary>Gets a texture from a file on the filesystem for use with the current frame.</summary>
|
||||
/// <param name="file">The filesystem path to a .tex, .atex, or an image file such as .png.</param>
|
||||
/// <param name="texture">An instance of <see cref="IDalamudTextureWrap"/> that is guaranteed to be available for
|
||||
/// the current frame being drawn, or <c>null</c> if texture is not loaded (yet).</param>
|
||||
/// <param name="exception">The load exception, if any.</param>
|
||||
/// <returns><c>true</c> if <paramref name="texture"/> points to the loaded texture; <c>false</c> if the texture is
|
||||
/// still being loaded, or the load has failed.</returns>
|
||||
/// <remarks><see cref="IDisposable.Dispose"/> on the returned <paramref name="texture"/> will be ignored.</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when called outside the UI thread.</exception>
|
||||
bool ImmediateTryGetFromFile(
|
||||
string file,
|
||||
[NotNullWhen(true)] out IDalamudTextureWrap? texture,
|
||||
out Exception? exception);
|
||||
|
||||
/// <summary>Gets the corresponding game icon for use with the current frame.</summary>
|
||||
/// <param name="lookup">The icon specifier.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
Task<IDalamudTextureWrap> GetFromGameIconAsync(
|
||||
in GameIconLookup lookup,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Gets a texture from a file shipped as a part of the game resources.</summary>
|
||||
/// <param name="path">The game-internal path to a .tex, .atex, or an image file such as .png.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
Task<IDalamudTextureWrap> GetFromGameAsync(
|
||||
string path,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Gets a texture from a file on the filesystem.</summary>
|
||||
/// <param name="file">The filesystem path to a .tex, .atex, or an image file such as .png.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
Task<IDalamudTextureWrap> GetFromFileAsync(
|
||||
string file,
|
||||
CancellationToken cancellationToken = default);
|
||||
/// <summary>Gets a shared texture corresponding to the given file on the filesystem.</summary>
|
||||
/// <param name="path">A path to a file on the filesystem.</param>
|
||||
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
|
||||
ISharedImmediateTexture GetFromFile(string path);
|
||||
|
||||
/// <summary>Gets a texture from the given bytes, trying to interpret it as a .tex file or other well-known image
|
||||
/// files, such as .png.</summary>
|
||||
/// <param name="bytes">The bytes to load.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
Task<IDalamudTextureWrap> GetFromImageAsync(
|
||||
Task<IDalamudTextureWrap> CreateFromImageAsync(
|
||||
ReadOnlyMemory<byte> bytes,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
|
|
@ -144,7 +58,7 @@ public partial interface ITextureProvider
|
|||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
/// <remarks><paramref name="stream"/> will be closed or not only according to <paramref name="leaveOpen"/>;
|
||||
/// <paramref name="cancellationToken"/> is irrelevant in closing the stream.</remarks>
|
||||
Task<IDalamudTextureWrap> GetFromImageAsync(
|
||||
Task<IDalamudTextureWrap> CreateFromImageAsync(
|
||||
Stream stream,
|
||||
bool leaveOpen = false,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
|
@ -153,7 +67,7 @@ public partial interface ITextureProvider
|
|||
/// <param name="specs">The specifications for the raw bitmap.</param>
|
||||
/// <param name="bytes">The bytes to load.</param>
|
||||
/// <returns>The texture loaded from the supplied raw bitmap. Dispose after use.</returns>
|
||||
IDalamudTextureWrap GetFromRaw(
|
||||
IDalamudTextureWrap CreateFromRaw(
|
||||
RawImageSpecification specs,
|
||||
ReadOnlySpan<byte> bytes);
|
||||
|
||||
|
|
@ -162,7 +76,7 @@ public partial interface ITextureProvider
|
|||
/// <param name="bytes">The bytes to load.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
Task<IDalamudTextureWrap> GetFromRawAsync(
|
||||
Task<IDalamudTextureWrap> CreateFromRawAsync(
|
||||
RawImageSpecification specs,
|
||||
ReadOnlyMemory<byte> bytes,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
|
@ -175,7 +89,7 @@ public partial interface ITextureProvider
|
|||
/// <returns>A <see cref="Task{TResult}"/> containing the loaded texture on success. Dispose after use.</returns>
|
||||
/// <remarks><paramref name="stream"/> will be closed or not only according to <paramref name="leaveOpen"/>;
|
||||
/// <paramref name="cancellationToken"/> is irrelevant in closing the stream.</remarks>
|
||||
Task<IDalamudTextureWrap> GetFromRawAsync(
|
||||
Task<IDalamudTextureWrap> CreateFromRawAsync(
|
||||
RawImageSpecification specs,
|
||||
Stream stream,
|
||||
bool leaveOpen = false,
|
||||
|
|
@ -196,22 +110,31 @@ public partial interface ITextureProvider
|
|||
/// <param name="path">The resolved path.</param>
|
||||
/// <returns><c>true</c> if the corresponding file exists and <paramref name="path"/> has been set.</returns>
|
||||
bool TryGetIconPath(in GameIconLookup lookup, [NotNullWhen(true)] out string? path);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a texture handle for the specified Lumina <see cref="TexFile"/>.
|
||||
/// Alias for fetching <see cref="Task{TResult}.Result"/> from <see cref="GetFromTexFileAsync"/>.
|
||||
/// Alias for fetching <see cref="Task{TResult}.Result"/> from <see cref="CreateFromTexFileAsync"/>.
|
||||
/// </summary>
|
||||
/// <param name="file">The texture to obtain a handle to.</param>
|
||||
/// <returns>A texture wrap that can be used to render the texture. Dispose after use.</returns>
|
||||
IDalamudTextureWrap GetTexture(TexFile file);
|
||||
|
||||
/// <remarks>Alias for <see cref="CreateFromTexFile"/>.</remarks>
|
||||
IDalamudTextureWrap GetTexture(TexFile file) => this.CreateFromTexFile(file);
|
||||
|
||||
/// <summary>
|
||||
/// Get a texture handle for the specified Lumina <see cref="TexFile"/>.
|
||||
/// Alias for fetching <see cref="Task{TResult}.Result"/> from <see cref="CreateFromTexFileAsync"/>.
|
||||
/// </summary>
|
||||
/// <param name="file">The texture to obtain a handle to.</param>
|
||||
/// <returns>A texture wrap that can be used to render the texture. Dispose after use.</returns>
|
||||
IDalamudTextureWrap CreateFromTexFile(TexFile file);
|
||||
|
||||
/// <summary>
|
||||
/// Get a texture handle for the specified Lumina <see cref="TexFile"/>.
|
||||
/// </summary>
|
||||
/// <param name="file">The texture to obtain a handle to.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A texture wrap that can be used to render the texture. Dispose after use.</returns>
|
||||
Task<IDalamudTextureWrap> GetFromTexFileAsync(
|
||||
Task<IDalamudTextureWrap> CreateFromTexFileAsync(
|
||||
TexFile file,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
|
|
|
|||
|
|
@ -309,10 +309,10 @@ internal sealed class DalamudAssetManager : IServiceType, IDisposable, IDalamudA
|
|||
stream.ReadExactly(buf, 0, length);
|
||||
var image = purpose switch
|
||||
{
|
||||
DalamudAssetPurpose.TextureFromPng => await tm.GetFromImageAsync(buf),
|
||||
DalamudAssetPurpose.TextureFromPng => await tm.CreateFromImageAsync(buf),
|
||||
DalamudAssetPurpose.TextureFromRaw =>
|
||||
asset.GetAttribute<DalamudAssetRawTextureAttribute>() is { } raw
|
||||
? await tm.GetFromRawAsync(raw.Specification, buf)
|
||||
? await tm.CreateFromRawAsync(raw.Specification, buf)
|
||||
: throw new InvalidOperationException(
|
||||
"TextureFromRaw must accompany a DalamudAssetRawTextureAttribute."),
|
||||
_ => null,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue