diff --git a/Runtime/Timer/TimerModule.cs b/Runtime/Timer/TimerModule.cs index 6e4d509..c23939c 100644 --- a/Runtime/Timer/TimerModule.cs +++ b/Runtime/Timer/TimerModule.cs @@ -8,6 +8,18 @@ namespace AlicizaX { public delegate void TimerHandler(params object[] args); public delegate void TimerHandlerNoArgs(); + internal delegate void TimerGenericInvoker(Delegate handler, object arg); + + internal static class TimerGenericInvokerCache + { + public static readonly TimerGenericInvoker Invoke = InvokeGeneric; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void InvokeGeneric(Delegate handler, object arg) + { + ((Action)handler)?.Invoke((T)arg); + } + } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] @@ -21,6 +33,7 @@ namespace AlicizaX public TimerHandler Handler; public TimerHandlerNoArgs HandlerNoArgs; public Delegate HandlerGeneric; + public TimerGenericInvoker GenericInvoker; public bool IsLoop; public bool IsRunning; @@ -40,11 +53,19 @@ namespace AlicizaX [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { + TimerId = 0; + TriggerTime = 0f; + Interval = 0f; Handler = null; HandlerNoArgs = null; HandlerGeneric = null; + GenericInvoker = null; + IsLoop = false; + IsRunning = false; + IsUnscaled = false; Args = null; GenericArg = null; + HandlerType = 0; IsActive = false; Level = -1; SlotIndex = -1; @@ -58,6 +79,8 @@ namespace AlicizaX [Il2CppSetOption(Option.ArrayBoundsChecks, false)] internal sealed class TimerModule : ITimerModule { + private const float TimeWheelSlotInterval = 0.001f; + private int _curTimerId; private HierarchicalTimeWheel _scaledTimeWheel; private HierarchicalTimeWheel _unscaledTimeWheel; @@ -76,10 +99,14 @@ namespace AlicizaX private const int SLOT_COUNT_LEVEL0 = 256; private const int SLOT_COUNT_LEVEL1 = 64; private const int SLOT_COUNT_LEVEL2 = 64; + private const int SLOT_MASK_LEVEL0 = SLOT_COUNT_LEVEL0 - 1; + private const int SLOT_MASK_LEVEL1 = SLOT_COUNT_LEVEL1 - 1; + private const int SLOT_MASK_LEVEL2 = SLOT_COUNT_LEVEL2 - 1; private readonly float _slotInterval; private readonly TimeWheelLevel[] _levels; private float _currentTime; + private bool _isInitialized; private class TimeWheelLevel { @@ -108,6 +135,8 @@ namespace AlicizaX [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddTimer(int poolIndex, TimerInfo[] pool, float currentTime) { + EnsureInitialized(currentTime); + ref TimerInfo timer = ref pool[poolIndex]; if (!timer.IsActive) return; @@ -121,19 +150,19 @@ namespace AlicizaX if (totalSlots < SLOT_COUNT_LEVEL0) { level = 0; - slotIndex = (_levels[0].CurrentSlot + totalSlots) % SLOT_COUNT_LEVEL0; + slotIndex = (_levels[0].CurrentSlot + totalSlots) & SLOT_MASK_LEVEL0; } else if (totalSlots < SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1) { level = 1; int level1Slots = totalSlots / SLOT_COUNT_LEVEL0; - slotIndex = (_levels[1].CurrentSlot + level1Slots) % SLOT_COUNT_LEVEL1; + slotIndex = (_levels[1].CurrentSlot + level1Slots) & SLOT_MASK_LEVEL1; } else { level = 2; int level2Slots = totalSlots / (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1); - slotIndex = (_levels[2].CurrentSlot + level2Slots) % SLOT_COUNT_LEVEL2; + slotIndex = (_levels[2].CurrentSlot + level2Slots) & SLOT_MASK_LEVEL2; } timer.Level = level; @@ -152,6 +181,8 @@ namespace AlicizaX public void Advance(float currentTime, TimerInfo[] pool, Action processTimer) { + EnsureInitialized(currentTime); + float timeDelta = currentTime - _currentTime; if (timeDelta <= 0) return; @@ -162,23 +193,45 @@ namespace AlicizaX _currentTime += _slotInterval; ProcessLevel(0, pool, processTimer); - if ((_levels[0].CurrentSlot % SLOT_COUNT_LEVEL0) == 0) + if (_levels[0].CurrentSlot == 0) { ProcessLevel(1, pool, processTimer); - } - - if ((_levels[0].CurrentSlot % (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)) == 0) - { - ProcessLevel(2, pool, processTimer); + if (_levels[1].CurrentSlot == 0) + { + ProcessLevel(2, pool, processTimer); + } } } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized(float currentTime) + { + if (_isInitialized) + { + return; + } + + _currentTime = currentTime; + _isInitialized = true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessLevel(int level, TimerInfo[] pool, Action processTimer) { TimeWheelLevel wheelLevel = _levels[level]; - wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) % wheelLevel.SlotCount; + if (level == 0) + { + wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) & SLOT_MASK_LEVEL0; + } + else if (level == 1) + { + wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) & SLOT_MASK_LEVEL1; + } + else + { + wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) & SLOT_MASK_LEVEL2; + } int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot]; wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1; @@ -263,6 +316,8 @@ namespace AlicizaX _pendingRemoveTimers = new int[64]; _pendingRemoveCount = 0; + InitializeTimerIdMapping(_timerIdToPoolIndex, 0); + for (int i = 0; i < _timerPoolCapacity; i++) { _freeIndices[i] = i; @@ -272,8 +327,8 @@ namespace AlicizaX _timerPool[i].SlotIndex = -1; } - _scaledTimeWheel = new HierarchicalTimeWheel(0.001f); - _unscaledTimeWheel = new HierarchicalTimeWheel(0.001f); + _scaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval); + _unscaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -285,7 +340,10 @@ namespace AlicizaX timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; + timer.Handler = null; timer.HandlerNoArgs = callback; + timer.HandlerGeneric = null; + timer.GenericInvoker = null; timer.HandlerType = 1; timer.IsLoop = isLoop; timer.IsRunning = true; @@ -311,7 +369,10 @@ namespace AlicizaX timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; + timer.Handler = null; + timer.HandlerNoArgs = null; timer.HandlerGeneric = callback; + timer.GenericInvoker = TimerGenericInvokerCache.Invoke; timer.GenericArg = arg; timer.HandlerType = 2; timer.IsLoop = isLoop; @@ -337,6 +398,9 @@ namespace AlicizaX timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; timer.Handler = callback; + timer.HandlerNoArgs = null; + timer.HandlerGeneric = null; + timer.GenericInvoker = null; timer.HandlerType = 0; timer.IsLoop = isLoop; timer.IsRunning = true; @@ -383,7 +447,15 @@ namespace AlicizaX { if (timerId >= _timerIdToPoolIndex.Length) { - Array.Resize(ref _timerIdToPoolIndex, _timerIdToPoolIndex.Length * 2); + int oldLength = _timerIdToPoolIndex.Length; + int newLength = oldLength; + while (timerId >= newLength) + { + newLength *= 2; + } + + Array.Resize(ref _timerIdToPoolIndex, newLength); + InitializeTimerIdMapping(_timerIdToPoolIndex, oldLength); } _timerIdToPoolIndex[timerId] = poolIndex; @@ -392,7 +464,28 @@ namespace AlicizaX [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetPoolIndex(int timerId) { - return timerId > 0 && timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1; + if (timerId <= 0 || timerId >= _timerIdToPoolIndex.Length) + { + return -1; + } + + int poolIndex = _timerIdToPoolIndex[timerId]; + if ((uint)poolIndex >= (uint)_timerPool.Length) + { + return -1; + } + + ref TimerInfo timer = ref _timerPool[poolIndex]; + return timer.IsActive && timer.TimerId == timerId ? poolIndex : -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void InitializeTimerIdMapping(int[] mapping, int startIndex) + { + for (int i = startIndex; i < mapping.Length; i++) + { + mapping[i] = -1; + } } public void Stop(int timerId) @@ -465,10 +558,20 @@ namespace AlicizaX { if (_timerPool[i].IsActive) { + int timerId = _timerPool[i].TimerId; + if (timerId > 0 && timerId < _timerIdToPoolIndex.Length) + { + _timerIdToPoolIndex[timerId] = -1; + } + _timerPool[i].Clear(); _freeIndices[_freeCount++] = i; } } + + _pendingRemoveCount = 0; + _scaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval); + _unscaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -476,6 +579,12 @@ namespace AlicizaX { if (poolIndex >= 0 && poolIndex < _timerPool.Length) { + int timerId = _timerPool[poolIndex].TimerId; + if (timerId > 0 && timerId < _timerIdToPoolIndex.Length && _timerIdToPoolIndex[timerId] == poolIndex) + { + _timerIdToPoolIndex[timerId] = -1; + } + _timerPool[poolIndex].Clear(); if (_freeCount >= _freeIndices.Length) @@ -516,7 +625,7 @@ namespace AlicizaX timer.HandlerNoArgs?.Invoke(); break; case 2: // Generic - ((dynamic)timer.HandlerGeneric)?.Invoke((dynamic)timer.GenericArg); + timer.GenericInvoker?.Invoke(timer.HandlerGeneric, timer.GenericArg); break; } } diff --git a/Runtime/UI/Manager/UIModule.Block.cs b/Runtime/UI/Manager/UIModule.Block.cs index 1f5c03d..95be9b1 100644 --- a/Runtime/UI/Manager/UIModule.Block.cs +++ b/Runtime/UI/Manager/UIModule.Block.cs @@ -49,7 +49,7 @@ namespace AlicizaX.UI.Runtime RecoverLayerOptionAll(); } - private void OnBlockCountDown(object[] args) + private void OnBlockCountDown() { RecoverLayerOptionAll(); } diff --git a/Runtime/UI/Manager/UIModule.Cache.cs b/Runtime/UI/Manager/UIModule.Cache.cs index 7adf841..9eb7bb0 100644 --- a/Runtime/UI/Manager/UIModule.Cache.cs +++ b/Runtime/UI/Manager/UIModule.Cache.cs @@ -42,10 +42,10 @@ namespace AlicizaX.UI.Runtime { timerId = _timerModule.AddTimer( OnTimerDisposeWindow, + uiMetadata, uiMetadata.MetaInfo.CacheTime, isLoop: false, - isUnscaled: true, - uiMetadata + isUnscaled: true ); if (timerId <= 0) @@ -58,11 +58,8 @@ namespace AlicizaX.UI.Runtime m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, new CacheEntry(uiMetadata, timerId)); } - private void OnTimerDisposeWindow(object[] args) + private void OnTimerDisposeWindow(UIMetadata meta) { - if (args == null || args.Length == 0) return; - - UIMetadata meta = args[0] as UIMetadata; if (meta != null) { meta.Dispose(); @@ -70,7 +67,7 @@ namespace AlicizaX.UI.Runtime } } - private void RemoveFromCache(RuntimeTypeHandle typeHandle) + private void RemoveFromCache(RuntimeTypeHandle typeHandle) { if (m_CacheWindow.TryGetValue(typeHandle, out var entry)) {