mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Remove PostPromotion event
`PostPromotion` is removed, as `IFontHandle.ImFontChanged` now does the job. It also removes the possibility that resources may get disposed while post promotion callback is in progress. * `IFontHandle.ImFontChanged` is now called with a locked instance of the font. * `IFontHandle.ImFontLocked`: Added `NewRef` to increase reference count.
This commit is contained in:
parent
fb8beb9370
commit
871deca6e9
17 changed files with 138 additions and 269 deletions
|
|
@ -69,7 +69,7 @@ namespace Dalamud.CorePlugin
|
||||||
this.Interface.UiBuilder.Draw += this.OnDraw;
|
this.Interface.UiBuilder.Draw += this.OnDraw;
|
||||||
this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi;
|
this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi;
|
||||||
this.Interface.UiBuilder.OpenMainUi += this.OnOpenMainUi;
|
this.Interface.UiBuilder.OpenMainUi += this.OnOpenMainUi;
|
||||||
this.Interface.UiBuilder.DefaultFontHandle.ImFontChanged += fc =>
|
this.Interface.UiBuilder.DefaultFontHandle.ImFontChanged += (fc, _) =>
|
||||||
{
|
{
|
||||||
Log.Information($"CorePlugin : DefaultFontHandle.ImFontChanged called {fc}");
|
Log.Information($"CorePlugin : DefaultFontHandle.ImFontChanged called {fc}");
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ public sealed class GameFontHandle : IFontHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Action<IFontHandle> ImFontChanged
|
public event IFontHandle.ImFontChangedDelegate ImFontChanged
|
||||||
{
|
{
|
||||||
add => this.fontHandle.ImFontChanged += value;
|
add => this.fontHandle.ImFontChanged += value;
|
||||||
remove => this.fontHandle.ImFontChanged -= value;
|
remove => this.fontHandle.ImFontChanged -= value;
|
||||||
|
|
|
||||||
|
|
@ -79,6 +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;
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
@ -717,8 +718,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
||||||
tk => tk.AddDalamudAssetFont(
|
tk => tk.AddDalamudAssetFont(
|
||||||
DalamudAsset.InconsolataRegular,
|
DalamudAsset.InconsolataRegular,
|
||||||
new() { SizePx = DefaultFontSizePx })));
|
new() { SizePx = DefaultFontSizePx })));
|
||||||
this.dalamudAtlas.BuildStepChange += e => e
|
this.dalamudAtlas.BuildStepChange += e => e.OnPostBuild(
|
||||||
.OnPostBuild(
|
|
||||||
tk =>
|
tk =>
|
||||||
{
|
{
|
||||||
// Fill missing glyphs in MonoFont from DefaultFont.
|
// Fill missing glyphs in MonoFont from DefaultFont.
|
||||||
|
|
@ -726,23 +726,27 @@ internal class InterfaceManager : IDisposable, IServiceType
|
||||||
tk.GetFont(this.DefaultFontHandle),
|
tk.GetFont(this.DefaultFontHandle),
|
||||||
tk.GetFont(this.MonoFontHandle),
|
tk.GetFont(this.MonoFontHandle),
|
||||||
missingOnly: true);
|
missingOnly: true);
|
||||||
})
|
});
|
||||||
.OnPostPromotion(
|
this.DefaultFontHandle.ImFontChanged += (_, font) => Service<Framework>.Get().RunOnFrameworkThread(
|
||||||
tk =>
|
() =>
|
||||||
{
|
{
|
||||||
// Update the ImGui default font.
|
// Update the ImGui default font.
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
ImGui.GetIO().NativePtr->FontDefault = tk.GetFont(this.DefaultFontHandle);
|
ImGui.GetIO().NativePtr->FontDefault = font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the reference to the resources of the default font.
|
||||||
|
this.defaultFontResourceLock.Dispose();
|
||||||
|
this.defaultFontResourceLock = font.NewRef();
|
||||||
|
|
||||||
// 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.
|
||||||
_ = this.dalamudAtlas.BuildFontsAsync(false);
|
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||||
|
|
||||||
this.address.Setup(sigScanner);
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,4 @@ public enum FontAtlasBuildStep
|
||||||
/// but it will never happen on its own.
|
/// but it will never happen on its own.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PostBuild = 2,
|
PostBuild = 2,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called after promoting staging font atlas to the actual atlas for <see cref="IFontAtlas"/>.<br />
|
|
||||||
/// Expect <see cref="PostBuild"/> to be passed.<br />
|
|
||||||
/// When called from <see cref="IFontAtlas.BuildStepChange"/>, this will be called <b>after</b> the delegates
|
|
||||||
/// passed to <see cref="IFontAtlas.NewDelegateFontHandle"/>; you should not make modifications to fonts.<br />
|
|
||||||
/// <br />
|
|
||||||
/// This callback is not guaranteed to happen after <see cref="IFontAtlasBuildToolkitPostPromotion"/>,
|
|
||||||
/// but it will never happen on its own.
|
|
||||||
/// </summary>
|
|
||||||
PostPromotion = 3,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,9 @@ namespace Dalamud.Interface.ManagedFontAtlas;
|
||||||
/// <param name="toolkit">A toolkit that may help you for font building steps.</param>
|
/// <param name="toolkit">A toolkit that may help you for font building steps.</param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// An implementation of <see cref="IFontAtlasBuildToolkit"/> may implement all of
|
/// An implementation of <see cref="IFontAtlasBuildToolkit"/> may implement all of
|
||||||
/// <see cref="IFontAtlasBuildToolkitPreBuild"/>, <see cref="IFontAtlasBuildToolkitPostBuild"/>, and
|
/// <see cref="IFontAtlasBuildToolkitPreBuild"/> and <see cref="IFontAtlasBuildToolkitPostBuild"/>.<br />
|
||||||
/// <see cref="IFontAtlasBuildToolkitPostPromotion"/>.<br />
|
|
||||||
/// Either use <see cref="IFontAtlasBuildToolkit.BuildStep"/> to identify the build step, or use
|
/// Either use <see cref="IFontAtlasBuildToolkit.BuildStep"/> to identify the build step, or use
|
||||||
/// <see cref="FontAtlasBuildToolkitUtilities.OnPreBuild"/>, <see cref="FontAtlasBuildToolkitUtilities.OnPostBuild"/>,
|
/// <see cref="FontAtlasBuildToolkitUtilities.OnPreBuild"/> and <see cref="FontAtlasBuildToolkitUtilities.OnPostBuild"/>
|
||||||
/// and <see cref="FontAtlasBuildToolkitUtilities.OnPostPromotion"/> for routing.
|
/// for routing.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public delegate void FontAtlasBuildStepDelegate(IFontAtlasBuildToolkit toolkit);
|
public delegate void FontAtlasBuildStepDelegate(IFontAtlasBuildToolkit toolkit);
|
||||||
|
|
|
||||||
|
|
@ -113,21 +113,4 @@ public static class FontAtlasBuildToolkitUtilities
|
||||||
action.Invoke((IFontAtlasBuildToolkitPostBuild)toolkit);
|
action.Invoke((IFontAtlasBuildToolkitPostBuild)toolkit);
|
||||||
return toolkit;
|
return toolkit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes <paramref name="action"/>
|
|
||||||
/// if <see cref="IFontAtlasBuildToolkit.BuildStep"/> of <paramref name="toolkit"/>
|
|
||||||
/// is <see cref="FontAtlasBuildStep.PostPromotion"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toolkit">The toolkit.</param>
|
|
||||||
/// <param name="action">The action.</param>
|
|
||||||
/// <returns>toolkit, for method chaining.</returns>
|
|
||||||
public static IFontAtlasBuildToolkit OnPostPromotion(
|
|
||||||
this IFontAtlasBuildToolkit toolkit,
|
|
||||||
Action<IFontAtlasBuildToolkitPostPromotion> action)
|
|
||||||
{
|
|
||||||
if (toolkit.BuildStep is FontAtlasBuildStep.PostPromotion)
|
|
||||||
action.Invoke((IFontAtlasBuildToolkitPostPromotion)toolkit);
|
|
||||||
return toolkit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,7 @@ public interface IFontAtlas : IDisposable
|
||||||
void BuildFontsOnNextFrame();
|
void BuildFontsOnNextFrame();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rebuilds fonts immediately, on the current thread.<br />
|
/// Rebuilds fonts immediately, on the current thread.
|
||||||
/// Even the callback for <see cref="FontAtlasBuildStep.PostPromotion"/> will be called on the same thread.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.Async"/>.</exception>
|
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.Async"/>.</exception>
|
||||||
void BuildFontsImmediately();
|
void BuildFontsImmediately();
|
||||||
|
|
@ -138,8 +137,7 @@ public interface IFontAtlas : IDisposable
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rebuilds fonts asynchronously, on any thread.
|
/// Rebuilds fonts asynchronously, on any thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="callPostPromotionOnMainThread">Call <see cref="FontAtlasBuildStep.PostPromotion"/> on the main thread.</param>
|
|
||||||
/// <returns>The task.</returns>
|
/// <returns>The task.</returns>
|
||||||
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.OnNewFrame"/>.</exception>
|
/// <exception cref="InvalidOperationException">If <see cref="AutoRebuildMode"/> is <see cref="FontAtlasAutoRebuildMode.OnNewFrame"/>.</exception>
|
||||||
Task BuildFontsAsync(bool callPostPromotionOnMainThread = true);
|
Task BuildFontsAsync();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Dalamud.Interface.ManagedFontAtlas;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toolkit for use when the build state is <see cref="FontAtlasBuildStep.PostPromotion"/>.
|
|
||||||
/// </summary>
|
|
||||||
public interface IFontAtlasBuildToolkitPostPromotion : IFontAtlasBuildToolkit
|
|
||||||
{
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
@ -12,14 +12,17 @@ namespace Dalamud.Interface.ManagedFontAtlas;
|
||||||
public interface IFontHandle : IDisposable
|
public interface IFontHandle : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the built instance of <see cref="ImFontPtr"/> has been changed.<br />
|
/// Delegate for <see cref="IFontHandle.ImFontChanged"/>.
|
||||||
/// This event will be invoked on the same thread with
|
|
||||||
/// <see cref="IFontAtlas"/>.<see cref="IFontAtlas.BuildStepChange"/>,
|
|
||||||
/// when the build step is <see cref="FontAtlasBuildStep.PostPromotion"/>.<br />
|
|
||||||
/// See <see cref="IFontAtlas.BuildFontsOnNextFrame"/>, <see cref="IFontAtlas.BuildFontsImmediately"/>, and
|
|
||||||
/// <see cref="IFontAtlas.BuildFontsAsync"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event Action<IFontHandle> ImFontChanged;
|
/// <param name="fontHandle">The relevant font handle.</param>
|
||||||
|
/// <param name="lockedFont">The locked font for this font handle, locked during the call of this delegate.</param>
|
||||||
|
public delegate void ImFontChangedDelegate(IFontHandle fontHandle, ImFontLocked lockedFont);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the built instance of <see cref="ImFontPtr"/> has been changed.<br />
|
||||||
|
/// This event can be invoked outside the main thread.
|
||||||
|
/// </summary>
|
||||||
|
event ImFontChangedDelegate ImFontChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the load exception, if it failed to load. Otherwise, it is null.
|
/// Gets the load exception, if it failed to load. Otherwise, it is null.
|
||||||
|
|
@ -102,6 +105,18 @@ public interface IFontHandle : IDisposable
|
||||||
|
|
||||||
public static unsafe implicit operator ImFont*(ImFontLocked l) => l.ImFont.NativePtr;
|
public static unsafe implicit operator ImFont*(ImFontLocked l) => l.ImFont.NativePtr;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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()
|
||||||
|
{
|
||||||
|
if (this.owner is null)
|
||||||
|
throw new ObjectDisposedException(nameof(ImFontLocked));
|
||||||
|
this.owner.AddRef();
|
||||||
|
return new(this.ImFont, this.owner);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -80,16 +80,6 @@ internal sealed class DelegateFontHandle : FontHandle
|
||||||
this.handles.Remove(cgfh);
|
this.handles.Remove(cgfh);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void InvokeFontHandleImFontChanged()
|
|
||||||
{
|
|
||||||
if (this.Substance is not HandleSubstance hs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var handle in hs.RelevantHandles)
|
|
||||||
handle.InvokeImFontChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IFontHandleSubstance NewSubstance(IRefCountable dataRoot)
|
public IFontHandleSubstance NewSubstance(IRefCountable dataRoot)
|
||||||
{
|
{
|
||||||
|
|
@ -133,6 +123,9 @@ internal sealed class DelegateFontHandle : FontHandle
|
||||||
// Not owned by this class. Do not dispose.
|
// Not owned by this class. Do not dispose.
|
||||||
public DelegateFontHandle[] RelevantHandles { get; }
|
public DelegateFontHandle[] RelevantHandles { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
ICollection<FontHandle> IFontHandleSubstance.RelevantHandles => this.RelevantHandles;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IRefCountable DataRoot { get; }
|
public IRefCountable DataRoot { get; }
|
||||||
|
|
||||||
|
|
@ -306,32 +299,5 @@ internal sealed class DelegateFontHandle : FontHandle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void OnPostPromotion(IFontAtlasBuildToolkitPostPromotion toolkitPostPromotion)
|
|
||||||
{
|
|
||||||
foreach (var k in this.RelevantHandles)
|
|
||||||
{
|
|
||||||
if (!this.fonts[k].IsNotNullAndLoaded())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
toolkitPostPromotion.Font = this.fonts[k];
|
|
||||||
k.CallOnBuildStepChange.Invoke(toolkitPostPromotion);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
this.fonts[k] = default;
|
|
||||||
this.buildExceptions[k] = e;
|
|
||||||
|
|
||||||
Log.Error(
|
|
||||||
e,
|
|
||||||
"[{name}:Substance] An error has occurred while during {delegate} PostPromotion call.",
|
|
||||||
this.Manager.Name,
|
|
||||||
nameof(FontAtlasBuildStepDelegate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -677,60 +677,4 @@ internal sealed partial class FontAtlasFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implementations for <see cref="IFontAtlasBuildToolkitPostPromotion"/>.
|
|
||||||
/// </summary>
|
|
||||||
private class BuildToolkitPostPromotion : IFontAtlasBuildToolkitPostPromotion
|
|
||||||
{
|
|
||||||
private readonly FontAtlasBuiltData builtData;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="BuildToolkitPostPromotion"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builtData">The built data.</param>
|
|
||||||
public BuildToolkitPostPromotion(FontAtlasBuiltData builtData) => this.builtData = builtData;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ImFontPtr Font { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public float Scale => this.builtData.Scale;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAsyncBuildOperation => true;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public FontAtlasBuildStep BuildStep => FontAtlasBuildStep.PostPromotion;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ImFontAtlasPtr NewImAtlas => this.builtData.Atlas;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public unsafe ImVectorWrapper<ImFontPtr> Fonts => new(
|
|
||||||
&this.NewImAtlas.NativePtr->Fonts,
|
|
||||||
x => ImGuiNative.ImFont_destroy(x->NativePtr));
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public T DisposeWithAtlas<T>(T disposable) where T : IDisposable => this.builtData.Garbage.Add(disposable);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public GCHandle DisposeWithAtlas(GCHandle gcHandle) => this.builtData.Garbage.Add(gcHandle);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void DisposeWithAtlas(Action action) => this.builtData.Garbage.Add(action);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ImFontPtr GetFont(IFontHandle fontHandle)
|
|
||||||
{
|
|
||||||
foreach (var s in this.builtData.Substances)
|
|
||||||
{
|
|
||||||
var f = s.GetFontPtr(fontHandle);
|
|
||||||
if (!f.IsNull())
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ internal sealed partial class FontAtlasFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task BuildFontsAsync(bool callPostPromotionOnMainThread = true)
|
public Task BuildFontsAsync()
|
||||||
{
|
{
|
||||||
#if VeryVerboseLog
|
#if VeryVerboseLog
|
||||||
Log.Verbose("[{name}] Called: {source}.", this.Name, nameof(this.BuildFontsAsync));
|
Log.Verbose("[{name}] Called: {source}.", this.Name, nameof(this.BuildFontsAsync));
|
||||||
|
|
@ -519,15 +519,7 @@ internal sealed partial class FontAtlasFactory
|
||||||
if (res.Atlas.IsNull())
|
if (res.Atlas.IsNull())
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if (callPostPromotionOnMainThread)
|
|
||||||
{
|
|
||||||
await this.factory.Framework.RunOnFrameworkThread(
|
|
||||||
() => this.InvokePostPromotion(rebuildIndex, res, nameof(this.BuildFontsAsync)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.InvokePostPromotion(rebuildIndex, res, nameof(this.BuildFontsAsync));
|
this.InvokePostPromotion(rebuildIndex, res, nameof(this.BuildFontsAsync));
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -536,6 +528,10 @@ internal sealed partial class FontAtlasFactory
|
||||||
|
|
||||||
private void InvokePostPromotion(int rebuildIndex, FontAtlasBuiltData data, [UsedImplicitly] string source)
|
private void InvokePostPromotion(int rebuildIndex, FontAtlasBuiltData data, [UsedImplicitly] string source)
|
||||||
{
|
{
|
||||||
|
// Capture the locks inside the lock block, so that the fonts are guaranteed to be the ones just built.
|
||||||
|
var fontsAndLocks = new List<(FontHandle FontHandle, IFontHandle.ImFontLocked Lock)>();
|
||||||
|
using var garbage = new DisposeSafety.ScopedFinalizer();
|
||||||
|
|
||||||
lock (this.syncRoot)
|
lock (this.syncRoot)
|
||||||
{
|
{
|
||||||
if (this.buildIndex != rebuildIndex)
|
if (this.buildIndex != rebuildIndex)
|
||||||
|
|
@ -549,57 +545,26 @@ internal sealed partial class FontAtlasFactory
|
||||||
prevBuiltData.ExplicitDisposeIgnoreExceptions();
|
prevBuiltData.ExplicitDisposeIgnoreExceptions();
|
||||||
|
|
||||||
this.buildTask = EmptyTask;
|
this.buildTask = EmptyTask;
|
||||||
|
fontsAndLocks.EnsureCapacity(data.Substances.Sum(x => x.RelevantHandles.Count));
|
||||||
foreach (var substance in data.Substances)
|
foreach (var substance in data.Substances)
|
||||||
|
{
|
||||||
substance.Manager.Substance = substance;
|
substance.Manager.Substance = substance;
|
||||||
|
foreach (var fontHandle in substance.RelevantHandles)
|
||||||
|
{
|
||||||
|
substance.DataRoot.AddRef();
|
||||||
|
var locked = new IFontHandle.ImFontLocked(substance.GetFontPtr(fontHandle), substance.DataRoot);
|
||||||
|
fontsAndLocks.Add((fontHandle, garbage.Add(locked)));
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (this.syncRootPostPromotion)
|
|
||||||
{
|
|
||||||
if (this.buildIndex != rebuildIndex)
|
|
||||||
{
|
|
||||||
data.ExplicitDisposeIgnoreExceptions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var substance in data.Substances)
|
|
||||||
substance.Manager.InvokeFontHandleImFontChanged();
|
|
||||||
|
|
||||||
var toolkit = new BuildToolkitPostPromotion(data);
|
|
||||||
|
|
||||||
foreach (var substance in data.Substances)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
substance.OnPostPromotion(toolkit);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(
|
|
||||||
e,
|
|
||||||
"[{name}] {substance} PostPromotion error",
|
|
||||||
this.Name,
|
|
||||||
substance.GetType().FullName ?? substance.GetType().Name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
foreach (var (fontHandle, lockedFont) in fontsAndLocks)
|
||||||
{
|
fontHandle.InvokeImFontChanged(lockedFont);
|
||||||
this.BuildStepChange?.Invoke(toolkit);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(
|
|
||||||
e,
|
|
||||||
"[{name}] {delegateName} PostPromotion error",
|
|
||||||
this.Name,
|
|
||||||
nameof(FontAtlasBuildStepDelegate));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if VeryVerboseLog
|
#if VeryVerboseLog
|
||||||
Log.Verbose("[{name}] Built from {source}.", this.Name, source);
|
Log.Verbose("[{name}] Built from {source}.", this.Name, source);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void ImGuiSceneOnNewRenderFrame()
|
private void ImGuiSceneOnNewRenderFrame()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ internal abstract class FontHandle : IFontHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event Action<IFontHandle>? ImFontChanged;
|
public event IFontHandle.ImFontChangedDelegate? ImFontChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event to be called on the first <see cref="IDisposable.Dispose"/> call.
|
/// Event to be called on the first <see cref="IDisposable.Dispose"/> call.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected event Action<IFontHandle>? Disposed;
|
protected event Action? Disposed;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Exception? LoadException => this.Manager.Substance?.GetBuildException(this);
|
public Exception? LoadException => this.Manager.Substance?.GetBuildException(this);
|
||||||
|
|
@ -70,6 +70,22 @@ internal abstract class FontHandle : IFontHandle
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes <see cref="IFontHandle.ImFontChanged"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">The font, locked during the call of <see cref="ImFontChanged"/>.</param>
|
||||||
|
public void InvokeImFontChanged(IFontHandle.ImFontLocked font)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.ImFontChanged?.Invoke(this, font);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, $"{nameof(this.InvokeImFontChanged)}: error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtains an instance of <see cref="ImFontPtr"/> corresponding to this font handle,
|
/// Obtains an instance of <see cref="ImFontPtr"/> corresponding to this font handle,
|
||||||
/// to be released after rendering the current frame.
|
/// to be released after rendering the current frame.
|
||||||
|
|
@ -220,35 +236,51 @@ internal abstract class FontHandle : IFontHandle
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<IFontHandle>();
|
var tcs = new TaskCompletionSource<IFontHandle>();
|
||||||
this.ImFontChanged += OnImFontChanged;
|
this.ImFontChanged += OnImFontChanged;
|
||||||
this.Disposed += OnImFontChanged;
|
this.Disposed += OnDisposed;
|
||||||
if (this.Available)
|
if (this.Available)
|
||||||
OnImFontChanged(this);
|
OnImFontChanged(this, default);
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
|
|
||||||
void OnImFontChanged(IFontHandle unused)
|
void OnImFontChanged(IFontHandle unused, IFontHandle.ImFontLocked unused2)
|
||||||
{
|
{
|
||||||
if (tcs.Task.IsCompletedSuccessfully)
|
if (tcs.Task.IsCompletedSuccessfully)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.ImFontChanged -= OnImFontChanged;
|
this.ImFontChanged -= OnImFontChanged;
|
||||||
this.Disposed -= OnImFontChanged;
|
this.Disposed -= OnDisposed;
|
||||||
if (this.manager is null)
|
try
|
||||||
tcs.SetException(new ObjectDisposedException(nameof(GamePrebakedFontHandle)));
|
{
|
||||||
else
|
|
||||||
tcs.SetResult(this);
|
tcs.SetResult(this);
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisposed()
|
||||||
|
{
|
||||||
|
if (tcs.Task.IsCompletedSuccessfully)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ImFontChanged -= OnImFontChanged;
|
||||||
|
this.Disposed -= OnDisposed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tcs.SetException(new ObjectDisposedException(nameof(GamePrebakedFontHandle)));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes <see cref="IFontHandle.ImFontChanged"/>.
|
/// Implementation for <see cref="IDisposable.Dispose"/>.
|
||||||
/// </summary>
|
|
||||||
protected void InvokeImFontChanged() => this.ImFontChanged.InvokeSafely(this);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overrideable implementation for <see cref="IDisposable.Dispose"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="disposing">If <c>true</c>, then the function is being called from <see cref="IDisposable.Dispose"/>.</param>
|
/// <param name="disposing">If <c>true</c>, then the function is being called from <see cref="IDisposable.Dispose"/>.</param>
|
||||||
protected virtual void Dispose(bool disposing)
|
protected void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
|
@ -256,7 +288,7 @@ internal abstract class FontHandle : IFontHandle
|
||||||
Log.Warning($"{nameof(IFontHandle)}.{nameof(IDisposable.Dispose)}: fonts were still in a stack.");
|
Log.Warning($"{nameof(IFontHandle)}.{nameof(IDisposable.Dispose)}: fonts were still in a stack.");
|
||||||
this.Manager.FreeFontHandle(this);
|
this.Manager.FreeFontHandle(this);
|
||||||
this.manager = null;
|
this.manager = null;
|
||||||
this.Disposed?.InvokeSafely(this);
|
this.Disposed?.InvokeSafely();
|
||||||
this.ImFontChanged = null;
|
this.ImFontChanged = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,16 +172,6 @@ internal class GamePrebakedFontHandle : FontHandle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void InvokeFontHandleImFontChanged()
|
|
||||||
{
|
|
||||||
if (this.Substance is not HandleSubstance hs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var handle in hs.RelevantHandles)
|
|
||||||
handle.InvokeImFontChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IFontHandleSubstance NewSubstance(IRefCountable dataRoot)
|
public IFontHandleSubstance NewSubstance(IRefCountable dataRoot)
|
||||||
{
|
{
|
||||||
|
|
@ -232,6 +222,9 @@ internal class GamePrebakedFontHandle : FontHandle
|
||||||
// Not owned by this class. Do not dispose.
|
// Not owned by this class. Do not dispose.
|
||||||
public GamePrebakedFontHandle[] RelevantHandles { get; }
|
public GamePrebakedFontHandle[] RelevantHandles { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
ICollection<FontHandle> IFontHandleSubstance.RelevantHandles => this.RelevantHandles;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IRefCountable DataRoot { get; }
|
public IRefCountable DataRoot { get; }
|
||||||
|
|
||||||
|
|
@ -413,12 +406,6 @@ internal class GamePrebakedFontHandle : FontHandle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void OnPostPromotion(IFontAtlasBuildToolkitPostPromotion toolkitPostPromotion)
|
|
||||||
{
|
|
||||||
// Irrelevant
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new template font.
|
/// Creates a new template font.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,4 @@ internal interface IFontHandleManager : IDisposable
|
||||||
/// <param name="dataRoot">The data root.</param>
|
/// <param name="dataRoot">The data root.</param>
|
||||||
/// <returns>The new substance.</returns>
|
/// <returns>The new substance.</returns>
|
||||||
IFontHandleSubstance NewSubstance(IRefCountable dataRoot);
|
IFontHandleSubstance NewSubstance(IRefCountable dataRoot);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes <see cref="IFontHandle.ImFontChanged"/>.
|
|
||||||
/// </summary>
|
|
||||||
void InvokeFontHandleImFontChanged();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using Dalamud.Utility;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
|
|
@ -32,6 +34,11 @@ internal interface IFontHandleSubstance : IDisposable
|
||||||
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
|
||||||
bool CreateFontOnAccess { get; set; }
|
bool CreateFontOnAccess { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the relevant handles.
|
||||||
|
/// </summary>
|
||||||
|
public ICollection<FontHandle> RelevantHandles { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the font.
|
/// Gets the font.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -64,11 +71,4 @@ internal interface IFontHandleSubstance : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="toolkitPostBuild">The toolkit.</param>
|
/// <param name="toolkitPostBuild">The toolkit.</param>
|
||||||
void OnPostBuild(IFontAtlasBuildToolkitPostBuild toolkitPostBuild);
|
void OnPostBuild(IFontAtlasBuildToolkitPostBuild toolkitPostBuild);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called on the specific thread depending on <see cref="IFontAtlasBuildToolkit.IsAsyncBuildOperation"/> after
|
|
||||||
/// promoting the staging atlas to direct use with <see cref="IFontAtlas"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toolkitPostPromotion">The toolkit.</param>
|
|
||||||
void OnPostPromotion(IFontAtlasBuildToolkitPostPromotion toolkitPostPromotion);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -744,7 +744,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
this.wrapped.ImFontChanged += this.WrappedOnImFontChanged;
|
this.wrapped.ImFontChanged += this.WrappedOnImFontChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action<IFontHandle>? ImFontChanged;
|
public event IFontHandle.ImFontChangedDelegate? ImFontChanged;
|
||||||
|
|
||||||
public Exception? LoadException =>
|
public Exception? LoadException =>
|
||||||
this.wrapped!.LoadException ?? new ObjectDisposedException(nameof(FontHandleWrapper));
|
this.wrapped!.LoadException ?? new ObjectDisposedException(nameof(FontHandleWrapper));
|
||||||
|
|
@ -775,6 +775,7 @@ public sealed class UiBuilder : IDisposable
|
||||||
|
|
||||||
public override string ToString() => $"{nameof(FontHandleWrapper)}({this.wrapped})";
|
public override string ToString() => $"{nameof(FontHandleWrapper)}({this.wrapped})";
|
||||||
|
|
||||||
private void WrappedOnImFontChanged(IFontHandle obj) => this.ImFontChanged.InvokeSafely(this);
|
private void WrappedOnImFontChanged(IFontHandle obj, IFontHandle.ImFontLocked lockedFont) =>
|
||||||
|
this.ImFontChanged?.Invoke(obj, lockedFont);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue