Load services asynchronously whenever possible (#893)

This commit is contained in:
kizer 2022-06-25 05:12:51 +09:00 committed by GitHub
parent fba8c7163c
commit 8e7f370ddd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 959 additions and 899 deletions

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Dalamud.Data;
using Dalamud.Interface.Internal;
using Dalamud.Utility.Timing;
@ -17,9 +17,10 @@ namespace Dalamud.Interface.GameFonts
/// <summary>
/// Loads game font for use in ImGui.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal class GameFontManager : IDisposable
{
private static readonly string[] FontNames =
private static readonly string?[] FontNames =
{
null,
"AXIS_96", "AXIS_12", "AXIS_14", "AXIS_18", "AXIS_36",
@ -31,8 +32,6 @@ namespace Dalamud.Interface.GameFonts
private readonly object syncRoot = new();
private readonly InterfaceManager interfaceManager;
private readonly FdtReader?[] fdts;
private readonly List<byte[]> texturePixels;
private readonly Dictionary<GameFontStyle, ImFontPtr> fonts = new();
@ -42,40 +41,28 @@ namespace Dalamud.Interface.GameFonts
private bool isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild = false;
private bool isBuildingAsFallbackFontMode = false;
/// <summary>
/// Initializes a new instance of the <see cref="GameFontManager"/> class.
/// </summary>
public GameFontManager()
[ServiceManager.ServiceConstructor]
private GameFontManager(DataManager dataManager)
{
var dataManager = Service<DataManager>.Get();
using (Timings.Start("Load FDTs"))
using (Timings.Start("Getting fdt data"))
{
this.fdts = FontNames.Select(fontName =>
{
var fileName = $"common/font/{fontName}.fdt";
using (Timings.Start($"Loading FDT: {fileName}"))
{
var file = fontName == null ? null : dataManager.GetFile(fileName);
return file == null ? null : new FdtReader(file!.Data);
}
}).ToArray();
this.fdts = FontNames.Select(fontName => fontName == null ? null : new FdtReader(dataManager.GetFile($"common/font/{fontName}.fdt")!.Data)).ToArray();
}
using (Timings.Start("Getting texture data"))
{
this.texturePixels = Enumerable.Range(1, 1 + this.fdts.Where(x => x != null).Select(x => x.Glyphs.Select(x => x.TextureFileIndex).Max()).Max()).Select(
x =>
{
var fileName = $"common/font/font{x}.tex";
using (Timings.Start($"Get tex: {fileName}"))
{
return dataManager.GameData.GetFile<TexFile>(fileName)!.ImageData;
}
}).ToList();
var texTasks = Enumerable
.Range(1, 1 + this.fdts
.Where(x => x != null)
.Select(x => x.Glyphs.Select(y => y.TextureFileIndex).Max())
.Max())
.Select(x => dataManager.GetFile<TexFile>($"common/font/font{x}.tex")!)
.Select(x => new Task<byte[]>(() => x.ImageData!))
.ToArray();
foreach (var task in texTasks)
task.Start();
this.texturePixels = texTasks.Select(x => x.GetAwaiter().GetResult()).ToList();
}
this.interfaceManager = Service<InterfaceManager>.Get();
}
/// <summary>
@ -183,6 +170,7 @@ namespace Dalamud.Interface.GameFonts
/// <returns>Handle to game font that may or may not be ready yet.</returns>
public GameFontHandle NewFontRef(GameFontStyle style)
{
var interfaceManager = Service<InterfaceManager>.Get();
var needRebuild = false;
lock (this.syncRoot)
@ -193,7 +181,7 @@ namespace Dalamud.Interface.GameFonts
needRebuild = !this.fonts.ContainsKey(style);
if (needRebuild)
{
if (Service<InterfaceManager>.Get().IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild)
if (interfaceManager.IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild)
{
Log.Information("[GameFontManager] NewFontRef: Building {0} right now, as it is called while BuildFonts is already in progress yet atlas build has not been called yet.", style.ToString());
this.EnsureFont(style);
@ -201,7 +189,7 @@ namespace Dalamud.Interface.GameFonts
else
{
Log.Information("[GameFontManager] NewFontRef: Calling RebuildFonts because {0} has been requested.", style.ToString());
this.interfaceManager.RebuildFonts();
interfaceManager.RebuildFonts();
}
}
@ -294,10 +282,11 @@ namespace Dalamud.Interface.GameFonts
/// </summary>
public unsafe void AfterBuildFonts()
{
var interfaceManager = Service<InterfaceManager>.Get();
var ioFonts = ImGui.GetIO().Fonts;
ioFonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height);
var pixels32 = (uint*)pixels8;
var fontGamma = this.interfaceManager.FontGamma;
var fontGamma = interfaceManager.FontGamma;
foreach (var (style, font) in this.fonts)
{

View file

@ -18,22 +18,12 @@ namespace Dalamud.Interface.Internal
/// <summary>
/// Class handling Dalamud core commands.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal class DalamudCommands
{
/// <summary>
/// Initializes a new instance of the <see cref="DalamudCommands"/> class.
/// </summary>
public DalamudCommands()
[ServiceManager.ServiceConstructor]
private DalamudCommands(CommandManager commandManager)
{
}
/// <summary>
/// Register all command handlers with the Dalamud instance.
/// </summary>
public void SetupCommands()
{
var commandManager = Service<CommandManager>.Get();
commandManager.AddHandler("/xldclose", new CommandInfo(this.OnUnloadCommand)
{
HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),

View file

@ -35,6 +35,7 @@ namespace Dalamud.Interface.Internal
/// <summary>
/// This plugin implements all of the Dalamud interface separately, to allow for reloading of the interface and rapid prototyping.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal class DalamudInterface : IDisposable
{
private static readonly ModuleLog Log = new("DUI");
@ -75,14 +76,9 @@ namespace Dalamud.Interface.Internal
private bool isImGuiTestWindowsInMonospace = false;
private bool isImGuiDrawMetricsWindow = false;
/// <summary>
/// Initializes a new instance of the <see cref="DalamudInterface"/> class.
/// </summary>
public DalamudInterface()
[ServiceManager.ServiceConstructor]
private DalamudInterface(Dalamud dalamud, DalamudConfiguration configuration, InterfaceManager interfaceManager)
{
var configuration = Service<DalamudConfiguration>.Get();
var interfaceManager = Service<InterfaceManager>.Get();
this.WindowSystem = new WindowSystem("DalamudCore");
this.changelogWindow = new ChangelogWindow() { IsOpen = false };
@ -123,7 +119,6 @@ namespace Dalamud.Interface.Internal
this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup;
interfaceManager.Draw += this.OnDraw;
var dalamud = Service<Dalamud>.Get();
var logoTex =
interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"));

View file

@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Game.ClientState.GamePad;
@ -44,6 +44,7 @@ namespace Dalamud.Interface.Internal
/// <summary>
/// This class manages interaction with the ImGui interface.
/// </summary>
[ServiceManager.BlockingEarlyLoadedService]
internal class InterfaceManager : IDisposable
{
private const float MinimumFallbackFontSizePt = 9.6f; // Game's minimum AXIS font size
@ -58,33 +59,27 @@ namespace Dalamud.Interface.Internal
private readonly HashSet<SpecialGlyphRequest> glyphRequests = new();
private readonly Dictionary<ImFontPtr, TargetFontModification> loadedFontInfo = new();
private readonly Hook<PresentDelegate> presentHook;
private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook;
private readonly Hook<SetCursorDelegate> setCursorHook;
private readonly ManualResetEvent fontBuildSignal;
private readonly SwapChainVtableResolver address;
private readonly TaskCompletionSource sceneInitializeTaskCompletionSource = new();
private RawDX11Scene? scene;
private Hook<PresentDelegate>? presentHook;
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
private Hook<SetCursorDelegate>? setCursorHook;
// can't access imgui IO before first present call
private bool lastWantCapture = false;
private bool isRebuildingFonts = false;
private bool isFallbackFontMode = false;
/// <summary>
/// Initializes a new instance of the <see cref="InterfaceManager"/> class.
/// </summary>
public InterfaceManager()
[ServiceManager.ServiceConstructor]
private InterfaceManager()
{
Service<NotificationManager>.Set();
var scanner = Service<SigScanner>.Get();
this.fontBuildSignal = new ManualResetEvent(false);
this.address = new SwapChainVtableResolver();
this.address.Setup(scanner);
try
{
@ -106,16 +101,12 @@ namespace Dalamud.Interface.Internal
Log.Error(e, "RTSS Free failed");
}
this.setCursorHook = Hook<SetCursorDelegate>.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour, true);
this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour);
var setCursorAddress = this.setCursorHook?.Address ?? IntPtr.Zero;
Log.Verbose("===== S W A P C H A I N =====");
Log.Verbose($"SetCursor address 0x{setCursorAddress.ToInt64():X}");
Log.Verbose($"Present address 0x{this.presentHook.Address.ToInt64():X}");
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook.Address.ToInt64():X}");
Task.Run(async () =>
{
var framework = await Service<Framework>.GetAsync();
var sigScanner = await Service<SigScanner>.GetAsync();
await framework.RunOnFrameworkThread(() => this.Enable(sigScanner));
});
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@ -129,6 +120,11 @@ namespace Dalamud.Interface.Internal
private delegate void InstallRTSSHook();
/// <summary>
/// Gets a task that gets completed when scene gets initialized.
/// </summary>
public Task SceneInitializeTask => this.sceneInitializeTaskCompletionSource.Task;
/// <summary>
/// This event gets called each frame to facilitate ImGui drawing.
/// </summary>
@ -259,34 +255,6 @@ namespace Dalamud.Interface.Internal
/// </summary>
public bool IsBuildingFontsBeforeAtlasBuild => this.isRebuildingFonts && !this.fontBuildSignal.WaitOne(0);
/// <summary>
/// Enable this module.
/// </summary>
public void Enable()
{
this.setCursorHook?.Enable();
this.presentHook.Enable();
this.resizeBuffersHook.Enable();
try
{
if (!string.IsNullOrEmpty(this.rtssPath))
{
NativeFunctions.LoadLibraryW(this.rtssPath);
var rtssModule = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
var installAddr = NativeFunctions.GetProcAddress(rtssModule, "InstallRTSSHook");
Log.Debug("Installing RTSS hook");
Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr).Invoke();
Log.Debug("RTSS hook OK!");
}
}
catch (Exception ex)
{
Log.Error(ex, "Could not reload RTSS");
}
}
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
@ -303,8 +271,8 @@ namespace Dalamud.Interface.Internal
this.scene?.Dispose();
this.setCursorHook?.Dispose();
this.presentHook.Dispose();
this.resizeBuffersHook.Dispose();
this.presentHook?.Dispose();
this.resizeBuffersHook?.Dispose();
}
#nullable enable
@ -480,9 +448,11 @@ namespace Dalamud.Interface.Internal
try
{
this.scene = new RawDX11Scene(swapChain);
this.sceneInitializeTaskCompletionSource.SetResult();
}
catch (DllNotFoundException ex)
{
this.sceneInitializeTaskCompletionSource.SetException(ex);
Log.Error(ex, "Could not load ImGui dependencies.");
var res = PInvoke.User32.MessageBox(
@ -1003,6 +973,43 @@ namespace Dalamud.Interface.Internal
}
}
private void Enable(SigScanner sigScanner)
{
this.address.Setup(sigScanner);
this.setCursorHook = Hook<SetCursorDelegate>.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour, true);
this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour);
var setCursorAddress = this.setCursorHook?.Address ?? IntPtr.Zero;
Log.Verbose("===== S W A P C H A I N =====");
Log.Verbose($"SetCursor address 0x{setCursorAddress.ToInt64():X}");
Log.Verbose($"Present address 0x{this.presentHook.Address.ToInt64():X}");
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook.Address.ToInt64():X}");
this.setCursorHook?.Enable();
this.presentHook.Enable();
this.resizeBuffersHook.Enable();
try
{
if (!string.IsNullOrEmpty(this.rtssPath))
{
NativeFunctions.LoadLibraryW(this.rtssPath);
var rtssModule = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
var installAddr = NativeFunctions.GetProcAddress(rtssModule, "InstallRTSSHook");
Log.Debug("Installing RTSS hook");
Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr).Invoke();
Log.Debug("RTSS hook OK!");
}
}
catch (Exception ex)
{
Log.Error(ex, "Could not reload RTSS");
}
}
private void Disable()
{
this.setCursorHook?.Disable();
@ -1094,6 +1101,9 @@ namespace Dalamud.Interface.Internal
var gamepadState = Service<GamepadState>.GetNullable();
var keyState = Service<KeyState>.GetNullable();
if (dalamudInterface == null || gamepadState == null || keyState == null)
return;
// fix for keys in game getting stuck, if you were holding a game key (like run)
// and then clicked on an imgui textbox - imgui would swallow the keyup event,
// so the game would think the key remained pressed continuously until you left

View file

@ -13,6 +13,7 @@ namespace Dalamud.Interface.Internal.Notifications
/// Class handling notifications/toasts in ImGui.
/// Ported from https://github.com/patrickcjk/imgui-notify.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal class NotificationManager
{
/// <summary>
@ -54,6 +55,11 @@ namespace Dalamud.Interface.Internal.Notifications
private readonly List<Notification> notifications = new();
[ServiceManager.ServiceConstructor]
private NotificationManager()
{
}
/// <summary>
/// Add a notification to the notification queue.
/// </summary>

View file

@ -1485,7 +1485,7 @@ namespace Dalamud.Interface.Internal.Windows
ImGuiHelpers.ScaledDummy(20);
// Needed to init the task tracker, if we're not on a debug build
var tracker = Service<TaskTracker>.GetNullable() ?? Service<TaskTracker>.Set();
Service<TaskTracker>.Get().Enable();
for (var i = 0; i < TaskTracker.Tasks.Count; i++)
{

View file

@ -60,6 +60,8 @@ namespace Dalamud.Interface.Internal.Windows
var dalamud = Service<Dalamud>.Get();
var interfaceManager = Service<InterfaceManager>.Get();
interfaceManager.SceneInitializeTask.Wait();
this.DefaultIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "defaultIcon.png"))!;
this.TroubleIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "troubleIcon.png"))!;
this.UpdateIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "updateIcon.png"))!;

View file

@ -33,6 +33,7 @@ namespace Dalamud.Interface.Internal.Windows
private XivChatType dalamudMessagesChatType;
private bool doWaitForPluginsOnStartup;
private bool doCfTaskBarFlash;
private bool doCfChatMessage;
private bool doMbCollect;
@ -90,6 +91,7 @@ namespace Dalamud.Interface.Internal.Windows
this.dalamudMessagesChatType = configuration.GeneralChatType;
this.doWaitForPluginsOnStartup = configuration.IsResumeGameAfterPluginLoad;
this.doCfTaskBarFlash = configuration.DutyFinderTaskbarFlash;
this.doCfChatMessage = configuration.DutyFinderChatMessage;
this.doMbCollect = configuration.IsMbCollect;
@ -261,6 +263,9 @@ namespace Dalamud.Interface.Internal.Windows
ImGuiHelpers.ScaledDummy(5);
ImGui.Checkbox(Loc.Localize("DalamudSettingsWaitForPluginsOnStartup", "Wait for plugins before game loads"), ref this.doWaitForPluginsOnStartup);
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsWaitForPluginsOnStartupHint", "Do not let the game load, until plugins are loaded."));
ImGui.Checkbox(Loc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"), ref this.doCfTaskBarFlash);
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."));
@ -916,6 +921,7 @@ namespace Dalamud.Interface.Internal.Windows
configuration.GeneralChatType = this.dalamudMessagesChatType;
configuration.IsResumeGameAfterPluginLoad = this.doWaitForPluginsOnStartup;
configuration.DutyFinderTaskbarFlash = this.doCfTaskBarFlash;
configuration.DutyFinderChatMessage = this.doCfChatMessage;
configuration.IsMbCollect = this.doMbCollect;

View file

@ -12,6 +12,7 @@ namespace Dalamud.Interface
/// </summary>
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
public class TitleScreenMenu
{
/// <summary>
@ -21,6 +22,11 @@ namespace Dalamud.Interface
private readonly List<TitleScreenMenuEntry> entries = new();
[ServiceManager.ServiceConstructor]
private TitleScreenMenu()
{
}
/// <summary>
/// Gets the list of entries in the title screen menu.
/// </summary>