Move PostPromotion modification functions to PostBuild

These changes are done to ensure that `IFontHandle.Lock` will be guaranteed to obtain a fully built font that will not be modified any further (unless `PostPromotion` is being used for modifying fonts, which should not be done by clients.)

* Moved `CopyGlyphsAcrossFonts` and `BuildLookupTable` from `PostPromotion` to `PostBuild` build toolkit.
* `IFontAtlasBuildToolkit`: Added `GetFont` to enable retrieving font corresponding to a handle being built.
* `InterfaceManager`: Use `OnPostBuild` for copying glyphs from Mono to Default.
* `FontAtlasBuildStep`:
    * Removed `Invalid` to prevent an unnecessary switch-case warnings.
    * Added contracts on when `IFontAtlas.BuildStepChanged` will be called.
This commit is contained in:
Soreepeong 2024-01-23 22:09:47 +09:00
parent 5479149e79
commit fb8beb9370
8 changed files with 180 additions and 136 deletions

View file

@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Interface.GameFonts;
@ -168,7 +167,7 @@ internal sealed partial class FontAtlasFactory
_ => throw new InvalidOperationException(),
};
public unsafe int Release()
public int Release()
{
switch (IRefCountable.AlterRefCount(-1, ref this.refCount, out var newRefCount))
{
@ -176,22 +175,35 @@ internal sealed partial class FontAtlasFactory
return newRefCount;
case IRefCountable.RefCountResult.FinalRelease:
if (this.IsBuildInProgress)
{
Log.Error(
"[{name}] 0x{ptr:X}: Trying to dispose while build is in progress; waiting for build.\n" +
"Stack:\n{trace}",
this.Owner?.Name ?? "<?>",
(nint)this.Atlas.NativePtr,
new StackTrace());
while (this.IsBuildInProgress)
Thread.Sleep(100);
}
#if VeryVerboseLog
Log.Verbose("[{name}] 0x{ptr:X}: Disposing", this.Owner?.Name ?? "<?>", (nint)this.Atlas.NativePtr);
#endif
this.Garbage.Dispose();
if (this.IsBuildInProgress)
{
unsafe
{
Log.Error(
"[{name}] 0x{ptr:X}: Trying to dispose while build is in progress; disposing later.\n" +
"Stack:\n{trace}",
this.Owner?.Name ?? "<?>",
(nint)this.Atlas.NativePtr,
new StackTrace());
}
Task.Run(
async () =>
{
while (this.IsBuildInProgress)
await Task.Delay(100);
this.Garbage.Dispose();
});
}
else
{
this.Garbage.Dispose();
}
return newRefCount;
case IRefCountable.RefCountResult.AlreadyDisposed:
@ -549,20 +561,10 @@ internal sealed partial class FontAtlasFactory
return;
}
var toolkit = new BuildToolkitPostPromotion(data);
foreach (var substance in data.Substances)
substance.Manager.InvokeFontHandleImFontChanged();
try
{
this.BuildStepChange?.Invoke(toolkit);
}
catch (Exception e)
{
Log.Error(
e,
"[{name}] {delegateName} PostPromotion error",
this.Name,
nameof(FontAtlasBuildStepDelegate));
}
var toolkit = new BuildToolkitPostPromotion(data);
foreach (var substance in data.Substances)
{
@ -580,20 +582,18 @@ internal sealed partial class FontAtlasFactory
}
}
foreach (var font in toolkit.Fonts)
try
{
try
{
toolkit.BuildLookupTable(font);
}
catch (Exception e)
{
Log.Error(e, "[{name}] BuildLookupTable error", this.Name);
}
this.BuildStepChange?.Invoke(toolkit);
}
catch (Exception e)
{
Log.Error(
e,
"[{name}] {delegateName} PostPromotion error",
this.Name,
nameof(FontAtlasBuildStepDelegate));
}
foreach (var substance in data.Substances)
substance.Manager.InvokeFontHandleImFontChanged();
#if VeryVerboseLog
Log.Verbose("[{name}] Built from {source}.", this.Name, source);
@ -709,6 +709,9 @@ internal sealed partial class FontAtlasFactory
toolkit.PostBuildSubstances();
this.BuildStepChange?.Invoke(toolkit);
foreach (var font in toolkit.Fonts)
toolkit.BuildLookupTable(font);
if (this.factory.SceneTask is { IsCompleted: false } sceneTask)
{
Log.Verbose(
@ -754,6 +757,8 @@ internal sealed partial class FontAtlasFactory
}
finally
{
// RS is being dumb
// ReSharper disable once ConstantConditionalAccessQualifier
toolkit?.Dispose();
this.buildQueued = false;
}