mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-14 12:44:16 +01:00
feat: add fallback texture, persist size
This commit is contained in:
parent
22a6261c98
commit
7a6916c732
1 changed files with 71 additions and 35 deletions
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
|
@ -40,6 +41,8 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
|
|
||||||
private readonly Dictionary<string, TextureInfo> activeTextures = new();
|
private readonly Dictionary<string, TextureInfo> activeTextures = new();
|
||||||
|
|
||||||
|
private TextureWrap? fallbackTextureWrap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TextureManager"/> class.
|
/// Initializes a new instance of the <see cref="TextureManager"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -56,6 +59,8 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
this.startInfo = startInfo;
|
this.startInfo = startInfo;
|
||||||
|
|
||||||
this.framework.Update += this.FrameworkOnUpdate;
|
this.framework.Update += this.FrameworkOnUpdate;
|
||||||
|
|
||||||
|
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync().ContinueWith(_ => this.CreateFallbackTexture());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -175,6 +180,7 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
this.fallbackTextureWrap?.Dispose();
|
||||||
this.framework.Update -= this.FrameworkOnUpdate;
|
this.framework.Update -= this.FrameworkOnUpdate;
|
||||||
|
|
||||||
Log.Verbose("Disposing {Num} left behind textures.");
|
Log.Verbose("Disposing {Num} left behind textures.");
|
||||||
|
|
@ -192,8 +198,12 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">Path to the texture.</param>
|
/// <param name="path">Path to the texture.</param>
|
||||||
/// <param name="refresh">Whether or not the texture should be reloaded if it was unloaded.</param>
|
/// <param name="refresh">Whether or not the texture should be reloaded if it was unloaded.</param>
|
||||||
|
/// <param name="rethrow">
|
||||||
|
/// If true, exceptions caused by texture load will not be caught.
|
||||||
|
/// If false, exceptions will be caught and a dummy texture will be returned to prevent plugins from using invalid texture handles.
|
||||||
|
/// </param>
|
||||||
/// <returns>Info object storing texture metadata.</returns>
|
/// <returns>Info object storing texture metadata.</returns>
|
||||||
internal TextureInfo GetInfo(string path, bool refresh = true)
|
internal TextureInfo GetInfo(string path, bool refresh = true, bool rethrow = false)
|
||||||
{
|
{
|
||||||
TextureInfo? info;
|
TextureInfo? info;
|
||||||
lock (this.activeTextures)
|
lock (this.activeTextures)
|
||||||
|
|
@ -232,39 +242,53 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureWrap? wrap;
|
TextureWrap? wrap;
|
||||||
|
try
|
||||||
// TODO: The actual loading here may fail due to circumstances outside of our control.
|
|
||||||
// We should create a fallback texture and return it instead, so that plugins don't crash.
|
|
||||||
|
|
||||||
// We want to load this from the disk, probably, if the path has a root
|
|
||||||
// Not sure if this can cause issues with e.g. network drives, might have to rethink
|
|
||||||
// and add a flag instead if it does.
|
|
||||||
if (Path.IsPathRooted(path))
|
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(path) == ".tex")
|
// We want to load this from the disk, probably, if the path has a root
|
||||||
|
// Not sure if this can cause issues with e.g. network drives, might have to rethink
|
||||||
|
// and add a flag instead if it does.
|
||||||
|
if (Path.IsPathRooted(path))
|
||||||
{
|
{
|
||||||
// Attempt to load via Lumina
|
if (Path.GetExtension(path) == ".tex")
|
||||||
var file = this.dataManager.GameData.GetFileFromDisk<TexFile>(path);
|
{
|
||||||
wrap = this.dataManager.GetImGuiTexture(file);
|
// Attempt to load via Lumina
|
||||||
Log.Verbose("Texture {Path} loaded FS via Lumina", path);
|
var file = this.dataManager.GameData.GetFileFromDisk<TexFile>(path);
|
||||||
|
wrap = this.dataManager.GetImGuiTexture(file);
|
||||||
|
Log.Verbose("Texture {Path} loaded FS via Lumina", path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Attempt to load image
|
||||||
|
wrap = this.im.LoadImage(path);
|
||||||
|
Log.Verbose("Texture {Path} loaded FS via LoadImage", path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Attempt to load image
|
// Load regularly from dats
|
||||||
wrap = this.im.LoadImage(path);
|
var file = this.dataManager.GetFile<TexFile>(path);
|
||||||
Log.Verbose("Texture {Path} loaded FS via LoadImage", path);
|
wrap = this.dataManager.GetImGuiTexture(file);
|
||||||
|
Log.Verbose("Texture {Path} loaded from SqPack", path);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (wrap == null)
|
||||||
{
|
throw new Exception("Could not create texture");
|
||||||
// Load regularly from dats
|
|
||||||
var file = this.dataManager.GetFile<TexFile>(path);
|
|
||||||
wrap = this.dataManager.GetImGuiTexture(file);
|
|
||||||
Log.Verbose("Texture {Path} loaded from SqPack", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wrap == null)
|
info.Extents = new Vector2(wrap.Width, wrap.Height);
|
||||||
throw new Exception("Could not create texture");
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Could not load texture from {Path}", path);
|
||||||
|
|
||||||
|
// When creating the texture initially, we want to be able to pass errors back to the plugin
|
||||||
|
if (rethrow)
|
||||||
|
throw;
|
||||||
|
|
||||||
|
// This means that the load failed due to circumstances outside of our control,
|
||||||
|
// and we can't do anything about it. Return a dummy texture so that the plugin still
|
||||||
|
// has something to draw.
|
||||||
|
wrap = this.fallbackTextureWrap;
|
||||||
|
}
|
||||||
|
|
||||||
info.Wrap = wrap;
|
info.Wrap = wrap;
|
||||||
}
|
}
|
||||||
|
|
@ -306,13 +330,13 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
{
|
{
|
||||||
// This will create the texture.
|
// This will create the texture.
|
||||||
// That's fine, it's probably used immediately and this will let the plugin catch load errors.
|
// That's fine, it's probably used immediately and this will let the plugin catch load errors.
|
||||||
var info = this.GetInfo(path, keepAlive);
|
var info = this.GetInfo(path, rethrow: true);
|
||||||
info.RefCount++;
|
info.RefCount++;
|
||||||
|
|
||||||
if (keepAlive)
|
if (keepAlive)
|
||||||
info.KeepAliveCount++;
|
info.KeepAliveCount++;
|
||||||
|
|
||||||
return new TextureManagerTextureWrap(path, keepAlive, this);
|
return new TextureManagerTextureWrap(path, info.Extents, keepAlive, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FrameworkOnUpdate(Framework fw)
|
private void FrameworkOnUpdate(Framework fw)
|
||||||
|
|
@ -353,6 +377,13 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateFallbackTexture()
|
||||||
|
{
|
||||||
|
var fallbackTexBytes = new byte[] { 0xFF, 0x00, 0xDC, 0xFF };
|
||||||
|
this.fallbackTextureWrap = this.im.LoadImageRaw(fallbackTexBytes, 1, 1, 4);
|
||||||
|
Debug.Assert(this.fallbackTextureWrap != null, "this.fallbackTextureWrap != null");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal representation of a managed texture.
|
/// Internal representation of a managed texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -377,6 +408,11 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP
|
||||||
/// Gets or sets the number of active holders that want this texture to stay alive forever.
|
/// Gets or sets the number of active holders that want this texture to stay alive forever.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint KeepAliveCount { get; set; }
|
public uint KeepAliveCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the extents of the texture.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Extents { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -474,30 +510,30 @@ internal class TextureManagerTextureWrap : IDalamudTextureWrap
|
||||||
private readonly string path;
|
private readonly string path;
|
||||||
private readonly bool keepAlive;
|
private readonly bool keepAlive;
|
||||||
|
|
||||||
private int? width;
|
|
||||||
private int? height;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TextureManagerTextureWrap"/> class.
|
/// Initializes a new instance of the <see cref="TextureManagerTextureWrap"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to the texture.</param>
|
/// <param name="path">The path to the texture.</param>
|
||||||
|
/// <param name="extents">The extents of the texture.</param>
|
||||||
/// <param name="keepAlive">Keep alive or not.</param>
|
/// <param name="keepAlive">Keep alive or not.</param>
|
||||||
/// <param name="manager">Manager that we obtained this from.</param>
|
/// <param name="manager">Manager that we obtained this from.</param>
|
||||||
internal TextureManagerTextureWrap(string path, bool keepAlive, TextureManager manager)
|
internal TextureManagerTextureWrap(string path, Vector2 extents, bool keepAlive, TextureManager manager)
|
||||||
{
|
{
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.keepAlive = keepAlive;
|
this.keepAlive = keepAlive;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.Width = (int)extents.X;
|
||||||
|
this.Height = (int)extents.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr ImGuiHandle => this.manager.GetInfo(this.path).Wrap!.ImGuiHandle;
|
public IntPtr ImGuiHandle => this.manager.GetInfo(this.path).Wrap!.ImGuiHandle;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int Width => this.width ??= this.manager.GetInfo(this.path).Wrap!.Width;
|
public int Width { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int Height => this.height ??= this.manager.GetInfo(this.path).Wrap!.Height;
|
public int Height { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether or not this wrap has already been disposed.
|
/// Gets a value indicating whether or not this wrap has already been disposed.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue