fix: wait for font rebuild when initially setting up ImGui

This is to work around changed behaviour with ImGui >1.78 wherein a race condition during font rebuild together with plugin loads would cause the remote CLR thread to end
This commit is contained in:
goat 2021-01-22 19:25:22 +01:00
parent 2c66ecc84e
commit d949734fe9
2 changed files with 83 additions and 67 deletions

View file

@ -178,91 +178,94 @@ namespace Dalamud {
} }
public void Start() { public void Start() {
Configuration = DalamudConfiguration.Load(StartInfo.ConfigurationPath); try {
Configuration = DalamudConfiguration.Load(StartInfo.ConfigurationPath);
// Initialize the process information. // Initialize the process information.
TargetModule = Process.GetCurrentProcess().MainModule; TargetModule = Process.GetCurrentProcess().MainModule;
SigScanner = new SigScanner(TargetModule, true); SigScanner = new SigScanner(TargetModule, true);
AntiDebug = new AntiDebug(SigScanner); AntiDebug = new AntiDebug(SigScanner);
#if DEBUG #if DEBUG
AntiDebug.Enable(); AntiDebug.Enable();
#endif #endif
// Initialize game subsystem // Initialize game subsystem
Framework = new Framework(SigScanner, this); Framework = new Framework(SigScanner, this);
WinSock2 = new WinSockHandlers(); WinSock2 = new WinSockHandlers();
NetworkHandlers = new NetworkHandlers(this, StartInfo.OptOutMbCollection); NetworkHandlers = new NetworkHandlers(this, StartInfo.OptOutMbCollection);
ClientState = new ClientState(this, StartInfo, SigScanner); ClientState = new ClientState(this, StartInfo, SigScanner);
LocalizationManager = new Localization(AssetDirectory.FullName); LocalizationManager = new Localization(AssetDirectory.FullName);
if (!string.IsNullOrEmpty(Configuration.LanguageOverride)) if (!string.IsNullOrEmpty(Configuration.LanguageOverride))
LocalizationManager.SetupWithLangCode(Configuration.LanguageOverride); LocalizationManager.SetupWithLangCode(Configuration.LanguageOverride);
else else
LocalizationManager.SetupWithUiCulture(); LocalizationManager.SetupWithUiCulture();
PluginRepository = new PluginRepository(this, StartInfo.PluginDirectory, StartInfo.GameVersion); PluginRepository = new PluginRepository(this, StartInfo.PluginDirectory, StartInfo.GameVersion);
DalamudUi = new DalamudInterface(this); DalamudUi = new DalamudInterface(this);
var isInterfaceLoaded = false; var isInterfaceLoaded = false;
if (!bool.Parse(Environment.GetEnvironmentVariable("DALAMUD_NOT_HAVE_INTERFACE") ?? "false")) { if (!bool.Parse(Environment.GetEnvironmentVariable("DALAMUD_NOT_HAVE_INTERFACE") ?? "false")) {
try {
InterfaceManager = new InterfaceManager(this, SigScanner);
InterfaceManager.OnDraw += DalamudUi.Draw;
InterfaceManager.Enable();
isInterfaceLoaded = true;
InterfaceManager.WaitForFontRebuild();
} catch (Exception e) {
Log.Information(e, "Could not init interface.");
}
}
Data = new DataManager(StartInfo.Language);
try { try {
InterfaceManager = new InterfaceManager(this, SigScanner); Data.Initialize(AssetDirectory.FullName);
InterfaceManager.OnDraw += DalamudUi.Draw;
InterfaceManager.Enable();
isInterfaceLoaded = true;
} catch (Exception e) { } catch (Exception e) {
Log.Information(e, "Could not init interface."); Log.Error(e, "Could not initialize DataManager.");
Unload();
return;
} }
}
Data = new DataManager(StartInfo.Language); SeStringManager = new SeStringManager(Data);
try {
Data.Initialize(AssetDirectory.FullName); // Initialize managers. Basically handlers for the logic
} catch (Exception e) { CommandManager = new CommandManager(this, StartInfo.Language);
Log.Error(e, "Could not initialize DataManager."); DalamudCommands = new DalamudCommands(this);
DalamudCommands.SetupCommands();
ChatHandlers = new ChatHandlers(this);
if (!bool.Parse(Environment.GetEnvironmentVariable("DALAMUD_NOT_HAVE_PLUGINS") ?? "false")) {
try {
PluginRepository.CleanupPlugins();
PluginManager =
new PluginManager(this, StartInfo.PluginDirectory, StartInfo.DefaultPluginDirectory);
PluginManager.LoadPlugins();
} catch (Exception ex) {
Log.Error(ex, "Plugin load failed.");
}
}
Framework.Enable();
ClientState.Enable();
IsReady = true;
Troubleshooting.LogTroubleshooting(this, isInterfaceLoaded);
Log.Information("Dalamud is ready.");
} catch (Exception ex) {
Log.Error(ex, "Oh no! Dalamud::Start() failed.");
Unload(); Unload();
return;
} }
SeStringManager = new SeStringManager(Data);
// Initialize managers. Basically handlers for the logic
CommandManager = new CommandManager(this, StartInfo.Language);
DalamudCommands = new DalamudCommands(this);
DalamudCommands.SetupCommands();
ChatHandlers = new ChatHandlers(this);
if (!bool.Parse(Environment.GetEnvironmentVariable("DALAMUD_NOT_HAVE_PLUGINS") ?? "false"))
{
try
{
PluginRepository.CleanupPlugins();
PluginManager =
new PluginManager(this, StartInfo.PluginDirectory, StartInfo.DefaultPluginDirectory);
PluginManager.LoadPlugins();
}
catch (Exception ex)
{
Log.Error(ex, "Plugin load failed.");
}
}
Framework.Enable();
ClientState.Enable();
IsReady = true;
Troubleshooting.LogTroubleshooting(this, isInterfaceLoaded);
Log.Information("Dalamud is ready.");
} }
public void Unload() { public void Unload() {

View file

@ -4,6 +4,7 @@ using System.IO;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.Internal.DXGI; using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking; using Dalamud.Hooking;
@ -42,6 +43,8 @@ namespace Dalamud.Interface
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr SetCursorDelegate(IntPtr hCursor); private delegate IntPtr SetCursorDelegate(IntPtr hCursor);
private ManualResetEvent fontBuildSignal;
private ISwapChainAddressResolver Address { get; } private ISwapChainAddressResolver Address { get; }
private Dalamud dalamud; private Dalamud dalamud;
@ -68,6 +71,8 @@ namespace Dalamud.Interface
{ {
this.dalamud = dalamud; this.dalamud = dalamud;
this.fontBuildSignal = new ManualResetEvent(false);
try { try {
var sigResolver = new SwapChainSigResolver(); var sigResolver = new SwapChainSigResolver();
sigResolver.Setup(scanner); sigResolver.Setup(scanner);
@ -272,6 +277,8 @@ namespace Dalamud.Interface
private unsafe void SetupFonts() private unsafe void SetupFonts()
{ {
this.fontBuildSignal.Reset();
ImGui.GetIO().Fonts.Clear(); ImGui.GetIO().Fonts.Clear();
ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig();
@ -317,12 +324,18 @@ namespace Dalamud.Interface
Log.Verbose("[FONT] Fonts built!"); Log.Verbose("[FONT] Fonts built!");
this.fontBuildSignal.Set();
fontConfig.Destroy(); fontConfig.Destroy();
japaneseRangeHandle.Free(); japaneseRangeHandle.Free();
gameRangeHandle.Free(); gameRangeHandle.Free();
iconRangeHandle.Free(); iconRangeHandle.Free();
} }
public void WaitForFontRebuild() {
this.fontBuildSignal.WaitOne();
}
// This is intended to only be called as a handler attached to scene.OnNewRenderFrame // This is intended to only be called as a handler attached to scene.OnNewRenderFrame
private void RebuildFontsInternal() private void RebuildFontsInternal()
{ {