mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-16 12:57:44 +01:00
more
This commit is contained in:
parent
9ba0a297c9
commit
70eecdaaef
30 changed files with 767 additions and 345 deletions
55
Dalamud/Interface/Textures/GameIconLookup.cs
Normal file
55
Dalamud/Interface/Textures/GameIconLookup.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System.Text;
|
||||
|
||||
namespace Dalamud.Interface.Textures;
|
||||
|
||||
/// <summary>Represents a lookup for a game icon.</summary>
|
||||
public readonly record struct GameIconLookup
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="GameIconLookup"/> class.</summary>
|
||||
/// <param name="iconId">The icon ID.</param>
|
||||
/// <param name="itemHq">Whether the HQ icon is requested, where HQ is in the context of items.</param>
|
||||
/// <param name="hiRes">Whether the high-resolution icon is requested.</param>
|
||||
/// <param name="language">The language of the icon to load.</param>
|
||||
public GameIconLookup(uint iconId, bool itemHq = false, bool hiRes = true, ClientLanguage? language = null)
|
||||
{
|
||||
this.IconId = iconId;
|
||||
this.ItemHq = itemHq;
|
||||
this.HiRes = hiRes;
|
||||
this.Language = language;
|
||||
}
|
||||
|
||||
public static implicit operator GameIconLookup(int iconId) => new(checked((uint)iconId));
|
||||
|
||||
public static implicit operator GameIconLookup(uint iconId) => new(iconId);
|
||||
|
||||
/// <summary>Gets the icon ID.</summary>
|
||||
public uint IconId { get; init; }
|
||||
|
||||
/// <summary>Gets a value indicating whether the HQ icon is requested, where HQ is in the context of items.</summary>
|
||||
public bool ItemHq { get; init; }
|
||||
|
||||
/// <summary>Gets a value indicating whether the high-resolution icon is requested.</summary>
|
||||
public bool HiRes { get; init; }
|
||||
|
||||
/// <summary>Gets the language of the icon to load.</summary>
|
||||
/// <remarks>
|
||||
/// <para><c>null</c> will use the active game language.</para>
|
||||
/// <para>If the specified resource does not have variants per language, the language-neutral texture will be used.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public ClientLanguage? Language { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(nameof(GameIconLookup)).Append('(').Append(this.IconId);
|
||||
if (this.ItemHq)
|
||||
sb.Append(", HQ");
|
||||
if (this.HiRes)
|
||||
sb.Append(", HR1");
|
||||
if (this.Language is not null)
|
||||
sb.Append(", ").Append(Enum.GetName(this.Language.Value));
|
||||
return sb.Append(')').ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.Textures;
|
||||
|
||||
/// <summary>Describes how to take a texture of an existing ImGui viewport.</summary>
|
||||
|
|
@ -44,6 +47,30 @@ public record struct ImGuiViewportTextureArgs()
|
|||
/// <summary>Gets the effective value of <see cref="Uv1"/>.</summary>
|
||||
internal Vector2 Uv1Effective => this.Uv1 == Vector2.Zero ? Vector2.One : this.Uv1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(nameof(ImGuiViewportTextureArgs)).Append('(');
|
||||
sb.Append($"0x{this.ViewportId:X}");
|
||||
if (this.AutoUpdate)
|
||||
sb.Append($", {nameof(this.AutoUpdate)}");
|
||||
if (this.TakeBeforeImGuiRender)
|
||||
sb.Append($", {nameof(this.TakeBeforeImGuiRender)}");
|
||||
if (this.KeepTransparency)
|
||||
sb.Append($", {nameof(this.KeepTransparency)}");
|
||||
|
||||
if (this.Uv0 != Vector2.Zero || this.Uv1Effective != Vector2.One)
|
||||
{
|
||||
sb.Append(", ")
|
||||
.Append(this.Uv0.ToString())
|
||||
.Append('-')
|
||||
.Append(this.Uv1.ToString());
|
||||
}
|
||||
|
||||
return sb.Append(')').ToString();
|
||||
}
|
||||
|
||||
/// <summary>Checks the properties and throws an exception if values are invalid.</summary>
|
||||
internal void ThrowOnInvalidValues()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ internal sealed class FileSystemSharedImmediateTexture : SharedImmediateTexture
|
|||
/// <summary>Creates a new placeholder instance of <see cref="GamePathSharedImmediateTexture"/>.</summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
/// <remarks>Only to be used from <see cref="TextureManager.SharedTextureManager.GetFromFile"/>.</remarks>
|
||||
public static SharedImmediateTexture CreatePlaceholder(string path) => new FileSystemSharedImmediateTexture(path);
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ internal sealed class GamePathSharedImmediateTexture : SharedImmediateTexture
|
|||
/// <summary>Creates a new placeholder instance of <see cref="GamePathSharedImmediateTexture"/>.</summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
/// <remarks>Only to be used from <see cref="TextureManager.SharedTextureManager.GetFromGame"/>.</remarks>
|
||||
public static SharedImmediateTexture CreatePlaceholder(string path) => new GamePathSharedImmediateTexture(path);
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ internal sealed class ManifestResourceSharedImmediateTexture : SharedImmediateTe
|
|||
/// <summary>Creates a new placeholder instance of <see cref="ManifestResourceSharedImmediateTexture"/>.</summary>
|
||||
/// <param name="args">The arguments to pass to the constructor.</param>
|
||||
/// <returns>The new instance.</returns>
|
||||
/// <remarks>Only to be used from <see cref="TextureManager.SharedTextureManager.GetFromManifestResource"/>.
|
||||
/// </remarks>
|
||||
public static SharedImmediateTexture CreatePlaceholder((Assembly Assembly, string Name) args) =>
|
||||
new ManifestResourceSharedImmediateTexture(args.Assembly, args.Name);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
using Dalamud.Plugin.Internal.Types;
|
||||
using Dalamud.Storage.Assets;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -22,6 +24,7 @@ internal abstract class SharedImmediateTexture
|
|||
private static long instanceCounter;
|
||||
|
||||
private readonly object reviveLock = new();
|
||||
private readonly List<LocalPlugin> ownerPlugins = new();
|
||||
|
||||
private bool resourceReleased;
|
||||
private int refCount;
|
||||
|
|
@ -43,10 +46,11 @@ internal abstract class SharedImmediateTexture
|
|||
this.IsOpportunistic = true;
|
||||
this.resourceReleased = true;
|
||||
this.FirstRequestedTick = this.LatestRequestedTick = Environment.TickCount64;
|
||||
this.PublicUseInstance = new(this);
|
||||
}
|
||||
|
||||
/// <summary>Gets the list of owner plugins.</summary>
|
||||
public List<LocalPlugin> OwnerPlugins { get; } = new();
|
||||
/// <summary>Gets a wrapper for this instance which disables resource reference management.</summary>
|
||||
public PureImpl PublicUseInstance { get; }
|
||||
|
||||
/// <summary>Gets the instance ID. Debug use only.</summary>
|
||||
public long InstanceIdForDebug { get; }
|
||||
|
|
@ -280,15 +284,15 @@ internal abstract class SharedImmediateTexture
|
|||
return this.availableOnAccessWrapForApi9;
|
||||
}
|
||||
|
||||
/// <summary>Adds a plugin to <see cref="OwnerPlugins"/>, in a thread-safe way.</summary>
|
||||
/// <summary>Adds a plugin to <see cref="ownerPlugins"/>, in a thread-safe way.</summary>
|
||||
/// <param name="plugin">The plugin to add.</param>
|
||||
public void AddOwnerPlugin(LocalPlugin plugin)
|
||||
{
|
||||
lock (this.OwnerPlugins)
|
||||
lock (this.ownerPlugins)
|
||||
{
|
||||
if (!this.OwnerPlugins.Contains(plugin))
|
||||
if (!this.ownerPlugins.Contains(plugin))
|
||||
{
|
||||
this.OwnerPlugins.Add(plugin);
|
||||
this.ownerPlugins.Add(plugin);
|
||||
this.UnderlyingWrap?.ContinueWith(
|
||||
r =>
|
||||
{
|
||||
|
|
@ -314,14 +318,14 @@ internal abstract class SharedImmediateTexture
|
|||
protected void LoadUnderlyingWrap()
|
||||
{
|
||||
int addLen;
|
||||
lock (this.OwnerPlugins)
|
||||
lock (this.ownerPlugins)
|
||||
{
|
||||
this.UnderlyingWrap = Service<TextureManager>.Get().DynamicPriorityTextureLoader.LoadAsync(
|
||||
this,
|
||||
this.CreateTextureAsync,
|
||||
this.LoadCancellationToken);
|
||||
|
||||
addLen = this.OwnerPlugins.Count;
|
||||
addLen = this.ownerPlugins.Count;
|
||||
}
|
||||
|
||||
if (addLen == 0)
|
||||
|
|
@ -331,8 +335,11 @@ internal abstract class SharedImmediateTexture
|
|||
{
|
||||
if (!r.IsCompletedSuccessfully)
|
||||
return;
|
||||
foreach (var op in this.OwnerPlugins.Take(addLen))
|
||||
Service<TextureManager>.Get().Blame(r.Result, op);
|
||||
lock (this.ownerPlugins)
|
||||
{
|
||||
foreach (var op in this.ownerPlugins.Take(addLen))
|
||||
Service<TextureManager>.Get().Blame(r.Result, op);
|
||||
}
|
||||
},
|
||||
default(CancellationToken));
|
||||
}
|
||||
|
|
@ -427,6 +434,52 @@ internal abstract class SharedImmediateTexture
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>A wrapper around <see cref="SharedImmediateTexture"/>, to prevent external consumers from mistakenly
|
||||
/// calling <see cref="IDisposable.Dispose"/> or <see cref="IRefCountable.Release"/>.</summary>
|
||||
internal sealed class PureImpl : ISharedImmediateTexture
|
||||
{
|
||||
private readonly SharedImmediateTexture inner;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="PureImpl"/> class.</summary>
|
||||
/// <param name="inner">The actual instance.</param>
|
||||
public PureImpl(SharedImmediateTexture inner) => this.inner = inner;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IDalamudTextureWrap GetWrapOrEmpty() =>
|
||||
this.inner.GetWrapOrEmpty();
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull(nameof(defaultWrap))]
|
||||
public IDalamudTextureWrap? GetWrapOrDefault(IDalamudTextureWrap? defaultWrap = null) =>
|
||||
this.inner.GetWrapOrDefault(defaultWrap);
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetWrap([NotNullWhen(true)] out IDalamudTextureWrap? texture, out Exception? exception) =>
|
||||
this.inner.TryGetWrap(out texture, out exception);
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Task<IDalamudTextureWrap> RentAsync(CancellationToken cancellationToken = default) =>
|
||||
this.inner.RentAsync(cancellationToken);
|
||||
|
||||
/// <inheritdoc cref="SharedImmediateTexture.GetAvailableOnAccessWrapForApi9"/>
|
||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IDalamudTextureWrap? GetAvailableOnAccessWrapForApi9() =>
|
||||
this.inner.GetAvailableOnAccessWrapForApi9();
|
||||
|
||||
/// <inheritdoc cref="SharedImmediateTexture.AddOwnerPlugin"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddOwnerPlugin(LocalPlugin plugin) =>
|
||||
this.inner.AddOwnerPlugin(plugin);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{this.inner}({nameof(PureImpl)})";
|
||||
}
|
||||
|
||||
/// <summary>Same with <see cref="DisposeSuppressingTextureWrap"/>, but with a custom implementation of
|
||||
/// <see cref="CreateWrapSharingLowLevelResource"/>.</summary>
|
||||
private sealed class NotOwnedTextureWrap : DisposeSuppressingTextureWrap
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
|
@ -18,11 +17,12 @@ namespace Dalamud.Interface.Textures.Internal;
|
|||
/// <summary>Service responsible for loading and disposing ImGui texture wraps.</summary>
|
||||
internal sealed partial class TextureManager
|
||||
{
|
||||
private readonly List<BlameTag> blameTracker = new();
|
||||
|
||||
/// <summary>A wrapper for underlying texture2D resources.</summary>
|
||||
public interface IBlameableDalamudTextureWrap : IDalamudTextureWrap
|
||||
{
|
||||
/// <summary>Gets the address of the native resource.</summary>
|
||||
public nint ResourceAddress { get; }
|
||||
|
||||
/// <summary>Gets the name of the underlying resource of this texture wrap.</summary>
|
||||
public string Name { get; }
|
||||
|
||||
|
|
@ -31,13 +31,27 @@ internal sealed partial class TextureManager
|
|||
|
||||
/// <summary>Gets the list of owner plugins.</summary>
|
||||
public List<LocalPlugin> OwnerPlugins { get; }
|
||||
|
||||
/// <summary>Gets the raw image specification.</summary>
|
||||
public RawImageSpecification RawSpecs { get; }
|
||||
|
||||
/// <summary>Tests whether the tag and the underlying resource are released or should be released.</summary>
|
||||
/// <returns><c>true</c> if there are no more remaining references to this instance.</returns>
|
||||
bool TestIsReleasedOrShouldRelease();
|
||||
}
|
||||
|
||||
/// <summary>Gets all the loaded textures from plugins.</summary>
|
||||
/// <returns>The enumerable that goes through all textures and relevant plugins.</returns>
|
||||
/// <summary>Gets the list containing all the loaded textures from plugins.</summary>
|
||||
/// <remarks>Returned value must be used inside a lock.</remarks>
|
||||
[SuppressMessage("ReSharper", "InconsistentlySynchronizedField", Justification = "Caller locks the return value.")]
|
||||
public IReadOnlyList<IBlameableDalamudTextureWrap> AllBlamesForDebug => this.blameTracker;
|
||||
public List<IBlameableDalamudTextureWrap> BlameTracker { get; } = new();
|
||||
|
||||
/// <summary>Gets the blame for a texture wrap.</summary>
|
||||
/// <param name="textureWrap">The texture wrap.</param>
|
||||
/// <returns>The blame, if it exists.</returns>
|
||||
public unsafe IBlameableDalamudTextureWrap? GetBlame(IDalamudTextureWrap textureWrap)
|
||||
{
|
||||
using var wrapAux = new WrapAux(textureWrap, true);
|
||||
return BlameTag.Get(wrapAux.ResPtr);
|
||||
}
|
||||
|
||||
/// <summary>Puts a plugin on blame for a texture.</summary>
|
||||
/// <param name="textureWrap">The texture.</param>
|
||||
|
|
@ -59,7 +73,7 @@ internal sealed partial class TextureManager
|
|||
}
|
||||
|
||||
using var wrapAux = new WrapAux(textureWrap, true);
|
||||
var blame = BlameTag.From(wrapAux.ResPtr, out var isNew);
|
||||
var blame = BlameTag.GetOrCreate(wrapAux.ResPtr, out var isNew);
|
||||
|
||||
if (ownerPlugin is not null)
|
||||
{
|
||||
|
|
@ -69,8 +83,8 @@ internal sealed partial class TextureManager
|
|||
|
||||
if (isNew)
|
||||
{
|
||||
lock (this.blameTracker)
|
||||
this.blameTracker.Add(blame);
|
||||
lock (this.BlameTracker)
|
||||
this.BlameTracker.Add(blame);
|
||||
}
|
||||
|
||||
return textureWrap;
|
||||
|
|
@ -96,13 +110,13 @@ internal sealed partial class TextureManager
|
|||
}
|
||||
|
||||
using var wrapAux = new WrapAux(textureWrap, true);
|
||||
var blame = BlameTag.From(wrapAux.ResPtr, out var isNew);
|
||||
blame.Name = name;
|
||||
var blame = BlameTag.GetOrCreate(wrapAux.ResPtr, out var isNew);
|
||||
blame.Name = name.Length <= 1024 ? name : $"{name[..1024]}...";
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
lock (this.blameTracker)
|
||||
this.blameTracker.Add(blame);
|
||||
lock (this.BlameTracker)
|
||||
this.BlameTracker.Add(blame);
|
||||
}
|
||||
|
||||
return textureWrap;
|
||||
|
|
@ -110,15 +124,15 @@ internal sealed partial class TextureManager
|
|||
|
||||
private void BlameTrackerUpdate(IFramework unused)
|
||||
{
|
||||
lock (this.blameTracker)
|
||||
lock (this.BlameTracker)
|
||||
{
|
||||
for (var i = 0; i < this.blameTracker.Count;)
|
||||
for (var i = 0; i < this.BlameTracker.Count;)
|
||||
{
|
||||
var entry = this.blameTracker[i];
|
||||
var entry = this.BlameTracker[i];
|
||||
if (entry.TestIsReleasedOrShouldRelease())
|
||||
{
|
||||
this.blameTracker[i] = this.blameTracker[^1];
|
||||
this.blameTracker.RemoveAt(this.blameTracker.Count - 1);
|
||||
this.BlameTracker[i] = this.BlameTracker[^1];
|
||||
this.BlameTracker.RemoveAt(this.BlameTracker.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -220,12 +234,22 @@ internal sealed partial class TextureManager
|
|||
/// <inheritdoc/>
|
||||
public List<LocalPlugin> OwnerPlugins { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public nint ResourceAddress => (nint)this.tex2D;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; set; } = "<unnamed>";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DXGI_FORMAT Format => this.desc.Format;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RawImageSpecification RawSpecs => new(
|
||||
(int)this.desc.Width,
|
||||
(int)this.desc.Height,
|
||||
(int)this.desc.Format,
|
||||
0);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr ImGuiHandle
|
||||
{
|
||||
|
|
@ -267,7 +291,23 @@ internal sealed partial class TextureManager
|
|||
/// <param name="isNew"><c>true</c> if the tracker is new.</param>
|
||||
/// <typeparam name="T">A COM object type.</typeparam>
|
||||
/// <returns>A new instance of <see cref="BlameTag"/>.</returns>
|
||||
public static BlameTag From<T>(T* trackWhat, out bool isNew) where T : unmanaged, IUnknown.Interface
|
||||
public static BlameTag GetOrCreate<T>(T* trackWhat, out bool isNew) where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
if (Get(trackWhat) is { } v)
|
||||
{
|
||||
isNew = false;
|
||||
return v;
|
||||
}
|
||||
|
||||
isNew = true;
|
||||
return new((IUnknown*)trackWhat);
|
||||
}
|
||||
|
||||
/// <summary>Gets an existing instance of <see cref="BlameTag"/> for the given resource.</summary>
|
||||
/// <param name="trackWhat">The COM object to track.</param>
|
||||
/// <typeparam name="T">A COM object type.</typeparam>
|
||||
/// <returns>An existing instance of <see cref="BlameTag"/>.</returns>
|
||||
public static BlameTag? Get<T>(T* trackWhat) where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
using var deviceChild = default(ComPtr<ID3D11DeviceChild>);
|
||||
fixed (Guid* piid = &IID.IID_ID3D11DeviceChild)
|
||||
|
|
@ -282,18 +322,15 @@ internal sealed partial class TextureManager
|
|||
if (ToManagedObject(existingTag) is { } existingTagInstance)
|
||||
{
|
||||
existingTagInstance.Release();
|
||||
isNew = false;
|
||||
return existingTagInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isNew = true;
|
||||
return new((IUnknown*)trackWhat);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the tag and the underlying resource are released or should be released.</summary>
|
||||
/// <returns><c>true</c> if there are no more remaining references to this instance.</returns>
|
||||
/// <inheritdoc/>
|
||||
public bool TestIsReleasedOrShouldRelease()
|
||||
{
|
||||
if (this.srvDebugPreviewExpiryTick <= Environment.TickCount64)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
using Dalamud.Plugin.Internal.Types;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -82,7 +83,7 @@ internal sealed partial class TextureManager
|
|||
this.BlameSetName(
|
||||
outWrap,
|
||||
debugName ??
|
||||
$"{nameof(this.CreateFromExistingTextureAsync)}({nameof(wrap)}, {nameof(args)}, {nameof(leaveWrapOpen)}, {nameof(cancellationToken)})");
|
||||
$"{nameof(this.CreateFromExistingTextureAsync)}({wrap}, {args})");
|
||||
return outWrap;
|
||||
}
|
||||
},
|
||||
|
|
@ -136,59 +137,57 @@ internal sealed partial class TextureManager
|
|||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return await this.interfaceManager.RunBeforeImGuiRender(
|
||||
() => ExtractMappedResource(wrapAux, tex2D, cancellationToken));
|
||||
|
||||
// ID3D11DeviceContext is not a threadsafe resource, and it must be used from the UI thread.
|
||||
return await this.RunDuringPresent(() => ExtractMappedResource(tex2D, cancellationToken));
|
||||
|
||||
static unsafe (RawImageSpecification Specification, byte[] RawData) ExtractMappedResource(
|
||||
in WrapAux wrapAux,
|
||||
ComPtr<ID3D11Texture2D> tex2D,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ID3D11Resource* mapWhat = null;
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
tex2D.Get()->GetDesc(&desc);
|
||||
|
||||
using var device = default(ComPtr<ID3D11Device>);
|
||||
tex2D.Get()->GetDevice(device.GetAddressOf());
|
||||
using var context = default(ComPtr<ID3D11DeviceContext>);
|
||||
device.Get()->GetImmediateContext(context.GetAddressOf());
|
||||
|
||||
using var tmpTex = default(ComPtr<ID3D11Texture2D>);
|
||||
if ((desc.CPUAccessFlags & (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ) == 0)
|
||||
{
|
||||
var tmpTexDesc = desc with
|
||||
{
|
||||
MipLevels = 1,
|
||||
ArraySize = 1,
|
||||
SampleDesc = new(1, 0),
|
||||
Usage = D3D11_USAGE.D3D11_USAGE_STAGING,
|
||||
BindFlags = 0u,
|
||||
CPUAccessFlags = (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ,
|
||||
MiscFlags = 0u,
|
||||
};
|
||||
device.Get()->CreateTexture2D(&tmpTexDesc, null, tmpTex.GetAddressOf()).ThrowOnError();
|
||||
context.Get()->CopyResource((ID3D11Resource*)tmpTex.Get(), (ID3D11Resource*)tex2D.Get());
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
var mapWhat = (ID3D11Resource*)(tmpTex.IsEmpty() ? tex2D.Get() : tmpTex.Get());
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
context.Get()->Map(mapWhat, 0, D3D11_MAP.D3D11_MAP_READ, 0, &mapped).ThrowOnError();
|
||||
|
||||
try
|
||||
{
|
||||
using var tmpTex = default(ComPtr<ID3D11Texture2D>);
|
||||
if ((wrapAux.Desc.CPUAccessFlags & (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ) == 0)
|
||||
{
|
||||
var tmpTexDesc = wrapAux.Desc with
|
||||
{
|
||||
MipLevels = 1,
|
||||
ArraySize = 1,
|
||||
SampleDesc = new(1, 0),
|
||||
Usage = D3D11_USAGE.D3D11_USAGE_STAGING,
|
||||
BindFlags = 0u,
|
||||
CPUAccessFlags = (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ,
|
||||
MiscFlags = 0u,
|
||||
};
|
||||
wrapAux.DevPtr->CreateTexture2D(&tmpTexDesc, null, tmpTex.GetAddressOf()).ThrowOnError();
|
||||
wrapAux.CtxPtr->CopyResource((ID3D11Resource*)tmpTex.Get(), (ID3D11Resource*)tex2D.Get());
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
mapWhat = (ID3D11Resource*)(tmpTex.IsEmpty() ? tex2D.Get() : tmpTex.Get());
|
||||
wrapAux.CtxPtr->Map(
|
||||
mapWhat,
|
||||
0,
|
||||
D3D11_MAP.D3D11_MAP_READ,
|
||||
0,
|
||||
&mapped).ThrowOnError();
|
||||
|
||||
var specs = new RawImageSpecification(
|
||||
(int)wrapAux.Desc.Width,
|
||||
(int)wrapAux.Desc.Height,
|
||||
(int)wrapAux.Desc.Format,
|
||||
(int)mapped.RowPitch);
|
||||
var specs = new RawImageSpecification(desc, mapped.RowPitch);
|
||||
var bytes = new Span<byte>(mapped.pData, checked((int)mapped.DepthPitch)).ToArray();
|
||||
return (specs, bytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mapWhat is not null)
|
||||
wrapAux.CtxPtr->Unmap(mapWhat, 0);
|
||||
context.Get()->Unmap(mapWhat, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -226,35 +225,34 @@ internal sealed partial class TextureManager
|
|||
this.device.Get()->CreateTexture2D(&tex2DCopyTempDesc, null, tex2DCopyTemp.GetAddressOf()).ThrowOnError();
|
||||
}
|
||||
|
||||
await this.interfaceManager.RunBeforeImGuiRender(
|
||||
() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
using var rtvCopyTemp = default(ComPtr<ID3D11RenderTargetView>);
|
||||
var rtvCopyTempDesc = new D3D11_RENDER_TARGET_VIEW_DESC(
|
||||
tex2DCopyTemp,
|
||||
D3D11_RTV_DIMENSION.D3D11_RTV_DIMENSION_TEXTURE2D);
|
||||
this.device.Get()->CreateRenderTargetView(
|
||||
(ID3D11Resource*)tex2DCopyTemp.Get(),
|
||||
&rtvCopyTempDesc,
|
||||
rtvCopyTemp.GetAddressOf()).ThrowOnError();
|
||||
|
||||
wrapAux.CtxPtr->OMSetRenderTargets(1u, rtvCopyTemp.GetAddressOf(), null);
|
||||
this.SimpleDrawer.Draw(
|
||||
wrapAux.CtxPtr,
|
||||
wrapAux.SrvPtr,
|
||||
args.Uv0,
|
||||
args.Uv1Effective);
|
||||
if (args.MakeOpaque)
|
||||
this.SimpleDrawer.StripAlpha(wrapAux.CtxPtr);
|
||||
|
||||
var dummy = default(ID3D11RenderTargetView*);
|
||||
wrapAux.CtxPtr->OMSetRenderTargets(1u, &dummy, null);
|
||||
}
|
||||
});
|
||||
await this.RunDuringPresent(() => DrawSourceTextureToTarget(wrapAux, args, this.SimpleDrawer, tex2DCopyTemp));
|
||||
|
||||
return new(tex2DCopyTemp);
|
||||
|
||||
static unsafe void DrawSourceTextureToTarget(
|
||||
WrapAux wrapAux,
|
||||
TextureModificationArgs args,
|
||||
SimpleDrawerImpl simpleDrawer,
|
||||
ComPtr<ID3D11Texture2D> tex2DCopyTemp)
|
||||
{
|
||||
using var rtvCopyTemp = default(ComPtr<ID3D11RenderTargetView>);
|
||||
var rtvCopyTempDesc = new D3D11_RENDER_TARGET_VIEW_DESC(
|
||||
tex2DCopyTemp,
|
||||
D3D11_RTV_DIMENSION.D3D11_RTV_DIMENSION_TEXTURE2D);
|
||||
wrapAux.DevPtr->CreateRenderTargetView(
|
||||
(ID3D11Resource*)tex2DCopyTemp.Get(),
|
||||
&rtvCopyTempDesc,
|
||||
rtvCopyTemp.GetAddressOf())
|
||||
.ThrowOnError();
|
||||
|
||||
wrapAux.CtxPtr->OMSetRenderTargets(1u, rtvCopyTemp.GetAddressOf(), null);
|
||||
simpleDrawer.Draw(wrapAux.CtxPtr, wrapAux.SrvPtr, args.Uv0, args.Uv1Effective);
|
||||
if (args.MakeOpaque)
|
||||
simpleDrawer.StripAlpha(wrapAux.CtxPtr);
|
||||
|
||||
var dummy = default(ID3D11RenderTargetView*);
|
||||
wrapAux.CtxPtr->OMSetRenderTargets(1u, &dummy, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Auxiliary data from <see cref="IDalamudTextureWrap"/>.</summary>
|
||||
|
|
|
|||
|
|
@ -88,25 +88,28 @@ internal sealed partial class TextureManager
|
|||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromGameIcon"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SharedImmediateTexture GetFromGameIcon(in GameIconLookup lookup) =>
|
||||
public SharedImmediateTexture.PureImpl GetFromGameIcon(in GameIconLookup lookup) =>
|
||||
this.GetFromGame(this.lookupCache.GetOrAdd(lookup, this.GetIconPathByValue));
|
||||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromGame"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SharedImmediateTexture GetFromGame(string path) =>
|
||||
this.gameDict.GetOrAdd(path, GamePathSharedImmediateTexture.CreatePlaceholder);
|
||||
public SharedImmediateTexture.PureImpl GetFromGame(string path) =>
|
||||
this.gameDict.GetOrAdd(path, GamePathSharedImmediateTexture.CreatePlaceholder)
|
||||
.PublicUseInstance;
|
||||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromFile"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SharedImmediateTexture GetFromFile(string path) =>
|
||||
this.fileDict.GetOrAdd(path, FileSystemSharedImmediateTexture.CreatePlaceholder);
|
||||
public SharedImmediateTexture.PureImpl GetFromFile(string path) =>
|
||||
this.fileDict.GetOrAdd(path, FileSystemSharedImmediateTexture.CreatePlaceholder)
|
||||
.PublicUseInstance;
|
||||
|
||||
/// <inheritdoc cref="ITextureProvider.GetFromFile"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SharedImmediateTexture GetFromManifestResource(Assembly assembly, string name) =>
|
||||
public SharedImmediateTexture.PureImpl GetFromManifestResource(Assembly assembly, string name) =>
|
||||
this.manifestResourceDict.GetOrAdd(
|
||||
(assembly, name),
|
||||
ManifestResourceSharedImmediateTexture.CreatePlaceholder);
|
||||
ManifestResourceSharedImmediateTexture.CreatePlaceholder)
|
||||
.PublicUseInstance;
|
||||
|
||||
/// <summary>Invalidates a cached item from <see cref="GetFromGame"/> and <see cref="GetFromGameIcon"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@ internal sealed partial class TextureManager
|
|||
{
|
||||
// See https://github.com/microsoft/DirectXTex/wiki/WIC-I-O-Functions#savetowicmemory-savetowicfile
|
||||
DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT => GUID.GUID_WICPixelFormat128bppRGBAFloat,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT => GUID.GUID_WICPixelFormat96bppRGBFloat,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT => GUID.GUID_WICPixelFormat64bppRGBAHalf,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM => GUID.GUID_WICPixelFormat64bppRGBA,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM => GUID.GUID_WICPixelFormat32bppRGBA1010102XR,
|
||||
|
|
@ -497,10 +498,10 @@ internal sealed partial class TextureManager
|
|||
var outPixelFormat = specs.Format switch
|
||||
{
|
||||
DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT => GUID.GUID_WICPixelFormat128bppRGBAFloat,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT when !this.wicFactory2.IsEmpty() =>
|
||||
GUID.GUID_WICPixelFormat128bppRGBAFloat,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT => GUID.GUID_WICPixelFormat32bppBGRA,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT => GUID.GUID_WICPixelFormat96bppRGBFloat,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT => GUID.GUID_WICPixelFormat128bppRGBAFloat,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM => GUID.GUID_WICPixelFormat64bppBGRA,
|
||||
DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM => GUID.GUID_WICPixelFormat24bppBGR,
|
||||
DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM => GUID.GUID_WICPixelFormat16bppBGRA5551,
|
||||
DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM => GUID.GUID_WICPixelFormat16bppBGR565,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT => GUID.GUID_WICPixelFormat8bppGray,
|
||||
|
|
@ -508,9 +509,25 @@ internal sealed partial class TextureManager
|
|||
DXGI_FORMAT.DXGI_FORMAT_R16_UNORM => GUID.GUID_WICPixelFormat8bppGray,
|
||||
DXGI_FORMAT.DXGI_FORMAT_R8_UNORM => GUID.GUID_WICPixelFormat8bppGray,
|
||||
DXGI_FORMAT.DXGI_FORMAT_A8_UNORM => GUID.GUID_WICPixelFormat8bppGray,
|
||||
_ => GUID.GUID_WICPixelFormat24bppBGR,
|
||||
_ => GUID.GUID_WICPixelFormat32bppBGRA,
|
||||
};
|
||||
|
||||
var accepted = false;
|
||||
foreach (var pfi in new ComponentEnumerable<IWICPixelFormatInfo>(
|
||||
this.wicFactory,
|
||||
WICComponentType.WICPixelFormat))
|
||||
{
|
||||
Guid tmp;
|
||||
if (pfi.Get()->GetFormatGUID(&tmp).FAILED)
|
||||
continue;
|
||||
accepted = tmp == outPixelFormat;
|
||||
if (accepted)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!accepted)
|
||||
outPixelFormat = GUID.GUID_WICPixelFormat32bppBGRA;
|
||||
|
||||
encoder.Get()->Initialize(stream, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache)
|
||||
.ThrowOnError();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
|
@ -613,8 +630,7 @@ internal sealed partial class TextureManager
|
|||
private readonly WICComponentType componentType;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ComponentEnumerable{T}"/> struct.</summary>
|
||||
/// <param name="factory">The WIC factory. Ownership is not transferred.
|
||||
/// </param>
|
||||
/// <param name="factory">The WIC factory. Ownership is not transferred.</param>
|
||||
/// <param name="componentType">The component type to enumerate.</param>
|
||||
public ComponentEnumerable(ComPtr<IWICImagingFactory> factory, WICComponentType componentType)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Dalamud.Data;
|
|||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.Internal.SharedImmediateTextures;
|
||||
using Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -127,7 +128,7 @@ internal sealed partial class TextureManager
|
|||
this.BlameSetName(
|
||||
this.NoThrottleCreateFromImage(bytes.ToArray(), ct),
|
||||
debugName ??
|
||||
$"{nameof(this.CreateFromImageAsync)}({nameof(bytes)}, {nameof(cancellationToken)})"),
|
||||
$"{nameof(this.CreateFromImageAsync)}({bytes.Length:n0}b)"),
|
||||
ct),
|
||||
cancellationToken);
|
||||
|
||||
|
|
@ -146,7 +147,7 @@ internal sealed partial class TextureManager
|
|||
return this.BlameSetName(
|
||||
this.NoThrottleCreateFromImage(ms.GetBuffer(), ct),
|
||||
debugName ??
|
||||
$"{nameof(this.CreateFromImageAsync)}({nameof(stream)}, {nameof(leaveOpen)}, {nameof(cancellationToken)})");
|
||||
$"{nameof(this.CreateFromImageAsync)}(stream)");
|
||||
},
|
||||
cancellationToken,
|
||||
leaveOpen ? null : stream);
|
||||
|
|
@ -159,7 +160,7 @@ internal sealed partial class TextureManager
|
|||
string? debugName = null) =>
|
||||
this.BlameSetName(
|
||||
this.NoThrottleCreateFromRaw(specs, bytes),
|
||||
debugName ?? $"{nameof(this.CreateFromRaw)}({nameof(specs)}, {nameof(bytes)})");
|
||||
debugName ?? $"{nameof(this.CreateFromRaw)}({specs}, {bytes.Length:n0})");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IDalamudTextureWrap> CreateFromRawAsync(
|
||||
|
|
@ -173,7 +174,7 @@ internal sealed partial class TextureManager
|
|||
this.BlameSetName(
|
||||
this.NoThrottleCreateFromRaw(specs, bytes.Span),
|
||||
debugName ??
|
||||
$"{nameof(this.CreateFromRawAsync)}({nameof(specs)}, {nameof(bytes)}, {nameof(cancellationToken)})")),
|
||||
$"{nameof(this.CreateFromRawAsync)}({specs}, {bytes.Length:n0})")),
|
||||
cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -192,7 +193,7 @@ internal sealed partial class TextureManager
|
|||
return this.BlameSetName(
|
||||
this.NoThrottleCreateFromRaw(specs, ms.GetBuffer().AsSpan(0, (int)ms.Length)),
|
||||
debugName ??
|
||||
$"{nameof(this.CreateFromRawAsync)}({nameof(specs)}, {nameof(stream)}, {nameof(leaveOpen)}, {nameof(cancellationToken)})");
|
||||
$"{nameof(this.CreateFromRawAsync)}({specs}, stream)");
|
||||
},
|
||||
cancellationToken,
|
||||
leaveOpen ? null : stream);
|
||||
|
|
@ -207,15 +208,19 @@ internal sealed partial class TextureManager
|
|||
public Task<IDalamudTextureWrap> CreateFromTexFileAsync(
|
||||
TexFile file,
|
||||
string? debugName = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.DynamicPriorityTextureLoader.LoadAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return this.DynamicPriorityTextureLoader.LoadAsync(
|
||||
null,
|
||||
_ => Task.FromResult(
|
||||
this.BlameSetName(
|
||||
this.NoThrottleCreateFromTexFile(file),
|
||||
debugName ?? $"{nameof(this.CreateFromTexFile)}({nameof(file)})")),
|
||||
debugName ?? $"{nameof(this.CreateFromTexFile)}({ForceNullable(file.FilePath)?.Path})")),
|
||||
cancellationToken);
|
||||
|
||||
static T? ForceNullable<T>(T s) => s;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ITextureProvider.IsDxgiFormatSupported(int dxgiFormat) =>
|
||||
this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat);
|
||||
|
|
@ -267,7 +272,7 @@ internal sealed partial class TextureManager
|
|||
.ThrowOnError();
|
||||
|
||||
var wrap = new UnknownTextureWrap((IUnknown*)view.Get(), specs.Width, specs.Height, true);
|
||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromRaw)}({nameof(specs)}, {nameof(bytes)})");
|
||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromRaw)}({specs}, {bytes.Length:n0})");
|
||||
return wrap;
|
||||
}
|
||||
|
||||
|
|
@ -289,8 +294,10 @@ internal sealed partial class TextureManager
|
|||
}
|
||||
|
||||
var wrap = this.NoThrottleCreateFromRaw(new(buffer.Width, buffer.Height, dxgiFormat), buffer.RawData);
|
||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({nameof(file)})");
|
||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({ForceNullable(file.FilePath).Path})");
|
||||
return wrap;
|
||||
|
||||
static T? ForceNullable<T>(T s) => s;
|
||||
}
|
||||
|
||||
/// <summary>Creates a texture from the given <paramref name="fileBytes"/>, trying to interpret it as a
|
||||
|
|
@ -315,9 +322,31 @@ internal sealed partial class TextureManager
|
|||
// Note: FileInfo and FilePath are not used from TexFile; skip it.
|
||||
|
||||
var wrap = this.NoThrottleCreateFromTexFile(tf);
|
||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({nameof(fileBytes)})");
|
||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({fileBytes.Length:n0})");
|
||||
return wrap;
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources() => this.device.Reset();
|
||||
|
||||
/// <summary>Runs the given action in IDXGISwapChain.Present immediately or waiting as needed.</summary>
|
||||
/// <param name="action">The action to run.</param>
|
||||
// Not sure why this and the below can't be unconditional RunOnFrameworkThread
|
||||
private async Task RunDuringPresent(Action action)
|
||||
{
|
||||
if (this.interfaceManager.IsInPresent && ThreadSafety.IsMainThread)
|
||||
action();
|
||||
else
|
||||
await this.interfaceManager.RunBeforeImGuiRender(action);
|
||||
}
|
||||
|
||||
/// <summary>Runs the given function in IDXGISwapChain.Present immediately or waiting as needed.</summary>
|
||||
/// <typeparam name="T">The type of the return value.</typeparam>
|
||||
/// <param name="func">The function to run.</param>
|
||||
/// <returns>The return value from the function.</returns>
|
||||
private async Task<T> RunDuringPresent<T>(Func<T> func)
|
||||
{
|
||||
if (this.interfaceManager.IsInPresent && ThreadSafety.IsMainThread)
|
||||
return func();
|
||||
return await this.interfaceManager.RunBeforeImGuiRender(func);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public record struct RawImageSpecification
|
|||
/// <param name="height">The height of the raw image.</param>
|
||||
/// <param name="dxgiFormat">The DXGI format of the raw image.</param>
|
||||
/// <param name="pitch">The pitch of the raw image in bytes.
|
||||
/// Specify <c>-1</c> to calculate it from other parameters.</param>
|
||||
/// Specify <c>-1</c> to calculate from other parameters.</param>
|
||||
public RawImageSpecification(int width, int height, int dxgiFormat, int pitch = -1)
|
||||
{
|
||||
if (pitch < 0)
|
||||
|
|
@ -31,6 +31,14 @@ public record struct RawImageSpecification
|
|||
this.DxgiFormat = dxgiFormat;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RawImageSpecification"/> class.</summary>
|
||||
/// <param name="desc">The source texture description.</param>
|
||||
/// <param name="pitch">The pitch of the raw image in bytes.</param>
|
||||
internal RawImageSpecification(in D3D11_TEXTURE2D_DESC desc, uint pitch)
|
||||
: this((int)desc.Width, (int)desc.Height, (int)desc.Format, checked((int)pitch))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the width of the raw image.</summary>
|
||||
public int Width { get; set; }
|
||||
|
||||
|
|
@ -102,6 +110,10 @@ public record struct RawImageSpecification
|
|||
public static RawImageSpecification A8(int width, int height) =>
|
||||
new(width, height, (int)DXGI_FORMAT.DXGI_FORMAT_A8_UNORM, width);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() =>
|
||||
$"{nameof(RawImageSpecification)}({this.Width}x{this.Height}, {this.Format}, {this.Pitch}b)";
|
||||
|
||||
private static bool GetFormatInfo(DXGI_FORMAT format, out int bitsPerPixel, out bool isBlockCompression)
|
||||
{
|
||||
switch (format)
|
||||
|
|
@ -246,7 +258,7 @@ public record struct RawImageSpecification
|
|||
return true;
|
||||
case DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM:
|
||||
bitsPerPixel = 16;
|
||||
isBlockCompression = true;
|
||||
isBlockCompression = false;
|
||||
return true;
|
||||
default:
|
||||
bitsPerPixel = 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
|
|
@ -52,6 +53,36 @@ public record struct TextureModificationArgs()
|
|||
/// <summary>Gets the effective value of <see cref="Uv1"/>.</summary>
|
||||
internal Vector2 Uv1Effective => this.Uv1 == Vector2.Zero ? Vector2.One : this.Uv1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(nameof(TextureModificationArgs)).Append('(');
|
||||
if (this.MakeOpaque)
|
||||
sb.Append($"{nameof(this.MakeOpaque)}, ");
|
||||
if (this.Format != DXGI_FORMAT.DXGI_FORMAT_UNKNOWN)
|
||||
sb.Append(Enum.GetName(this.Format) is { } name ? name[12..] : this.Format.ToString()).Append(", ");
|
||||
if (this.NewWidth != 0 || this.NewHeight != 0)
|
||||
{
|
||||
sb.Append(this.NewWidth == 0 ? "?" : this.NewWidth.ToString())
|
||||
.Append('x')
|
||||
.Append(this.NewHeight == 0 ? "?" : this.NewHeight.ToString())
|
||||
.Append(", ");
|
||||
}
|
||||
|
||||
if (this.Uv0 != Vector2.Zero || this.Uv1Effective != Vector2.One)
|
||||
{
|
||||
sb.Append(this.Uv0.ToString())
|
||||
.Append('-')
|
||||
.Append(this.Uv1.ToString())
|
||||
.Append(", ");
|
||||
}
|
||||
|
||||
if (sb[^1] != '(')
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
return sb.Append(')').ToString();
|
||||
}
|
||||
|
||||
/// <summary>Test if this instance of <see cref="TextureModificationArgs"/> does not instruct to change the
|
||||
/// underlying data of a texture.</summary>
|
||||
/// <param name="desc">The texture description to test against.</param>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ using System.Numerics;
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.Textures;
|
||||
namespace Dalamud.Interface.Textures.TextureWraps;
|
||||
|
||||
/// <summary>Base class for implementations of <see cref="IDalamudTextureWrap"/> that forwards to another.</summary>
|
||||
public abstract class ForwardingTextureWrap : IDalamudTextureWrap
|
||||
|
|
@ -2,6 +2,8 @@ using System.Numerics;
|
|||
|
||||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Interface.Textures.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using Dalamud.Interface.Internal;
|
||||
|
||||
namespace Dalamud.Interface.Textures.Internal;
|
||||
namespace Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
|
||||
/// <summary>A texture wrap that ignores <see cref="IDisposable.Dispose"/> calls.</summary>
|
||||
internal class DisposeSuppressingTextureWrap : ForwardingTextureWrap
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
using System.Threading;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.Textures.Internal;
|
||||
namespace Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
|
||||
/// <summary>A texture wrap that is created from an <see cref="IUnknown"/>.</summary>
|
||||
internal sealed unsafe class UnknownTextureWrap : IDalamudTextureWrap, IDeferredDisposable
|
||||
|
|
@ -50,6 +51,10 @@ internal sealed unsafe class UnknownTextureWrap : IDalamudTextureWrap, IDeferred
|
|||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() =>
|
||||
$"{nameof(UnknownTextureWrap)}({Service<TextureManager>.GetNullable()?.GetBlame(this)?.Name ?? $"{this.imGuiHandle:X}"})";
|
||||
|
||||
/// <summary>Actually dispose the wrapped texture.</summary>
|
||||
void IDeferredDisposable.RealDispose()
|
||||
{
|
||||
|
|
@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.Internal;
|
||||
using Dalamud.Plugin.Internal.Types;
|
||||
using Dalamud.Storage.Assets;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -15,7 +16,7 @@ using TerraFX.Interop.Windows;
|
|||
|
||||
using NotSupportedException = System.NotSupportedException;
|
||||
|
||||
namespace Dalamud.Interface.Textures.Internal;
|
||||
namespace Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
|
||||
/// <summary>A texture wrap that takes its buffer from the frame buffer (of swap chain).</summary>
|
||||
internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDisposable
|
||||
Loading…
Add table
Add a link
Reference in a new issue