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 Hook<ResizeBuffersDelegate>? resizeBuffersHook;
private IFontAtlas? dalamudAtlas; private IFontAtlas? dalamudAtlas;
private IFontHandle.ImFontLocked defaultFontResourceLock; private IFontHandle.ImFontLocked? defaultFontResourceLock;
// can't access imgui IO before first present call // can't access imgui IO before first present call
private bool lastWantCapture = false; private bool lastWantCapture = false;
@ -243,6 +243,8 @@ internal class InterfaceManager : IDisposable, IServiceType
Disposer(); Disposer();
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc; this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
this.defaultFontResourceLock?.Dispose(); // lock outlives handle and atlas
this.defaultFontResourceLock = null;
this.dalamudAtlas?.Dispose(); this.dalamudAtlas?.Dispose();
this.scene?.Dispose(); this.scene?.Dispose();
return; return;
@ -727,22 +729,26 @@ internal class InterfaceManager : IDisposable, IServiceType
tk.GetFont(this.MonoFontHandle), tk.GetFont(this.MonoFontHandle),
missingOnly: true); missingOnly: true);
}); });
this.DefaultFontHandle.ImFontChanged += (_, font) => Service<Framework>.Get().RunOnFrameworkThread( this.DefaultFontHandle.ImFontChanged += (_, font) =>
() => {
{ var fontLocked = font.NewRef();
// Update the ImGui default font. Service<Framework>.Get().RunOnFrameworkThread(
unsafe () =>
{ {
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. // Update the reference to the resources of the default font.
this.defaultFontResourceLock.Dispose(); this.defaultFontResourceLock?.Dispose();
this.defaultFontResourceLock = font.NewRef(); this.defaultFontResourceLock = fontLocked;
// Broadcast to auto-rebuilding instances. // Broadcast to auto-rebuilding instances.
this.AfterBuildFonts?.Invoke(); this.AfterBuildFonts?.Invoke();
}); });
};
} }
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene. // 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 Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using Microsoft.Extensions.ObjectPool;
namespace Dalamud.Interface.ManagedFontAtlas; namespace Dalamud.Interface.ManagedFontAtlas;
/// <summary> /// <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 /// The wrapper for <see cref="ImFontPtr"/>, guaranteeing that the associated data will be available as long as
/// this struct is not disposed. /// this struct is not disposed.
/// </summary> /// </summary>
public struct ImFontLocked : IDisposable public class ImFontLocked : IDisposable
{ {
/// <summary> // Using constructor instead of DefaultObjectPoolProvider, since we do not want the pool to call Dispose.
/// The associated <see cref="ImFontPtr"/>. private static readonly ObjectPool<ImFontLocked> Pool =
/// </summary> new DefaultObjectPool<ImFontLocked>(new DefaultPooledObjectPolicy<ImFontLocked>());
public ImFontPtr ImFont;
private IRefCountable? owner; private IRefCountable? owner;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImFontLocked"/> struct. /// Finalizes an instance of the <see cref="ImFontLocked"/> class.
/// Ownership of reference of <paramref name="owner"/> is transferred.
/// </summary> /// </summary>
/// <param name="imFont">The contained font.</param> ~ImFontLocked() => this.FreeOwner();
/// <param name="owner">The owner.</param>
internal ImFontLocked(ImFontPtr imFont, IRefCountable owner) /// <summary>
{ /// Gets the associated <see cref="ImFontPtr"/>.
this.ImFont = imFont; /// </summary>
this.owner = owner; public ImFontPtr ImFont { get; private set; }
}
public static implicit operator ImFontPtr(ImFontLocked l) => l.ImFont; 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. /// Creates a new instance of <see cref="ImFontLocked"/> with an additional reference to the owner.
/// </summary> /// </summary>
/// <returns>The new locked instance.</returns> /// <returns>The new locked instance.</returns>
public readonly ImFontLocked NewRef() public ImFontLocked NewRef()
{ {
if (this.owner is null) if (this.owner is null)
throw new ObjectDisposedException(nameof(ImFontLocked)); throw new ObjectDisposedException(nameof(ImFontLocked));
var rented = Pool.Get();
rented.owner = this.owner;
rented.ImFont = this.ImFont;
this.owner.AddRef(); this.owner.AddRef();
return new(this.ImFont, this.owner); return rented;
} }
/// <inheritdoc/> /// <inheritdoc/>
[SuppressMessage(
"Usage",
"CA1816:Dispose methods should call SuppressFinalize",
Justification = "Dispose returns this object to the pool.")]
public void Dispose() 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) if (this.owner is null)
return; return;

View file

@ -557,7 +557,9 @@ internal sealed partial class FontAtlasFactory
foreach (var fontHandle in substance.RelevantHandles) foreach (var fontHandle in substance.RelevantHandles)
{ {
substance.DataRoot.AddRef(); 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))); fontsAndLocks.Add((fontHandle, garbage.Add(locked)));
} }
} }

View file

@ -182,7 +182,7 @@ internal abstract class FontHandle : IFontHandle
// Transfer the ownership of reference. // Transfer the ownership of reference.
errorMessage = null; 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> /// </summary>
/// <param name="stack">The <see cref="IFontHandle"/>-private stack.</param> /// <param name="stack">The <see cref="IFontHandle"/>-private stack.</param>
/// <param name="fontPtr">The font pointer being pushed.</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) public static SimplePushedFont Rent(List<IDisposable> stack, ImFontPtr fontPtr)
{ {
var rented = Pool.Get(); var rented = Pool.Get();