Merge pull request #1699 from Soreepeong/fix/ime-perf

Fix Chinese IME lagging
This commit is contained in:
goat 2024-03-08 23:04:47 +01:00 committed by GitHub
commit 7ee20272de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 620 additions and 531 deletions

View file

@ -96,12 +96,6 @@ internal class DalamudCommands : IServiceType
ShowInHelp = false,
});
commandManager.AddHandler("/xlime", new CommandInfo(this.OnDebugDrawIMEPanel)
{
HelpMessage = Loc.Localize("DalamudIMEPanelHelp", "Draw IME panel"),
ShowInHelp = false,
});
commandManager.AddHandler("/xllog", new CommandInfo(this.OnOpenLog)
{
HelpMessage = Loc.Localize("DalamudDevLogHelp", "Open dev log DEBUG"),
@ -308,11 +302,6 @@ internal class DalamudCommands : IServiceType
dalamudInterface.ToggleDataWindow(arguments);
}
private void OnDebugDrawIMEPanel(string command, string arguments)
{
Service<DalamudInterface>.Get().OpenImeWindow();
}
private void OnOpenLog(string command, string arguments)
{
Service<DalamudInterface>.Get().ToggleLogWindow();

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,6 @@ internal class DalamudInterface : IDisposable, IServiceType
private readonly ComponentDemoWindow componentDemoWindow;
private readonly DataWindow dataWindow;
private readonly GamepadModeNotifierWindow gamepadModeNotifierWindow;
private readonly DalamudImeWindow imeWindow;
private readonly ConsoleWindow consoleWindow;
private readonly PluginStatWindow pluginStatWindow;
private readonly PluginInstallerWindow pluginWindow;
@ -114,7 +113,6 @@ internal class DalamudInterface : IDisposable, IServiceType
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
this.dataWindow = new DataWindow() { IsOpen = false };
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow() { IsOpen = false };
this.imeWindow = new DalamudImeWindow() { IsOpen = false };
this.consoleWindow = new ConsoleWindow(configuration) { IsOpen = configuration.LogOpenAtStartup };
this.pluginStatWindow = new PluginStatWindow() { IsOpen = false };
this.pluginWindow = new PluginInstallerWindow(pluginImageCache, configuration) { IsOpen = false };
@ -142,7 +140,6 @@ internal class DalamudInterface : IDisposable, IServiceType
this.WindowSystem.AddWindow(this.componentDemoWindow);
this.WindowSystem.AddWindow(this.dataWindow);
this.WindowSystem.AddWindow(this.gamepadModeNotifierWindow);
this.WindowSystem.AddWindow(this.imeWindow);
this.WindowSystem.AddWindow(this.consoleWindow);
this.WindowSystem.AddWindow(this.pluginStatWindow);
this.WindowSystem.AddWindow(this.pluginWindow);
@ -265,11 +262,6 @@ internal class DalamudInterface : IDisposable, IServiceType
/// </summary>
public void OpenGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="DalamudImeWindow"/>.
/// </summary>
public void OpenImeWindow() => this.imeWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="ConsoleWindow"/>.
/// </summary>
@ -365,11 +357,6 @@ internal class DalamudInterface : IDisposable, IServiceType
#region Close
/// <summary>
/// Closes the <see cref="DalamudImeWindow"/>.
/// </summary>
public void CloseImeWindow() => this.imeWindow.IsOpen = false;
/// <summary>
/// Closes the <see cref="GamepadModeNotifierWindow"/>.
/// </summary>
@ -417,11 +404,6 @@ internal class DalamudInterface : IDisposable, IServiceType
/// </summary>
public void ToggleGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.Toggle();
/// <summary>
/// Toggles the <see cref="DalamudImeWindow"/>.
/// </summary>
public void ToggleImeWindow() => this.imeWindow.Toggle();
/// <summary>
/// Toggles the <see cref="ConsoleWindow"/>.
/// </summary>

View file

@ -67,9 +67,6 @@ internal class InterfaceManager : IDisposable, IServiceType
[ServiceManager.ServiceDependency]
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
[ServiceManager.ServiceDependency]
private readonly DalamudIme dalamudIme = Service<DalamudIme>.Get();
private readonly SwapChainVtableResolver address = new();
private readonly Hook<SetCursorDelegate> setCursorHook;
@ -627,8 +624,6 @@ internal class InterfaceManager : IDisposable, IServiceType
var r = this.scene?.ProcessWndProcW(args.Hwnd, (User32.WindowMessage)args.Message, args.WParam, args.LParam);
if (r is not null)
args.SuppressWithValue(r.Value);
this.dalamudIme.ProcessImeMessage(args);
}
/*

View file

@ -1,266 +0,0 @@
using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows;
/// <summary>
/// A window for displaying IME details.
/// </summary>
internal unsafe class DalamudImeWindow : Window
{
private const int ImePageSize = 9;
/// <summary>
/// Initializes a new instance of the <see cref="DalamudImeWindow"/> class.
/// </summary>
public DalamudImeWindow()
: base(
"Dalamud IME",
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoBackground)
{
this.Size = default(Vector2);
this.RespectCloseHotkey = false;
}
/// <inheritdoc/>
public override void Draw()
{
}
/// <inheritdoc/>
public override void PostDraw()
{
if (Service<DalamudIme>.GetNullable() is not { } ime)
return;
var viewport = ime.AssociatedViewport;
if (viewport.NativePtr is null)
return;
var drawCand = ime.ImmCand.Count != 0;
var drawConv = drawCand || ime.ShowPartialConversion;
var drawIme = ime.InputModeIcon != 0;
var imeIconFont = InterfaceManager.DefaultFont;
var pad = ImGui.GetStyle().WindowPadding;
var candTextSize = ImGui.CalcTextSize(ime.ImmComp == string.Empty ? " " : ime.ImmComp);
var native = ime.ImmCandNative;
var totalIndex = native.dwSelection + 1;
var totalSize = native.dwCount;
var pageStart = native.dwPageStart;
var pageIndex = (pageStart / ImePageSize) + 1;
var pageCount = (totalSize / ImePageSize) + 1;
var pageInfo = $"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})";
// Calc the window size.
var maxTextWidth = 0f;
for (var i = 0; i < ime.ImmCand.Count; i++)
{
var textSize = ImGui.CalcTextSize($"{i + 1}. {ime.ImmCand[i]}");
maxTextWidth = maxTextWidth > textSize.X ? maxTextWidth : textSize.X;
}
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(pageInfo).X ? maxTextWidth : ImGui.CalcTextSize(pageInfo).X;
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(ime.ImmComp).X
? maxTextWidth
: ImGui.CalcTextSize(ime.ImmComp).X;
var numEntries = (drawCand ? ime.ImmCand.Count + 1 : 0) + 1 + (drawIme ? 1 : 0);
var spaceY = ImGui.GetStyle().ItemSpacing.Y;
var imeWindowHeight = (spaceY * (numEntries - 1)) + (candTextSize.Y * numEntries);
var windowSize = new Vector2(maxTextWidth, imeWindowHeight) + (pad * 2);
// 1. Figure out the expanding direction.
var expandUpward = ime.CursorPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y;
var windowPos = ime.CursorPos - pad;
if (expandUpward)
{
windowPos.Y -= windowSize.Y - candTextSize.Y - (pad.Y * 2);
if (drawIme)
windowPos.Y += candTextSize.Y + spaceY;
}
else
{
if (drawIme)
windowPos.Y -= candTextSize.Y + spaceY;
}
// 2. Contain within the viewport. Do not use clamp, as the target window might be too small.
if (windowPos.X < viewport.WorkPos.X)
windowPos.X = viewport.WorkPos.X;
else if (windowPos.X + windowSize.X > viewport.WorkPos.X + viewport.WorkSize.X)
windowPos.X = (viewport.WorkPos.X + viewport.WorkSize.X) - windowSize.X;
if (windowPos.Y < viewport.WorkPos.Y)
windowPos.Y = viewport.WorkPos.Y;
else if (windowPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y)
windowPos.Y = (viewport.WorkPos.Y + viewport.WorkSize.Y) - windowSize.Y;
var cursor = windowPos + pad;
// Draw the ime window.
var drawList = ImGui.GetForegroundDrawList(viewport);
// Draw the background rect for candidates.
if (drawCand)
{
Vector2 candRectLt, candRectRb;
if (!expandUpward)
{
candRectLt = windowPos + candTextSize with { X = 0 } + pad with { X = 0 };
candRectRb = windowPos + windowSize;
if (drawIme)
candRectLt.Y += spaceY + candTextSize.Y;
}
else
{
candRectLt = windowPos;
candRectRb = windowPos + (windowSize - candTextSize with { X = 0 } - pad with { X = 0 });
if (drawIme)
candRectRb.Y -= spaceY + candTextSize.Y;
}
drawList.AddRectFilled(
candRectLt,
candRectRb,
ImGui.GetColorU32(ImGuiCol.WindowBg),
ImGui.GetStyle().WindowRounding);
}
if (!expandUpward && drawIme)
{
for (var dx = -2; dx <= 2; dx++)
{
for (var dy = -2; dy <= 2; dy++)
{
if (dx != 0 || dy != 0)
{
imeIconFont.RenderChar(
drawList,
imeIconFont.FontSize,
cursor + new Vector2(dx, dy),
ImGui.GetColorU32(ImGuiCol.WindowBg),
ime.InputModeIcon);
}
}
}
imeIconFont.RenderChar(
drawList,
imeIconFont.FontSize,
cursor,
ImGui.GetColorU32(ImGuiCol.Text),
ime.InputModeIcon);
cursor.Y += candTextSize.Y + spaceY;
}
if (!expandUpward && drawConv)
{
DrawTextBeingConverted();
cursor.Y += candTextSize.Y + spaceY;
// Add a separator.
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
}
if (drawCand)
{
// Add the candidate words.
for (var i = 0; i < ime.ImmCand.Count; i++)
{
var selected = i == (native.dwSelection % ImePageSize);
var color = ImGui.GetColorU32(ImGuiCol.Text);
if (selected)
color = ImGui.GetColorU32(ImGuiCol.NavHighlight);
drawList.AddText(cursor, color, $"{i + 1}. {ime.ImmCand[i]}");
cursor.Y += candTextSize.Y + spaceY;
}
// Add a separator
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
// Add the pages infomation.
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), pageInfo);
cursor.Y += candTextSize.Y + spaceY;
}
if (expandUpward && drawConv)
{
// Add a separator.
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
DrawTextBeingConverted();
cursor.Y += candTextSize.Y + spaceY;
}
if (expandUpward && drawIme)
{
for (var dx = -2; dx <= 2; dx++)
{
for (var dy = -2; dy <= 2; dy++)
{
if (dx != 0 || dy != 0)
{
imeIconFont.RenderChar(
drawList,
imeIconFont.FontSize,
cursor + new Vector2(dx, dy),
ImGui.GetColorU32(ImGuiCol.WindowBg),
ime.InputModeIcon);
}
}
}
imeIconFont.RenderChar(
drawList,
imeIconFont.FontSize,
cursor,
ImGui.GetColorU32(ImGuiCol.Text),
ime.InputModeIcon);
}
return;
void DrawTextBeingConverted()
{
// Draw the text background.
drawList.AddRectFilled(
cursor - (pad / 2),
cursor + candTextSize + (pad / 2),
ImGui.GetColorU32(ImGuiCol.WindowBg));
// If only a part of the full text is marked for conversion, then draw background for the part being edited.
if (ime.PartialConversionFrom != 0 || ime.PartialConversionTo != ime.ImmComp.Length)
{
var part1 = ime.ImmComp[..ime.PartialConversionFrom];
var part2 = ime.ImmComp[..ime.PartialConversionTo];
var size1 = ImGui.CalcTextSize(part1);
var size2 = ImGui.CalcTextSize(part2);
drawList.AddRectFilled(
cursor + size1 with { Y = 0 },
cursor + size2,
ImGui.GetColorU32(ImGuiCol.TextSelectedBg));
}
// Add the text being converted.
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), ime.ImmComp);
// Draw the caret inside the composition string.
if (DalamudIme.ShowCursorInInputText)
{
var partBeforeCaret = ime.ImmComp[..ime.CompositionCursorOffset];
var sizeBeforeCaret = ImGui.CalcTextSize(partBeforeCaret);
drawList.AddLine(
cursor + sizeBeforeCaret with { Y = 0 },
cursor + sizeBeforeCaret,
ImGui.GetColorU32(ImGuiCol.Text));
}
}
}
}

View file

@ -165,6 +165,7 @@ internal static class ServiceManager
var earlyLoadingServices = new HashSet<Type>();
var blockingEarlyLoadingServices = new HashSet<Type>();
var providedServices = new HashSet<Type>();
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
var getAsyncTaskMap = new Dictionary<Type, Task>();
@ -197,7 +198,10 @@ internal static class ServiceManager
// We don't actually need to load provided services, something else does
if (serviceKind.HasFlag(ServiceKind.ProvidedService))
{
providedServices.Add(serviceType);
continue;
}
Debug.Assert(
serviceKind.HasFlag(ServiceKind.EarlyLoadedService) ||
@ -340,7 +344,16 @@ internal static class ServiceManager
}
if (!tasks.Any())
throw new InvalidOperationException("Unresolvable dependency cycle detected");
{
// No more services we can start loading for now.
// Either we're waiting for provided services, or there's a dependency cycle.
providedServices.RemoveWhere(x => getAsyncTaskMap[x].IsCompleted);
if (providedServices.Any())
await Task.WhenAny(providedServices.Select(x => getAsyncTaskMap[x]));
else
throw new InvalidOperationException("Unresolvable dependency cycle detected");
continue;
}
if (servicesToLoad.Any())
{