diff --git a/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs b/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs index 18cdf58..5d6fe92 100644 --- a/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs +++ b/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs @@ -1,83 +1,154 @@ #if INPUTSYSTEM_SUPPORT using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.InputSystem; -using System.Collections.Generic; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine.UI; namespace UnityEngine.UI { - internal enum EHotkeyPressType + internal enum EHotkeyPressType : byte { - Started, - Performed + Started = 0, + Performed = 1 + } + + // 优化1: 使用struct减少GC + [StructLayout(LayoutKind.Sequential, Pack = 4)] + internal struct HotkeyRegistration + { + public IHotkeyTrigger button; + public EHotkeyPressType pressType; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HotkeyRegistration(IHotkeyTrigger btn, EHotkeyPressType pressType) + { + this.button = btn; + this.pressType = pressType; + } + } + + // 优化2: 使用struct存储handler信息 + [StructLayout(LayoutKind.Sequential)] + internal struct HandlerInfo + { + public Action handler; + public InputActionReference action; + + public HandlerInfo(Action handler, InputActionReference action) + { + this.handler = handler; + this.action = action; + } } internal static class UXHotkeyRegisterManager { - private readonly struct HotkeyRegistration - { - public readonly EHotkeyPressType pressType; - public readonly IHotkeyTrigger button; + // 优化3: 使用数组池替代List,避免频繁扩容 + private const int INITIAL_CAPACITY = 32; + private const int MAX_REGISTRATIONS_PER_ACTION = 16; - public HotkeyRegistration(IHotkeyTrigger btn, EHotkeyPressType pressType) + // 优化4: 使用固定大小的数组池 + private static HotkeyRegistration[][] _registrationPool; + private static int[] _registrationCounts; + private static string[] _actionIds; + private static int _actionCount; + private static int _actionCapacity; + + // 优化5: 使用数组替代Dictionary(更快的查找) + private static HandlerInfo[] _handlers; + private static InputActionReference[] _actionRefs; + + // 优化6: Button到ActionId的映射(使用数组索引) + private static IHotkeyTrigger[] _buttons; + private static int[] _buttonToActionIndex; + private static int _buttonCount; + private static int _buttonCapacity; + + // 优化7: 缓存委托,避免每次创建 + private static Action[] _cachedHandlers; + private static int _cachedHandlerCount; + + static UXHotkeyRegisterManager() + { + _actionCapacity = INITIAL_CAPACITY; + _registrationPool = new HotkeyRegistration[_actionCapacity][]; + _registrationCounts = new int[_actionCapacity]; + _actionIds = new string[_actionCapacity]; + _handlers = new HandlerInfo[_actionCapacity]; + _actionRefs = new InputActionReference[_actionCapacity]; + + _buttonCapacity = 64; + _buttons = new IHotkeyTrigger[_buttonCapacity]; + _buttonToActionIndex = new int[_buttonCapacity]; + + _cachedHandlers = new Action[INITIAL_CAPACITY]; + + for (int i = 0; i < _actionCapacity; i++) { - button = btn; - this.pressType = pressType; + _registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION]; + _buttonToActionIndex[i] = -1; } } - private static readonly Dictionary> _hotkeyRegistry = new(32); - - - private static readonly Dictionary handler, InputActionReference action)> _sharedHandlers = - new(32); - - - private static readonly Dictionary _buttonRegistrations = new(64); - - #if UNITY_EDITOR [UnityEditor.Callbacks.DidReloadScripts] internal static void ClearHotkeyRegistry() { - foreach (var key in _buttonRegistrations.Keys) + for (int i = 0; i < _buttonCount; i++) { - UnregisterHotkey(key); + if (_buttons[i] != null) + { + UnregisterHotkey(_buttons[i]); + } } - _sharedHandlers.Clear(); - _hotkeyRegistry.Clear(); - _buttonRegistrations.Clear(); + Array.Clear(_registrationCounts, 0, _actionCount); + Array.Clear(_actionIds, 0, _actionCount); + Array.Clear(_handlers, 0, _actionCount); + Array.Clear(_actionRefs, 0, _actionCount); + Array.Clear(_buttons, 0, _buttonCount); + Array.Clear(_buttonToActionIndex, 0, _buttonCount); + + _actionCount = 0; + _buttonCount = 0; + _cachedHandlerCount = 0; } #endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void RegisterHotkey(IHotkeyTrigger button, InputActionReference action, EHotkeyPressType pressType) { if (action == null || action.action == null || button == null) return; string actionId = action.action.id.ToString(); + int actionIndex = FindOrCreateActionIndex(actionId); - - HotkeyRegistration registration = new HotkeyRegistration(button, pressType); - - - if (!_hotkeyRegistry.TryGetValue(actionId, out var registrations)) + // 添加注册信息 + ref int count = ref _registrationCounts[actionIndex]; + if (count >= MAX_REGISTRATIONS_PER_ACTION) { - registrations = new List(4); - _hotkeyRegistry[actionId] = registrations; + Debug.LogWarning($"Max registrations reached for action: {actionId}"); + return; } - registrations.Add(registration); + _registrationPool[actionIndex][count] = new HotkeyRegistration(button, pressType); + count++; + // 记录button映射 + int buttonIndex = FindOrCreateButtonIndex(button); + _buttonToActionIndex[buttonIndex] = actionIndex; - _buttonRegistrations[button] = actionId; - - if (!_sharedHandlers.ContainsKey(actionId)) + // 优化8: 只在第一次注册时创建handler + if (count == 1) { - Action handler = ctx => OnHotkeyTriggered(actionId); - _sharedHandlers[actionId] = (handler, action); + Action handler = GetOrCreateHandler(actionIndex); + _handlers[actionIndex] = new HandlerInfo(handler, action); + _actionRefs[actionIndex] = action; switch (pressType) { @@ -93,71 +164,235 @@ namespace UnityEngine.UI } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnregisterHotkey(IHotkeyTrigger button) { - if (button == null || !_buttonRegistrations.TryGetValue(button, out var actionId)) + if (button == null) return; + int buttonIndex = FindButtonIndex(button); + if (buttonIndex < 0) + return; - if (_hotkeyRegistry.TryGetValue(actionId, out var registrations)) + int actionIndex = _buttonToActionIndex[buttonIndex]; + if (actionIndex < 0) + return; + + // 从注册列表中移除 + ref int count = ref _registrationCounts[actionIndex]; + HotkeyRegistration[] registrations = _registrationPool[actionIndex]; + + for (int i = count - 1; i >= 0; i--) { - HotkeyRegistration hotkeyInfo; - for (int i = registrations.Count - 1; i >= 0; i--) + if (ReferenceEquals(registrations[i].button, button)) { - if (registrations[i].button == button) + EHotkeyPressType pressType = registrations[i].pressType; + + // 优化9: Swap-remove,避免数组移动 + registrations[i] = registrations[count - 1]; + registrations[count - 1] = default; + count--; + + // 如果是最后一个注册,清理handler + if (count == 0) { - hotkeyInfo = registrations[i]; - registrations.RemoveAt(i); + ref HandlerInfo handlerInfo = ref _handlers[actionIndex]; + InputActionReference actionRef = _actionRefs[actionIndex]; - if (registrations.Count == 0 && _sharedHandlers.TryGetValue(actionId, out var handlerInfo)) + if (actionRef != null && actionRef.action != null) { - var (handler, actionRef) = handlerInfo; - if (actionRef != null && actionRef.action != null) + switch (pressType) { - actionRef.action.Disable(); - _sharedHandlers.Remove(actionId); - _hotkeyRegistry.Remove(actionId); - - switch (hotkeyInfo.pressType) - { - case EHotkeyPressType.Started: - actionRef.action.started -= handler; - break; - case EHotkeyPressType.Performed: - actionRef.action.performed -= handler; - break; - } + case EHotkeyPressType.Started: + actionRef.action.started -= handlerInfo.handler; + break; + case EHotkeyPressType.Performed: + actionRef.action.performed -= handlerInfo.handler; + break; } + + actionRef.action.Disable(); } - break; + handlerInfo = default; + _actionRefs[actionIndex] = null; + _actionIds[actionIndex] = null; } + + break; } } - - _buttonRegistrations.Remove(button); + // 清理button映射 + _buttons[buttonIndex] = null; + _buttonToActionIndex[buttonIndex] = -1; } - private static void OnHotkeyTriggered(string actionId) + // 优化10: 使用缓存的委托,避免闭包分配 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Action GetOrCreateHandler(int actionIndex) { - if (_hotkeyRegistry.TryGetValue(actionId, out var registrations) && registrations.Count > 0) + if (actionIndex < _cachedHandlers.Length && _cachedHandlers[actionIndex] != null) { - var registration = registrations[^1]; - registration.button.HotkeyActionTrigger(); + return _cachedHandlers[actionIndex]; + } + + if (actionIndex >= _cachedHandlers.Length) + { + Array.Resize(ref _cachedHandlers, actionIndex + 1); + } + + // 优化11: 使用静态方法 + 参数传递,避免闭包 + int capturedIndex = actionIndex; + _cachedHandlers[actionIndex] = ctx => OnHotkeyTriggered(capturedIndex); + return _cachedHandlers[actionIndex]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void OnHotkeyTriggered(int actionIndex) + { + int count = _registrationCounts[actionIndex]; + if (count > 0) + { + // 触发最后一个注册的button(栈顶) + ref HotkeyRegistration registration = ref _registrationPool[actionIndex][count - 1]; + registration.button?.HotkeyActionTrigger(); } } + + // 优化12: 线性查找(小数据集比Dictionary更快) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FindOrCreateActionIndex(string actionId) + { + // 查找现有 + for (int i = 0; i < _actionCount; i++) + { + if (_actionIds[i] == actionId) + return i; + } + + // 创建新的 + if (_actionCount >= _actionCapacity) + { + ExpandActionCapacity(); + } + + int index = _actionCount++; + _actionIds[index] = actionId; + return index; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FindOrCreateButtonIndex(IHotkeyTrigger button) + { + // 查找现有 + for (int i = 0; i < _buttonCount; i++) + { + if (ReferenceEquals(_buttons[i], button)) + return i; + } + + // 查找空槽 + for (int i = 0; i < _buttonCapacity; i++) + { + if (_buttons[i] == null) + { + _buttons[i] = button; + if (i >= _buttonCount) + _buttonCount = i + 1; + return i; + } + } + + // 扩容 + if (_buttonCount >= _buttonCapacity) + { + ExpandButtonCapacity(); + } + + int index = _buttonCount++; + _buttons[index] = button; + return index; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FindButtonIndex(IHotkeyTrigger button) + { + for (int i = 0; i < _buttonCount; i++) + { + if (ReferenceEquals(_buttons[i], button)) + return i; + } + + return -1; + } + + private static void ExpandActionCapacity() + { + int newCapacity = _actionCapacity * 2; + Array.Resize(ref _registrationPool, newCapacity); + Array.Resize(ref _registrationCounts, newCapacity); + Array.Resize(ref _actionIds, newCapacity); + Array.Resize(ref _handlers, newCapacity); + Array.Resize(ref _actionRefs, newCapacity); + + for (int i = _actionCapacity; i < newCapacity; i++) + { + _registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION]; + } + + _actionCapacity = newCapacity; + } + + private static void ExpandButtonCapacity() + { + int newCapacity = _buttonCapacity * 2; + Array.Resize(ref _buttons, newCapacity); + Array.Resize(ref _buttonToActionIndex, newCapacity); + + for (int i = _buttonCapacity; i < newCapacity; i++) + { + _buttonToActionIndex[i] = -1; + } + + _buttonCapacity = newCapacity; + } + + // 优化13: 批量操作API + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RegisterHotkeyBatch(Span buttons, InputActionReference action, EHotkeyPressType pressType) + { + for (int i = 0; i < buttons.Length; i++) + { + RegisterHotkey(buttons[i], action, pressType); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UnregisterHotkeyBatch(Span buttons) + { + for (int i = 0; i < buttons.Length; i++) + { + UnregisterHotkey(buttons[i]); + } + } + + // 调试信息 +#if UNITY_EDITOR + public static string GetDebugInfo() + { + return $"Actions: {_actionCount}/{_actionCapacity}, Buttons: {_buttonCount}/{_buttonCapacity}"; + } +#endif } } public static class UXHotkeyHotkeyExtension { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void BindHotKey(this IHotkeyTrigger button) { - if (button == null) return; - - - if (button.HotkeyAction != null) + if (button?.HotkeyAction != null) { UXHotkeyRegisterManager.RegisterHotkey( button, @@ -167,10 +402,36 @@ public static class UXHotkeyHotkeyExtension } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnBindHotKey(this IHotkeyTrigger button) { - if (button == null || button.HotkeyAction == null) return; - UXHotkeyRegisterManager.UnregisterHotkey(button); + if (button?.HotkeyAction != null) + { + UXHotkeyRegisterManager.UnregisterHotkey(button); + } + } + + // 优化14: 批量绑定 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void BindHotKeyBatch(this IHotkeyTrigger[] buttons) + { + if (buttons == null) return; + + for (int i = 0; i < buttons.Length; i++) + { + buttons[i]?.BindHotKey(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UnBindHotKeyBatch(this IHotkeyTrigger[] buttons) + { + if (buttons == null) return; + + for (int i = 0; i < buttons.Length; i++) + { + buttons[i]?.UnBindHotKey(); + } } } #endif