Turn ImFontLocked into a class

As `ImFontLocked` utilizes a reference counter, changed it to a class so that at worst case we still got the destructor to decrease the reference count.
This commit is contained in:
Soreepeong 2024-01-23 23:39:25 +09:00
parent df89472d4c
commit 68dc16803c
5 changed files with 75 additions and 34 deletions

View file

@ -79,7 +79,7 @@ internal class InterfaceManager : IDisposable, IServiceType
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
private IFontAtlas? dalamudAtlas;
private IFontHandle.ImFontLocked defaultFontResourceLock;
private IFontHandle.ImFontLocked? defaultFontResourceLock;
// can't access imgui IO before first present call
private bool lastWantCapture = false;
@ -243,6 +243,8 @@ internal class InterfaceManager : IDisposable, IServiceType
Disposer();
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
this.defaultFontResourceLock?.Dispose(); // lock outlives handle and atlas
this.defaultFontResourceLock = null;
this.dalamudAtlas?.Dispose();
this.scene?.Dispose();
return;
@ -727,22 +729,26 @@ internal class InterfaceManager : IDisposable, IServiceType
tk.GetFont(this.MonoFontHandle),
missingOnly: true);
});
this.DefaultFontHandle.ImFontChanged += (_, font) => Service<Framework>.Get().RunOnFrameworkThread(
() =>
{
// Update the ImGui default font.
unsafe
this.DefaultFontHandle.ImFontChanged += (_, font) =>
{
var fontLocked = font.NewRef();
Service<Framework>.Get().RunOnFrameworkThread(
() =>
{
ImGui.GetIO().NativePtr->FontDefault = font;
}
// Update the ImGui default font.
unsafe
{
ImGui.GetIO().NativePtr->FontDefault = fontLocked;
}
// Update the reference to the resources of the default font.
this.defaultFontResourceLock.Dispose();
this.defaultFontResourceLock = font.NewRef();
// Update the reference to the resources of the default font.
this.defaultFontResourceLock?.Dispose();
this.defaultFontResourceLock = fontLocked;
// Broadcast to auto-rebuilding instances.
this.AfterBuildFonts?.Invoke();
});
// Broadcast to auto-rebuilding instances.
this.AfterBuildFonts?.Invoke();
});
};
}
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.

View file

@ -1,9 +1,14 @@
using System.Threading.Tasks;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Dalamud.Interface.Utility;
using Dalamud.Utility;
using ImGuiNET;
using Microsoft.Extensions.ObjectPool;
namespace Dalamud.Interface.ManagedFontAtlas;
/// <summary>
@ -80,26 +85,23 @@ public interface IFontHandle : IDisposable
/// The wrapper for <see cref="ImFontPtr"/>, guaranteeing that the associated data will be available as long as
/// this struct is not disposed.
/// </summary>
public struct ImFontLocked : IDisposable
public class ImFontLocked : IDisposable
{
/// <summary>
/// The associated <see cref="ImFontPtr"/>.
/// </summary>
public ImFontPtr ImFont;
// Using constructor instead of DefaultObjectPoolProvider, since we do not want the pool to call Dispose.
private static readonly ObjectPool<ImFontLocked> Pool =
new DefaultObjectPool<ImFontLocked>(new DefaultPooledObjectPolicy<ImFontLocked>());
private IRefCountable? owner;
/// <summary>
/// Initializes a new instance of the <see cref="ImFontLocked"/> struct.
/// Ownership of reference of <paramref name="owner"/> is transferred.
/// Finalizes an instance of the <see cref="ImFontLocked"/> class.
/// </summary>
/// <param name="imFont">The contained font.</param>
/// <param name="owner">The owner.</param>
internal ImFontLocked(ImFontPtr imFont, IRefCountable owner)
{
this.ImFont = imFont;
this.owner = owner;
}
~ImFontLocked() => this.FreeOwner();
/// <summary>
/// Gets the associated <see cref="ImFontPtr"/>.
/// </summary>
public ImFontPtr ImFont { get; private set; }
public static implicit operator ImFontPtr(ImFontLocked l) => l.ImFont;
@ -109,16 +111,47 @@ public interface IFontHandle : IDisposable
/// Creates a new instance of <see cref="ImFontLocked"/> with an additional reference to the owner.
/// </summary>
/// <returns>The new locked instance.</returns>
public readonly ImFontLocked NewRef()
public ImFontLocked NewRef()
{
if (this.owner is null)
throw new ObjectDisposedException(nameof(ImFontLocked));
var rented = Pool.Get();
rented.owner = this.owner;
rented.ImFont = this.ImFont;
this.owner.AddRef();
return new(this.ImFont, this.owner);
return rented;
}
/// <inheritdoc/>
[SuppressMessage(
"Usage",
"CA1816:Dispose methods should call SuppressFinalize",
Justification = "Dispose returns this object to the pool.")]
public void Dispose()
{
this.FreeOwner();
Pool.Return(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="ImFontLocked"/> class.
/// Ownership of reference of <paramref name="owner"/> is transferred.
/// </summary>
/// <param name="font">The contained font.</param>
/// <param name="owner">The owner.</param>
/// <returns>The rented instance of <see cref="ImFontLocked"/>.</returns>
internal static ImFontLocked Rent(ImFontPtr font, IRefCountable owner)
{
var rented = Pool.Get();
Debug.Assert(rented.ImFont.IsNull(), "Rented object must not have its font set");
rented.ImFont = font;
rented.owner = owner;
return rented;
}
private void FreeOwner()
{
if (this.owner is null)
return;

View file

@ -557,7 +557,9 @@ internal sealed partial class FontAtlasFactory
foreach (var fontHandle in substance.RelevantHandles)
{
substance.DataRoot.AddRef();
var locked = new IFontHandle.ImFontLocked(substance.GetFontPtr(fontHandle), substance.DataRoot);
var locked = IFontHandle.ImFontLocked.Rent(
substance.GetFontPtr(fontHandle),
substance.DataRoot);
fontsAndLocks.Add((fontHandle, garbage.Add(locked)));
}
}

View file

@ -182,7 +182,7 @@ internal abstract class FontHandle : IFontHandle
// Transfer the ownership of reference.
errorMessage = null;
return new(fontPtr, substance.DataRoot);
return IFontHandle.ImFontLocked.Rent(fontPtr, substance.DataRoot);
}
}

View file

@ -28,7 +28,7 @@ internal sealed class SimplePushedFont : IDisposable
/// </summary>
/// <param name="stack">The <see cref="IFontHandle"/>-private stack.</param>
/// <param name="fontPtr">The font pointer being pushed.</param>
/// <returns><c>this</c>.</returns>
/// <returns>The rented instance of <see cref="SimplePushedFont"/>.</returns>
public static SimplePushedFont Rent(List<IDisposable> stack, ImFontPtr fontPtr)
{
var rented = Pool.Get();