diff --git a/Runtime/UI/Constant/UIMetaRegistry.cs b/Runtime/UI/Constant/UIMetaRegistry.cs
index 4a9c85b..d3fcd82 100644
--- a/Runtime/UI/Constant/UIMetaRegistry.cs
+++ b/Runtime/UI/Constant/UIMetaRegistry.cs
@@ -81,10 +81,6 @@ namespace AlicizaX.UI.Runtime
return TryReflectAndRegisterInternal(uiType, out info);
}
- ///
- /// Internal method to reflect and register UI type without logging.
- /// Used by both runtime fallback and pre-registration.
- ///
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool TryReflectAndRegisterInternal(Type uiType, out UIMetaInfo info)
{
@@ -93,7 +89,6 @@ namespace AlicizaX.UI.Runtime
Type baseType = uiType;
Type? holderType = null;
- // Get holder type from generic arguments
var genericArgs = baseType.GetGenericArguments();
if (genericArgs.Length > 0)
{
@@ -105,7 +100,6 @@ namespace AlicizaX.UI.Runtime
int cacheTime = 0;
bool needUpdate = false;
- // Read attributes
var windowAttribute = CustomAttributeData.GetCustomAttributes(uiType)
.FirstOrDefault(a => a.AttributeType.Name == nameof(WindowAttribute));
var uiUpdateAttribute = CustomAttributeData.GetCustomAttributes(uiType)
diff --git a/Runtime/UI/Constant/UIMetadata.cs b/Runtime/UI/Constant/UIMetadata.cs
index 0879e04..a408447 100644
--- a/Runtime/UI/Constant/UIMetadata.cs
+++ b/Runtime/UI/Constant/UIMetadata.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.CompilerServices;
+using System.Threading;
using AlicizaX;
using Cysharp.Threading.Tasks;
@@ -13,6 +14,9 @@ namespace AlicizaX.UI.Runtime
public readonly Type UILogicType;
public bool InCache = false;
+ private CancellationTokenSource _cancellationTokenSource;
+ public CancellationToken CancellationToken => _cancellationTokenSource?.Token ?? CancellationToken.None;
+
public UIState State
{
get
@@ -31,9 +35,17 @@ namespace AlicizaX.UI.Runtime
return;
View = (UIBase)InstanceFactory.CreateInstanceOptimized(UILogicType);
+ _cancellationTokenSource = new CancellationTokenSource();
}
}
+ public void CancelAsyncOperations()
+ {
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource?.Dispose();
+ _cancellationTokenSource = null;
+ }
+
public void Dispose()
{
DisposeAsync().Forget();
@@ -41,6 +53,8 @@ namespace AlicizaX.UI.Runtime
private async UniTask DisposeAsync()
{
+ CancelAsyncOperations();
+
if (State != UIState.Uninitialized && State != UIState.Destroying)
{
await View.InternalDestroy();
diff --git a/Runtime/UI/Constant/UIMetadataObject.cs b/Runtime/UI/Constant/UIMetadataObject.cs
index d7b89d6..e39f14c 100644
--- a/Runtime/UI/Constant/UIMetadataObject.cs
+++ b/Runtime/UI/Constant/UIMetadataObject.cs
@@ -15,7 +15,6 @@ namespace AlicizaX.UI.Runtime
public static UIMetadataObject Create(UIMetadata target, RuntimeTypeHandle handle)
{
UIMetadataObject obj = MemoryPool.Acquire();
- // Use type handle hash code as name to avoid string allocation
obj.Initialize(handle.GetHashCode().ToString(), target);
return obj;
}
diff --git a/Runtime/UI/Manager/UIModule.Cache.cs b/Runtime/UI/Manager/UIModule.Cache.cs
index 62fcbdb..7adf841 100644
--- a/Runtime/UI/Manager/UIModule.Cache.cs
+++ b/Runtime/UI/Manager/UIModule.Cache.cs
@@ -6,7 +6,19 @@ namespace AlicizaX.UI.Runtime
{
internal sealed partial class UIModule
{
- private readonly Dictionary m_CacheWindow = new();
+ private readonly struct CacheEntry
+ {
+ public readonly UIMetadata Metadata;
+ public readonly int TimerId;
+
+ public CacheEntry(UIMetadata metadata, int timerId)
+ {
+ Metadata = metadata;
+ TimerId = timerId;
+ }
+ }
+
+ private readonly Dictionary m_CacheWindow = new();
private void CacheWindow(UIMetadata uiMetadata, bool force)
{
@@ -43,7 +55,7 @@ namespace AlicizaX.UI.Runtime
}
uiMetadata.InCache = true;
- m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, (uiMetadata, timerId));
+ m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, new CacheEntry(uiMetadata, timerId));
}
private void OnTimerDisposeWindow(object[] args)
@@ -60,13 +72,13 @@ namespace AlicizaX.UI.Runtime
private void RemoveFromCache(RuntimeTypeHandle typeHandle)
{
- if (m_CacheWindow.TryGetValue(typeHandle, out var result))
+ if (m_CacheWindow.TryGetValue(typeHandle, out var entry))
{
m_CacheWindow.Remove(typeHandle);
- result.Item1.InCache = false;
- if (result.Item2 > 0)
+ entry.Metadata.InCache = false;
+ if (entry.TimerId > 0)
{
- _timerModule.RemoveTimer(result.Item2);
+ _timerModule.RemoveTimer(entry.TimerId);
}
}
}
diff --git a/Runtime/UI/Manager/UIModule.Open.cs b/Runtime/UI/Manager/UIModule.Open.cs
index fcee69e..379fdbb 100644
--- a/Runtime/UI/Manager/UIModule.Open.cs
+++ b/Runtime/UI/Manager/UIModule.Open.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Threading;
using AlicizaX;
using Cysharp.Threading.Tasks;
@@ -29,7 +30,7 @@ namespace AlicizaX.UI.Runtime
CreateMetaUI(metaInfo);
await UIHolderFactory.CreateUIResourceAsync(metaInfo, UICacheLayer);
FinalizeShow(metaInfo, userDatas);
- UpdateVisualState(metaInfo).Forget();
+ await UpdateVisualState(metaInfo, metaInfo.CancellationToken);
return metaInfo.View;
}
@@ -49,6 +50,7 @@ namespace AlicizaX.UI.Runtime
return;
}
+ meta.CancelAsyncOperations();
await meta.View.InternalClose();
Pop(meta);
SortWindowVisible(meta.MetaInfo.UILayer);
@@ -167,16 +169,16 @@ namespace AlicizaX.UI.Runtime
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private async UniTask UpdateVisualState(UIMetadata meta)
+ private async UniTask UpdateVisualState(UIMetadata meta, CancellationToken cancellationToken = default)
{
SortWindowVisible(meta.MetaInfo.UILayer);
SortWindowDepth(meta.MetaInfo.UILayer);
if (meta.State == UIState.Loaded)
{
- await meta.View.InternalInitlized();
+ await meta.View.InternalInitlized(cancellationToken);
}
- await meta.View.InternalOpen();
+ await meta.View.InternalOpen(cancellationToken);
}
private void SortWindowVisible(int layer)
diff --git a/Runtime/UI/OPTIMIZATION_SUMMARY.md b/Runtime/UI/OPTIMIZATION_SUMMARY.md
deleted file mode 100644
index f5c36b2..0000000
--- a/Runtime/UI/OPTIMIZATION_SUMMARY.md
+++ /dev/null
@@ -1,275 +0,0 @@
-# UI Module Optimization - Complete Summary
-
-## Date: 2025-12-24
-
-## Executive Summary
-
-Successfully implemented comprehensive optimizations across 3 phases for the Aliciza X Unity UI module, resulting in significant performance improvements, reduced memory allocations, and better code quality.
-
----
-
-## Overall Performance Improvements
-
-### Memory Allocations Eliminated
-
-| Operation | Before | After | Savings |
-|-----------|--------|-------|---------|
-| **First UI Open** | 5-10ms reflection | 0ms | **5-10ms** |
-| **UI Open (GC)** | ~150 bytes | 0 bytes | **150 bytes** |
-| **Widget Update (per frame)** | 56 bytes | 0 bytes | **56 bytes** |
-| **Widget Creation** | 40 bytes | 0 bytes | **40 bytes** |
-| **Window Switch** | O(n) + 1-2ms | O(1) + 0ms | **1-2ms** |
-
-### Total Impact Per Session
-- **Startup:** One-time 10-30ms cost for pre-registration (acceptable trade-off)
-- **Per UI Open:** ~200 bytes GC eliminated
-- **Per Frame (with widgets):** ~100 bytes GC eliminated
-- **Window Operations:** 50-70% faster
-
----
-
-## Phase 1: Critical Optimizations ✅
-
-### 1. Pre-Register All UI Types at Startup
-- **Problem:** 5-10ms reflection overhead on first UI open
-- **Solution:** Automatic pre-registration at startup using `[RuntimeInitializeOnLoadMethod]`
-- **Impact:** Eliminate all runtime reflection overhead
-- **Files:** `UIMetaRegistry.cs`, `UIResRegistry.cs`
-
-### 2. Replace List.IndexOf with Index Tracking
-- **Problem:** O(n) linear search in `MoveToTop()`
-- **Solution:** Added `IndexMap` dictionary for O(1) lookups
-- **Impact:** 0.5-2ms faster window switching
-- **Files:** `UIModule.Open.cs`
-
-### 3. Fix Async State Machine Allocations
-- **Problem:** Unnecessary async state machine allocations
-- **Solution:** Return `UniTask.CompletedTask` instead of `await`
-- **Impact:** Eliminate ~150 bytes per UI open
-- **Files:** `UIBase.cs`
-
----
-
-## Phase 2: High Priority Optimizations ✅
-
-### 4. Widget Dictionary Enumeration
-- **Problem:** 40-56 bytes allocation per frame from dictionary enumeration
-- **Solution:** Cache updateable widgets in list, use struct enumerator
-- **Impact:** Zero allocation per frame
-- **Files:** `UIBase.Widget.cs`
-
-### 5. UIMetadataFactory String Allocations
-- **Problem:** String allocation on every widget creation
-- **Solution:** Use `RuntimeTypeHandle` as key instead of string
-- **Impact:** Eliminate 40+ bytes per widget
-- **Files:** `UIMetadataFactory.cs`, `UIMetadataObject.cs`
-
-### 6. SortWindowVisible Early Exit
-- **Problem:** No early exit when fullscreen found
-- **Solution:** Two-phase algorithm with early exit
-- **Impact:** Faster visibility sorting
-- **Files:** `UIModule.Open.cs`
-
-### 7. Cache Timer Management
-- **Problem:** Typos and poor error handling
-- **Solution:** Fixed typos, added validation, better error messages
-- **Impact:** Better code quality and reliability
-- **Files:** `UIModule.Cache.cs`
-
----
-
-## Phase 3: Medium Priority Optimizations ✅
-
-### 8. UI State Machine Validation
-- **Problem:** No validation of state transitions
-- **Solution:** Created `UIStateMachine` with full validation
-- **Impact:** Better debugging, prevent crashes
-- **Files:** `UIStateMachine.cs` (new), `UIBase.cs`, `UIMetadata.cs`
-
-### 9. Depth Sorting Optimization
-- **Problem:** Recalculate depth for all windows
-- **Solution:** Only update changed windows, check before setting
-- **Impact:** Fewer Canvas updates
-- **Files:** `UIModule.Open.cs`
-
-### 10. Remove Implicit Bool Operator
-- **Problem:** Confusing implicit operator overload
-- **Solution:** Explicit `IsValid()` method
-- **Impact:** Clearer code, safer
-- **Files:** `UIHolderObjectBase.cs`, `UIModule.Open.cs`
-
-### 11. State Check Improvements
-- **Problem:** Redundant state checks
-- **Solution:** Early returns with validation
-- **Impact:** Prevent duplicate calls
-- **Files:** `UIBase.cs`
-
----
-
-## Files Modified Summary
-
-### New Files Created (1)
-- `UIStateMachine.cs` - State transition validation
-
-### Files Modified (11)
-1. `UIMetaRegistry.cs` - Pre-registration
-2. `UIResRegistry.cs` - Pre-registration
-3. `UIModule.Open.cs` - Index tracking, visibility/depth optimization
-4. `UIBase.cs` - Async fixes, state validation
-5. `UIBase.Widget.cs` - Widget enumeration optimization
-6. `UIMetadataFactory.cs` - String allocation fix
-7. `UIMetadataObject.cs` - RuntimeTypeHandle support
-8. `UIModule.Cache.cs` - Timer management fixes
-9. `UIMetadata.cs` - State validation
-10. `UIHolderObjectBase.cs` - Remove implicit operator
-11. `UIModule.Initlize.cs` - (if needed for initialization)
-
-### Documentation Created (3)
-- `PHASE1_OPTIMIZATIONS.md`
-- `PHASE2_OPTIMIZATIONS.md`
-- `PHASE3_OPTIMIZATIONS.md`
-
----
-
-## Optimization Categories
-
-### Memory Optimizations
-- ✅ Eliminate reflection allocations (Phase 1)
-- ✅ Eliminate async state machine allocations (Phase 1)
-- ✅ Eliminate widget enumeration allocations (Phase 2)
-- ✅ Eliminate string allocations (Phase 2)
-
-### Performance Optimizations
-- ✅ O(n) → O(1) window lookup (Phase 1)
-- ✅ Early exit in visibility sorting (Phase 2)
-- ✅ Partial depth updates (Phase 3)
-- ✅ Avoid unnecessary Canvas updates (Phase 3)
-
-### Code Quality Improvements
-- ✅ State machine validation (Phase 3)
-- ✅ Fixed typos (Phase 2)
-- ✅ Better error handling (Phase 2, 3)
-- ✅ Clearer intent (Phase 3)
-
----
-
-## Testing Strategy
-
-### Unit Tests
-```csharp
-// State machine validation
-TestInvalidStateTransition()
-TestValidStateTransitions()
-
-// Performance tests
-TestUIOpenPerformance()
-TestWindowSwitchPerformance()
-TestWidgetUpdateAllocations()
-
-// Memory tests
-TestUIOpenAllocations()
-TestWidgetCreationAllocations()
-
-// Functionality tests
-TestDepthSortingOptimization()
-TestHolderIsValid()
-TestDuplicateOpenPrevention()
-```
-
-### Integration Tests
-- Open/close multiple windows
-- Switch between windows rapidly
-- Create/destroy many widgets
-- Test cache expiration
-- Test state transitions
-
-### Performance Profiling
-- Unity Profiler memory tracking
-- GC.Alloc monitoring
-- Frame time measurements
-- Startup time measurement
-
----
-
-## Backward Compatibility
-
-✅ **100% Backward Compatible**
-- No breaking changes to public API
-- Existing UI code requires no modifications
-- All optimizations are transparent to users
-- Fallback to runtime reflection if pre-registration fails
-
----
-
-## Known Limitations
-
-1. **Startup Time:** +10-30ms one-time cost for pre-registration
-2. **Memory Overhead:** ~32 bytes per layer for IndexMap
-3. **State Machine:** Small overhead for validation (negligible)
-4. **Assembly Scanning:** May fail on some platforms (graceful fallback)
-
----
-
-## Recommendations for Usage
-
-### Best Practices
-1. Let pre-registration run at startup (automatic)
-2. Use async UI loading for smooth experience
-3. Cache frequently used UI windows
-4. Use widgets for reusable components
-5. Monitor state transitions in debug builds
-
-### Performance Tips
-1. Minimize fullscreen windows (blocks lower windows)
-2. Use `[UIUpdate]` attribute only when needed
-3. Pool frequently created/destroyed UI
-4. Preload critical UI during loading screens
-5. Use appropriate cache times
-
----
-
-## Future Enhancement Opportunities
-
-### Phase 4 (Architectural)
-1. **UI Preloading System**
- - Preload critical UI during loading
- - Batch loading for better performance
- - Memory budget management
-
-2. **UI Pooling**
- - Pool frequently used windows (toasts, tooltips)
- - Reduce allocation for transient UI
- - Configurable pool sizes
-
-3. **Performance Metrics**
- - Track UI open/close times
- - Memory usage per UI
- - Frame time impact
- - Analytics integration
-
-4. **Advanced Optimizations**
- - Separate update loop for visible windows only
- - Batch Canvas updates
- - Lazy initialization for widgets
- - Virtual scrolling for lists
-
----
-
-## Conclusion
-
-The UI module optimizations have achieved:
-
-✅ **30-50% faster UI operations**
-✅ **50-70% less GC pressure**
-✅ **Better scalability** with many windows
-✅ **Improved debugging** with state validation
-✅ **Higher code quality** with fixes and clarity
-✅ **100% backward compatible**
-
-The optimizations are production-ready and will significantly improve user experience, especially on lower-end devices and when many UI windows are active.
-
----
-
-## Acknowledgments
-
-All optimizations maintain the original architecture's elegance while adding performance and reliability improvements. The module is now more robust, faster, and easier to debug.
diff --git a/Runtime/UI/OPTIMIZATION_SUMMARY.md.meta b/Runtime/UI/OPTIMIZATION_SUMMARY.md.meta
deleted file mode 100644
index be8b9be..0000000
--- a/Runtime/UI/OPTIMIZATION_SUMMARY.md.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: dcb06edbd88c426459ffbcce1ffc484f
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Runtime/UI/PHASE1_OPTIMIZATIONS.md b/Runtime/UI/PHASE1_OPTIMIZATIONS.md
deleted file mode 100644
index de08f90..0000000
--- a/Runtime/UI/PHASE1_OPTIMIZATIONS.md
+++ /dev/null
@@ -1,236 +0,0 @@
-# Phase 1 UI Module Optimizations - Implementation Summary
-
-## Date: 2025-12-24
-
-## Overview
-Successfully implemented Phase 1 critical optimizations for the UI module, targeting reflection overhead, lookup performance, and memory allocations.
-
----
-
-## ✅ Optimization 1: Pre-Register All UI Types at Startup
-
-### Problem
-- Reflection overhead of 5-10ms on first UI open
-- `CustomAttributeData.GetCustomAttributes()` called at runtime
-- `GetGenericArguments()` called for every unregistered UI type
-
-### Solution
-**Files Modified:**
-- `UIMetaRegistry.cs`
-- `UIResRegistry.cs`
-
-**Changes:**
-1. Added `PreRegisterAllUITypes()` method with `[RuntimeInitializeOnLoadMethod]` attribute
-2. Scans all assemblies at startup (skips system assemblies)
-3. Pre-caches all UIBase-derived types and their metadata
-4. Added `PreRegisterAllUIResources()` for UI holder resource paths
-5. Split reflection logic into `TryReflectAndRegisterInternal()` for reuse
-
-**Key Features:**
-- Runs automatically at `SubsystemRegistration` phase
-- Logs registration time and count
-- Graceful error handling for assembly load failures
-- One-time execution with `_isPreRegistered` flag
-
-**Expected Impact:**
-- ✅ Eliminate 5-10ms reflection overhead on first UI open
-- ✅ All UI types cached at startup
-- ✅ Predictable startup time (one-time cost)
-
----
-
-## ✅ Optimization 2: Replace List.IndexOf with Index Tracking
-
-### Problem
-- `List.IndexOf(meta)` is O(n) linear search
-- Called in `MoveToTop()` every time a window is re-opened
-- Performance degrades with more windows in a layer
-
-### Solution
-**File Modified:**
-- `UIModule.Open.cs`
-
-**Changes:**
-1. Added `IndexMap` dictionary to `LayerData` struct
-2. Updated `Push()` to track index when adding windows
-3. Updated `Pop()` to maintain indices after removal
-4. Replaced `IndexOf()` with O(1) dictionary lookup in `MoveToTop()`
-5. Update indices for shifted elements after removal/move
-
-**Code Changes:**
-```csharp
-// Before
-int currentIdx = layer.OrderList.IndexOf(meta); // O(n)
-
-// After
-if (!layer.IndexMap.TryGetValue(meta.MetaInfo.RuntimeTypeHandle, out int currentIdx)) // O(1)
- return;
-```
-
-**Expected Impact:**
-- ✅ O(n) → O(1) lookup performance
-- ✅ ~0.5-2ms saved per window switch
-- ✅ Scales better with more windows
-
----
-
-## ✅ Optimization 3: Fix Async State Machine Allocations
-
-### Problem
-- Virtual async methods allocate state machines even when not overridden
-- `await UniTask.CompletedTask` allocates 48-64 bytes per call
-- Called 3 times per UI open (Initialize, Open, Close)
-- Unnecessary GC pressure
-
-### Solution
-**File Modified:**
-- `UIBase.cs`
-
-**Changes:**
-1. Removed `async` keyword from virtual methods
-2. Changed return pattern from `await UniTask.CompletedTask` to `return UniTask.CompletedTask`
-3. Call synchronous methods first, then return completed task
-
-**Code Changes:**
-```csharp
-// Before
-protected virtual async UniTask OnInitializeAsync()
-{
- await UniTask.CompletedTask; // Allocates state machine
- OnInitialize();
-}
-
-// After
-protected virtual UniTask OnInitializeAsync()
-{
- OnInitialize();
- return UniTask.CompletedTask; // No allocation
-}
-```
-
-**Expected Impact:**
-- ✅ Eliminate ~100-200 bytes allocation per UI open
-- ✅ Reduce GC pressure
-- ✅ Faster execution (no state machine overhead)
-
----
-
-## Performance Improvements Summary
-
-| Metric | Before | After | Improvement |
-|--------|--------|-------|-------------|
-| First UI Open | 5-10ms reflection | 0ms (pre-cached) | **5-10ms faster** |
-| Window Switch | O(n) IndexOf | O(1) lookup | **0.5-2ms faster** |
-| GC per UI Open | ~150 bytes | ~0 bytes | **150 bytes saved** |
-| Startup Time | 0ms | +10-30ms (one-time) | Acceptable trade-off |
-
----
-
-## Testing Recommendations
-
-### 1. Startup Performance Test
-```csharp
-[Test]
-public void TestUIPreRegistration()
-{
- // Verify all UI types are registered at startup
- var registeredCount = UIMetaRegistry.GetRegisteredCount();
- Assert.Greater(registeredCount, 0);
-
- // Verify no reflection warnings in logs
- LogAssert.NoUnexpectedReceived();
-}
-```
-
-### 2. Window Switch Performance Test
-```csharp
-[Test]
-public async Task TestWindowSwitchPerformance()
-{
- await GameApp.UI.ShowUI();
- await GameApp.UI.ShowUI();
-
- var stopwatch = Stopwatch.StartNew();
- await GameApp.UI.ShowUI(); // Re-open (triggers MoveToTop)
- stopwatch.Stop();
-
- Assert.Less(stopwatch.ElapsedMilliseconds, 5); // Should be < 5ms
-}
-```
-
-### 3. Memory Allocation Test
-```csharp
-[Test]
-public async Task TestUIOpenAllocations()
-{
- GC.Collect();
- var beforeMemory = GC.GetTotalMemory(true);
-
- await GameApp.UI.ShowUI();
-
- var afterMemory = GC.GetTotalMemory(false);
- var allocated = afterMemory - beforeMemory;
-
- // Should allocate less than before (no async state machines)
- Assert.Less(allocated, 1000); // Adjust threshold as needed
-}
-```
-
-### 4. Index Map Consistency Test
-```csharp
-[Test]
-public async Task TestIndexMapConsistency()
-{
- // Open multiple windows
- await GameApp.UI.ShowUI();
- await GameApp.UI.ShowUI();
- await GameApp.UI.ShowUI();
-
- // Close middle window
- GameApp.UI.CloseUI();
-
- // Verify indices are correct
- // (Internal test - would need access to UIModule internals)
-}
-```
-
----
-
-## Verification Checklist
-
-- [x] Code compiles without errors
-- [x] No breaking changes to public API
-- [x] Backward compatible with existing UI code
-- [ ] Run Unity Editor and verify no errors in console
-- [ ] Test UI opening/closing in play mode
-- [ ] Profile memory allocations with Unity Profiler
-- [ ] Measure startup time increase (should be < 50ms)
-- [ ] Test with multiple UI windows open simultaneously
-- [ ] Verify UI switching performance improvement
-
----
-
-## Known Limitations
-
-1. **Startup Time Increase**: Pre-registration adds 10-30ms to startup (acceptable trade-off)
-2. **Memory Overhead**: IndexMap adds ~32 bytes per layer (negligible)
-3. **Assembly Scanning**: May fail on some platforms (graceful fallback to runtime reflection)
-
----
-
-## Next Steps (Phase 2)
-
-1. Optimize widget enumeration allocations
-2. Fix UIMetadataFactory string allocations
-3. Optimize SortWindowVisible with early exit
-4. Add state machine validation
-5. Implement UI preloading system
-
----
-
-## Notes
-
-- All changes maintain backward compatibility
-- Existing UI code requires no modifications
-- Optimizations are transparent to users
-- Fallback to runtime reflection if pre-registration fails
diff --git a/Runtime/UI/PHASE1_OPTIMIZATIONS.md.meta b/Runtime/UI/PHASE1_OPTIMIZATIONS.md.meta
deleted file mode 100644
index dc8369c..0000000
--- a/Runtime/UI/PHASE1_OPTIMIZATIONS.md.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: e65d5c1ad620ab945a8e79be86b53728
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Runtime/UI/PHASE2_OPTIMIZATIONS.md b/Runtime/UI/PHASE2_OPTIMIZATIONS.md
deleted file mode 100644
index 0d8205f..0000000
--- a/Runtime/UI/PHASE2_OPTIMIZATIONS.md
+++ /dev/null
@@ -1,434 +0,0 @@
-# Phase 2 UI Module Optimizations - Implementation Summary
-
-## Date: 2025-12-24
-
-## Overview
-Successfully implemented Phase 2 high-priority optimizations for the UI module, targeting widget enumeration, string allocations, visibility sorting, and cache management.
-
----
-
-## ✅ Optimization 1: Widget Dictionary Enumeration Allocations
-
-### Problem
-- `_children.Values` enumeration allocates 40-56 bytes per call
-- Called every frame for windows with widgets
-- `UpdateChildren()` and `ChildVisible()` both enumerate dictionary
-
-### Solution
-**File Modified:**
-- `UIBase.Widget.cs`
-
-**Changes:**
-1. Added `_updateableChildren` list to cache widgets that need updates
-2. Changed `UpdateChildren()` to iterate cached list instead of dictionary
-3. Changed `ChildVisible()` to use struct enumerator (`foreach (var kvp in _children)`)
-4. Updated `AddWidget()` to populate updateable list
-5. Updated `RemoveWidget()` to maintain updateable list
-6. Updated `DestroyAllChildren()` to clear both collections
-
-**Code Changes:**
-```csharp
-// Before
-private void UpdateChildren()
-{
- var values = _children.Values; // Allocates enumerator
- foreach (var meta in values)
- {
- if (meta.View.State == UIState.Opened && meta.MetaInfo.NeedUpdate)
- {
- meta.View.InternalUpdate();
- }
- }
-}
-
-// After
-private readonly List _updateableChildren = new();
-
-private void UpdateChildren()
-{
- // Use cached list - no allocation
- for (int i = 0; i < _updateableChildren.Count; i++)
- {
- var meta = _updateableChildren[i];
- if (meta.View.State == UIState.Opened)
- {
- meta.View.InternalUpdate();
- }
- }
-}
-```
-
-**Expected Impact:**
-- ✅ Eliminate 40-56 bytes allocation per frame per window with widgets
-- ✅ Faster iteration (direct list access vs dictionary enumeration)
-- ✅ Only iterate widgets that actually need updates
-
----
-
-## ✅ Optimization 2: UIMetadataFactory String Allocations
-
-### Problem
-- `type.FullName` allocates new string on every widget creation
-- String used as dictionary key in object pool
-- Unnecessary allocation for frequently created widgets
-
-### Solution
-**Files Modified:**
-- `UIMetadataFactory.cs`
-- `UIMetadataObject.cs`
-
-**Changes:**
-1. Replaced string-based pooling with `RuntimeTypeHandle`-based caching
-2. Added `WidgetMetadataCache` dictionary using `RuntimeTypeHandle` as key
-3. Changed `GetWidgetMetadata()` to accept `RuntimeTypeHandle` directly
-4. Added overload to `UIMetadataObject.Create()` accepting `RuntimeTypeHandle`
-5. Simplified `ReturnToPool()` (widgets are now cached, not pooled)
-
-**Code Changes:**
-```csharp
-// Before
-private static UIMetadata GetFromPool(Type type)
-{
- string typeHandleKey = type.FullName; // Allocates string
- UIMetadataObject metadataObj = m_UIMetadataPool.Spawn(typeHandleKey);
- // ...
-}
-
-// After
-private static readonly Dictionary WidgetMetadataCache = new();
-
-private static UIMetadata GetFromPool(RuntimeTypeHandle handle)
-{
- // Use RuntimeTypeHandle directly - no string allocation
- if (WidgetMetadataCache.TryGetValue(handle, out var metadataObj))
- {
- if (metadataObj != null && metadataObj.Target != null)
- {
- return (UIMetadata)metadataObj.Target;
- }
- }
- // ...
-}
-```
-
-**Expected Impact:**
-- ✅ Eliminate 40+ bytes string allocation per widget creation
-- ✅ Faster lookup (RuntimeTypeHandle comparison vs string comparison)
-- ✅ Better memory locality
-
----
-
-## ✅ Optimization 3: SortWindowVisible Early Exit
-
-### Problem
-- Original logic iterates all windows even after finding fullscreen
-- Unclear logic with boolean flag
-- No early exit optimization
-
-### Solution
-**File Modified:**
-- `UIModule.Open.cs`
-
-**Changes:**
-1. Split logic into two phases: find fullscreen, then set visibility
-2. Added early exit when fullscreen window found
-3. Clearer logic with explicit index tracking
-4. Separate fast paths for "no fullscreen" and "has fullscreen" cases
-
-**Code Changes:**
-```csharp
-// Before
-private void SortWindowVisible(int layer)
-{
- var list = _openUI[layer].OrderList;
- bool shouldHide = false;
-
- for (int i = list.Count - 1; i >= 0; i--)
- {
- var meta = list[i];
- meta.View.Visible = !shouldHide;
- shouldHide |= meta.MetaInfo.FullScreen && meta.State == UIState.Opened;
- }
-}
-
-// After
-private void SortWindowVisible(int layer)
-{
- var list = _openUI[layer].OrderList;
- int count = list.Count;
-
- // Find topmost fullscreen window (early exit)
- int fullscreenIdx = -1;
- for (int i = count - 1; i >= 0; i--)
- {
- var meta = list[i];
- if (meta.MetaInfo.FullScreen && meta.State == UIState.Opened)
- {
- fullscreenIdx = i;
- break; // Early exit
- }
- }
-
- // Set visibility based on fullscreen index
- if (fullscreenIdx == -1)
- {
- // Fast path: no fullscreen, all visible
- for (int i = 0; i < count; i++)
- {
- list[i].View.Visible = true;
- }
- }
- else
- {
- // Hide below fullscreen, show from fullscreen onwards
- for (int i = 0; i < count; i++)
- {
- list[i].View.Visible = (i >= fullscreenIdx);
- }
- }
-}
-```
-
-**Expected Impact:**
-- ✅ Early exit when fullscreen found (saves iterations)
-- ✅ Clearer, more maintainable code
-- ✅ Separate fast paths for common cases
-
----
-
-## ✅ Optimization 4: Cache Timer Management
-
-### Problem
-- Typo: `tiemrId` instead of `timerId`
-- Typo: `OnTimerDiposeWindow` instead of `OnTimerDisposeWindow`
-- No validation if timer creation fails
-- Poor error messages
-- No null checks on timer callback args
-
-### Solution
-**File Modified:**
-- `UIModule.Cache.cs`
-
-**Changes:**
-1. Fixed typo: `tiemrId` → `timerId`
-2. Fixed typo: `OnTimerDiposeWindow` → `OnTimerDisposeWindow`
-3. Added validation for timer creation failure
-4. Improved error messages with context
-5. Added null checks in timer callback
-6. Used named parameters for clarity
-7. Better null-conditional operator usage
-
-**Code Changes:**
-```csharp
-// Before
-int tiemrId = -1;
-tiemrId = _timerModule.AddTimer(OnTimerDiposeWindow, uiMetadata.MetaInfo.CacheTime, false, true, uiMetadata);
-
-private void OnTimerDiposeWindow(object[] args)
-{
- UIMetadata meta = args[0] as UIMetadata;
- meta?.Dispose();
- RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle);
-}
-
-// After
-int timerId = -1;
-timerId = _timerModule.AddTimer(
- OnTimerDisposeWindow,
- uiMetadata.MetaInfo.CacheTime,
- oneShot: true,
- ignoreTimeScale: true,
- uiMetadata
-);
-
-if (timerId <= 0)
-{
- Log.Warning($"Failed to create cache timer for {uiMetadata.UILogicType.Name}");
-}
-
-private void OnTimerDisposeWindow(object[] args)
-{
- if (args == null || args.Length == 0) return;
-
- UIMetadata meta = args[0] as UIMetadata;
- if (meta != null)
- {
- meta.Dispose();
- RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle);
- }
-}
-```
-
-**Expected Impact:**
-- ✅ Fixed typos (better code quality)
-- ✅ Better error detection and logging
-- ✅ Prevent null reference exceptions
-- ✅ More maintainable code
-
----
-
-## Performance Improvements Summary
-
-| Metric | Before | After | Improvement |
-|--------|--------|-------|-------------|
-| Widget Update (per frame) | 56 bytes | 0 bytes | **56 bytes saved** |
-| Widget Creation | 40 bytes | 0 bytes | **40 bytes saved** |
-| Visibility Sort | No early exit | Early exit | **Faster** |
-| Code Quality | Typos present | Fixed | **Better** |
-
-**Total Per-Frame Savings (per window with widgets):**
-- **~100 bytes GC allocation eliminated**
-- **Faster widget updates** (list iteration vs dictionary enumeration)
-- **Clearer, more maintainable code**
-
----
-
-## Testing Recommendations
-
-### 1. Widget Update Performance Test
-```csharp
-[Test]
-public void TestWidgetUpdateAllocations()
-{
- var window = await GameApp.UI.ShowUI();
-
- GC.Collect();
- var beforeMemory = GC.GetTotalMemory(true);
-
- // Simulate multiple frames
- for (int i = 0; i < 100; i++)
- {
- window.InternalUpdate();
- }
-
- var afterMemory = GC.GetTotalMemory(false);
- var allocated = afterMemory - beforeMemory;
-
- // Should allocate much less than before
- Assert.Less(allocated, 1000); // Adjust threshold
-}
-```
-
-### 2. Widget Creation Allocation Test
-```csharp
-[Test]
-public async Task TestWidgetCreationAllocations()
-{
- var window = await GameApp.UI.ShowUI();
-
- GC.Collect();
- var beforeMemory = GC.GetTotalMemory(true);
-
- // Create multiple widgets
- for (int i = 0; i < 10; i++)
- {
- await window.CreateWidgetAsync(parent);
- }
-
- var afterMemory = GC.GetTotalMemory(false);
- var allocated = afterMemory - beforeMemory;
-
- // Should not allocate strings for type names
- Assert.Less(allocated, 5000); // Adjust based on widget size
-}
-```
-
-### 3. Visibility Sort Performance Test
-```csharp
-[Test]
-public async Task TestVisibilitySortPerformance()
-{
- // Open multiple windows
- await GameApp.UI.ShowUI();
- await GameApp.UI.ShowUI();
- await GameApp.UI.ShowUI();
- await GameApp.UI.ShowUI();
-
- var stopwatch = Stopwatch.StartNew();
-
- // Trigger visibility sort
- await GameApp.UI.ShowUI(); // Re-open
-
- stopwatch.Stop();
-
- // Should be fast with early exit
- Assert.Less(stopwatch.ElapsedMilliseconds, 2);
-}
-```
-
-### 4. Cache Timer Test
-```csharp
-[Test]
-public async Task TestCacheTimerManagement()
-{
- // Open window with cache time
- await GameApp.UI.ShowUI();
- GameApp.UI.CloseUI();
-
- // Verify window is cached
- // Wait for cache expiration
- await UniTask.Delay(TimeSpan.FromSeconds(cacheTime + 1));
-
- // Verify window was disposed
- LogAssert.NoUnexpectedReceived();
-}
-```
-
----
-
-## Verification Checklist
-
-- [x] Code compiles without errors
-- [x] No breaking changes to public API
-- [x] Backward compatible with existing UI code
-- [x] Fixed all typos
-- [ ] Run Unity Editor and verify no errors
-- [ ] Test widget creation and updates
-- [ ] Profile memory allocations
-- [ ] Test cache timer expiration
-- [ ] Verify visibility sorting with fullscreen windows
-
----
-
-## Code Quality Improvements
-
-1. **Fixed Typos:**
- - `tiemrId` → `timerId`
- - `OnTimerDiposeWindow` → `OnTimerDisposeWindow`
-
-2. **Better Error Handling:**
- - Null checks in timer callbacks
- - Validation of timer creation
- - Improved error messages
-
-3. **Clearer Logic:**
- - Explicit early exit in visibility sorting
- - Named parameters for timer creation
- - Better comments
-
----
-
-## Known Limitations
-
-1. **Widget Update List:** Requires manual maintenance (add/remove)
-2. **Cache Dictionary:** Uses RuntimeTypeHandle which may have hash collisions (rare)
-3. **Visibility Sort:** Two-pass algorithm (could be optimized further)
-
----
-
-## Next Steps (Phase 3)
-
-1. Add state machine validation
-2. Optimize depth sorting (only update changed windows)
-3. Implement UI preloading system
-4. Add UI pooling for frequent windows
-5. Implement performance metrics
-
----
-
-## Notes
-
-- All optimizations maintain backward compatibility
-- No changes required to existing UI code
-- Significant reduction in per-frame allocations
-- Better code quality and maintainability
diff --git a/Runtime/UI/PHASE2_OPTIMIZATIONS.md.meta b/Runtime/UI/PHASE2_OPTIMIZATIONS.md.meta
deleted file mode 100644
index 11c336c..0000000
--- a/Runtime/UI/PHASE2_OPTIMIZATIONS.md.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 8eecca0a49896024eb62845a03e74f11
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Runtime/UI/PHASE3_OPTIMIZATIONS.md b/Runtime/UI/PHASE3_OPTIMIZATIONS.md
deleted file mode 100644
index 8f599b1..0000000
--- a/Runtime/UI/PHASE3_OPTIMIZATIONS.md
+++ /dev/null
@@ -1,431 +0,0 @@
-# Phase 3 UI Module Optimizations - Implementation Summary
-
-## Date: 2025-12-24
-
-## Overview
-Successfully implemented Phase 3 medium-priority optimizations for the UI module, focusing on state validation, depth sorting optimization, and code quality improvements.
-
----
-
-## ✅ Optimization 1: UI State Machine Validation
-
-### Problem
-- No validation of state transitions
-- Invalid state transitions could cause bugs
-- Difficult to debug lifecycle issues
-- No clear documentation of valid state flows
-
-### Solution
-**Files Created:**
-- `UIStateMachine.cs` (new file)
-
-**Files Modified:**
-- `UIBase.cs`
-- `UIMetadata.cs`
-
-**Changes:**
-1. Created `UIStateMachine` class with state transition validation
-2. Defined valid state transitions in dictionary
-3. Added `ValidateTransition()` method with logging
-4. Added `GetStateDescription()` for debugging
-5. Integrated validation into all lifecycle methods:
- - `CreateUI()` → CreatedUI
- - `InternalInitlized()` → Initialized
- - `InternalOpen()` → Opened
- - `InternalClose()` → Closed
- - `InternalDestroy()` → Destroying → Destroyed
-
-**Valid State Transitions:**
-```
-Uninitialized → CreatedUI
-CreatedUI → Loaded
-Loaded → Initialized
-Initialized → Opened
-Opened → Closed | Destroying
-Closed → Opened | Destroying
-Destroying → Destroyed
-Destroyed → (none)
-```
-
-**Code Example:**
-```csharp
-internal async UniTask InternalOpen()
-{
- if (_state == UIState.Opened)
- return; // Already open
-
- if (!UIStateMachine.ValidateTransition(GetType().Name, _state, UIState.Opened))
- return; // Invalid transition, logged
-
- _state = UIState.Opened;
- // ... rest of logic
-}
-```
-
-**Expected Impact:**
-- ✅ Catch invalid state transitions early
-- ✅ Better error messages with UI name
-- ✅ Prevent crashes from invalid lifecycle calls
-- ✅ Easier debugging of UI lifecycle issues
-
----
-
-## ✅ Optimization 2: Depth Sorting Optimization
-
-### Problem
-- `SortWindowDepth()` recalculates depth for ALL windows in layer
-- Called even when only one window changed
-- Unnecessary Canvas.sortingOrder updates
-- Performance degrades with more windows
-
-### Solution
-**File Modified:**
-- `UIModule.Open.cs`
-
-**Changes:**
-1. Added `startIndex` parameter to `SortWindowDepth()`
-2. Only update windows from `startIndex` onwards
-3. Check if depth changed before setting (avoid Canvas update)
-4. Integrated with `MoveToTop()` to only update affected windows
-
-**Code Changes:**
-```csharp
-// Before
-private void SortWindowDepth(int layer)
-{
- var list = _openUI[layer].OrderList;
- int baseDepth = layer * LAYER_DEEP;
-
- for (int i = 0; i < list.Count; i++)
- {
- list[i].View.Depth = baseDepth + i * WINDOW_DEEP; // Always set
- }
-}
-
-// After
-private void SortWindowDepth(int layer, int startIndex = 0)
-{
- var list = _openUI[layer].OrderList;
- int baseDepth = layer * LAYER_DEEP;
-
- // Only update from startIndex onwards
- for (int i = startIndex; i < list.Count; i++)
- {
- int newDepth = baseDepth + i * WINDOW_DEEP;
-
- // Only set if changed
- if (list[i].View.Depth != newDepth)
- {
- list[i].View.Depth = newDepth;
- }
- }
-}
-
-// MoveToTop now only updates affected windows
-private void MoveToTop(UIMetadata meta)
-{
- // ... move logic ...
-
- // Only update depth for affected windows
- SortWindowDepth(meta.MetaInfo.UILayer, currentIdx);
-}
-```
-
-**Expected Impact:**
-- ✅ Reduce unnecessary Canvas.sortingOrder updates
-- ✅ Faster window switching (only update changed windows)
-- ✅ Better performance with many windows in a layer
-
----
-
-## ✅ Optimization 3: Remove Implicit Bool Operator
-
-### Problem
-- `UIHolderObjectBase` had confusing implicit bool operator
-- Overloaded Unity's null check behavior
-- Could cause unexpected behavior
-- Not clear intent when used
-
-### Solution
-**File Modified:**
-- `UIHolderObjectBase.cs`
-
-**Changes:**
-1. Removed `implicit operator bool()`
-2. Added explicit `IsValid()` method
-3. Updated usage sites to use `IsValid()`
-4. Clearer intent and safer code
-
-**Code Changes:**
-```csharp
-// Before
-public static implicit operator bool(UIHolderObjectBase exists)
-{
- if (exists == null) return false;
- return exists.IsAlive;
-}
-
-// Usage
-if (meta.View?.Holder) // Confusing
-{
- // ...
-}
-
-// After
-public bool IsValid()
-{
- return this != null && _isAlive;
-}
-
-// Usage
-if (meta.View?.Holder != null && meta.View.Holder.IsValid()) // Clear
-{
- // ...
-}
-```
-
-**Expected Impact:**
-- ✅ Clearer intent in code
-- ✅ Avoid operator confusion
-- ✅ Safer null checking
-- ✅ Better maintainability
-
----
-
-## ✅ Optimization 4: State Check Improvements
-
-### Problem
-- Redundant state checks in lifecycle methods
-- No early returns for already-open windows
-- Could call lifecycle methods multiple times
-
-### Solution
-**File Modified:**
-- `UIBase.cs`
-
-**Changes:**
-1. Added early return in `InternalOpen()` if already open
-2. Added early return in `InternalClose()` if not open
-3. Combined with state machine validation
-4. Better error prevention
-
-**Code Changes:**
-```csharp
-// Before
-internal async UniTask InternalOpen()
-{
- if (_state != UIState.Opened)
- {
- _state = UIState.Opened;
- // ... logic
- }
-}
-
-// After
-internal async UniTask InternalOpen()
-{
- if (_state == UIState.Opened)
- return; // Already open, early exit
-
- if (!UIStateMachine.ValidateTransition(GetType().Name, _state, UIState.Opened))
- return; // Invalid transition
-
- _state = UIState.Opened;
- // ... logic
-}
-```
-
-**Expected Impact:**
-- ✅ Prevent duplicate lifecycle calls
-- ✅ Better error detection
-- ✅ Clearer logic flow
-
----
-
-## Performance Improvements Summary
-
-| Metric | Before | After | Improvement |
-|--------|--------|-------|-------------|
-| **Depth Updates** | All windows | Only changed | **Faster** |
-| **Canvas Updates** | Always set | Only if changed | **Fewer updates** |
-| **State Validation** | None | Full validation | **Better debugging** |
-| **Code Clarity** | Implicit operator | Explicit method | **Clearer** |
-
----
-
-## Testing Recommendations
-
-### 1. State Machine Validation Test
-```csharp
-[Test]
-public async Task TestInvalidStateTransition()
-{
- var window = await GameApp.UI.ShowUI();
-
- // Try to open already-open window
- await window.InternalOpen(); // Should log error and return
-
- // Verify no crash
- Assert.AreEqual(UIState.Opened, window.State);
-}
-
-[Test]
-public async Task TestValidStateTransitions()
-{
- var window = new TestWindow();
-
- // Uninitialized → CreatedUI
- window.CreateUI();
- Assert.AreEqual(UIState.CreatedUI, window.State);
-
- // CreatedUI → Loaded → Initialized → Opened
- await window.InternalInitlized();
- await window.InternalOpen();
- Assert.AreEqual(UIState.Opened, window.State);
-
- // Opened → Closed
- await window.InternalClose();
- Assert.AreEqual(UIState.Closed, window.State);
-
- // Closed → Destroying → Destroyed
- await window.InternalDestroy();
- Assert.AreEqual(UIState.Destroyed, window.State);
-}
-```
-
-### 2. Depth Sorting Optimization Test
-```csharp
-[Test]
-public async Task TestDepthSortingOptimization()
-{
- // Open multiple windows
- var w1 = await GameApp.UI.ShowUI();
- var w2 = await GameApp.UI.ShowUI();
- var w3 = await GameApp.UI.ShowUI();
-
- int initialDepth1 = w1.Depth;
- int initialDepth2 = w2.Depth;
- int initialDepth3 = w3.Depth;
-
- // Re-open w1 (should move to top)
- await GameApp.UI.ShowUI();
-
- // w1 should have new depth, w2 and w3 should be updated
- Assert.AreNotEqual(initialDepth1, w1.Depth);
-}
-```
-
-### 3. IsValid() Method Test
-```csharp
-[Test]
-public void TestHolderIsValid()
-{
- var holder = CreateTestHolder();
-
- Assert.IsTrue(holder.IsValid());
-
- Destroy(holder.gameObject);
-
- // After destroy, should be invalid
- Assert.IsFalse(holder.IsValid());
-}
-```
-
-### 4. State Check Test
-```csharp
-[Test]
-public async Task TestDuplicateOpenPrevention()
-{
- var window = await GameApp.UI.ShowUI();
-
- int openCallCount = 0;
- window.OnWindowBeforeShowEvent += () => openCallCount++;
-
- // Try to open again
- await window.InternalOpen();
-
- // Should only be called once
- Assert.AreEqual(1, openCallCount);
-}
-```
-
----
-
-## Verification Checklist
-
-- [x] Code compiles without errors
-- [x] No breaking changes to public API
-- [x] State machine validates all transitions
-- [x] Depth sorting only updates changed windows
-- [x] Implicit operator removed
-- [ ] Run Unity Editor and verify no errors
-- [ ] Test invalid state transitions
-- [ ] Test depth sorting with multiple windows
-- [ ] Verify IsValid() works correctly
-- [ ] Test duplicate lifecycle calls
-
----
-
-## Code Quality Improvements
-
-1. **State Machine:**
- - Clear documentation of valid transitions
- - Better error messages with UI name
- - Easier debugging
-
-2. **Depth Sorting:**
- - Partial updates instead of full recalculation
- - Avoid unnecessary Canvas updates
- - Better performance
-
-3. **Implicit Operator:**
- - Removed confusing operator overload
- - Explicit `IsValid()` method
- - Clearer intent
-
-4. **State Checks:**
- - Early returns for invalid states
- - Combined with validation
- - Better error prevention
-
----
-
-## Known Limitations
-
-1. **State Machine:** Adds small overhead to lifecycle calls (negligible)
-2. **Depth Sorting:** Still O(n) but with smaller n
-3. **IsValid():** Requires explicit call instead of implicit check
-
----
-
-## Next Steps (Future Enhancements)
-
-1. Implement UI preloading system
-2. Add UI pooling for frequent windows
-3. Implement performance metrics/profiling
-4. Add UI analytics tracking
-5. Optimize visibility sorting further
-
----
-
-## Combined Results (Phase 1 + 2 + 3)
-
-| Metric | Original | Optimized | Total Improvement |
-|--------|----------|-----------|-------------------|
-| **First UI Open** | 5-10ms | 0ms | **5-10ms faster** |
-| **Window Switch** | O(n) | O(1) | **0.5-2ms faster** |
-| **Per UI Open** | ~150 bytes | 0 bytes | **150 bytes saved** |
-| **Per Frame (widgets)** | ~100 bytes | 0 bytes | **100 bytes saved** |
-| **Widget Creation** | 40 bytes | 0 bytes | **40 bytes saved** |
-| **Depth Updates** | All windows | Only changed | **Fewer Canvas updates** |
-| **State Validation** | None | Full | **Better debugging** |
-
----
-
-## Notes
-
-- All optimizations maintain backward compatibility
-- State machine adds safety without performance cost
-- Depth sorting optimization reduces Canvas updates
-- Code is clearer and more maintainable
-- Better error detection and debugging capabilities
diff --git a/Runtime/UI/PHASE3_OPTIMIZATIONS.md.meta b/Runtime/UI/PHASE3_OPTIMIZATIONS.md.meta
deleted file mode 100644
index 4014c4c..0000000
--- a/Runtime/UI/PHASE3_OPTIMIZATIONS.md.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 3cc945d19bf80634eb057f3a5c8653ca
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Runtime/UI/UIBase/UIBase.Widget.cs b/Runtime/UI/UIBase/UIBase.Widget.cs
index 629a9c8..e6abe1e 100644
--- a/Runtime/UI/UIBase/UIBase.Widget.cs
+++ b/Runtime/UI/UIBase/UIBase.Widget.cs
@@ -1,6 +1,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
+using System.Threading;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
@@ -70,7 +71,7 @@ namespace AlicizaX.UI.Runtime
{
metadata.CreateUI();
await UIHolderFactory.CreateUIResourceAsync(metadata, parent, this);
- await ProcessWidget(metadata, visible);
+ await ProcessWidget(metadata, visible, metadata.CancellationToken);
return (UIBase)metadata.View;
}
@@ -105,7 +106,7 @@ namespace AlicizaX.UI.Runtime
metadata.CreateUI();
UIBase widget = (UIBase)metadata.View;
widget.BindUIHolder(holder, this);
- await ProcessWidget(metadata, true);
+ await ProcessWidget(metadata, true, metadata.CancellationToken);
return (T)widget;
}
@@ -142,14 +143,14 @@ namespace AlicizaX.UI.Runtime
#endregion
- private async UniTask ProcessWidget(UIMetadata meta, bool visible)
+ private async UniTask ProcessWidget(UIMetadata meta, bool visible, CancellationToken cancellationToken = default)
{
if (!AddWidget(meta)) return;
- await meta.View.InternalInitlized();
+ await meta.View.InternalInitlized(cancellationToken);
meta.View.Visible = visible;
if (meta.View.Visible)
{
- await meta.View.InternalOpen();
+ await meta.View.InternalOpen(cancellationToken);
}
}
@@ -176,6 +177,7 @@ namespace AlicizaX.UI.Runtime
{
if (_children.Remove(widget, out var meta))
{
+ meta.CancelAsyncOperations();
await widget.InternalClose();
// Remove from updateable list if present
diff --git a/Runtime/UI/UIBase/UIBase.cs b/Runtime/UI/UIBase/UIBase.cs
index 1773b73..b8b82da 100644
--- a/Runtime/UI/UIBase/UIBase.cs
+++ b/Runtime/UI/UIBase/UIBase.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
@@ -68,7 +69,7 @@ namespace AlicizaX.UI.Runtime
///
/// 如果重写当前方法 则同步OnInitialize不会调用
///
- protected virtual UniTask OnInitializeAsync()
+ protected virtual UniTask OnInitializeAsync(CancellationToken cancellationToken = default)
{
OnInitialize();
return UniTask.CompletedTask;
@@ -77,7 +78,7 @@ namespace AlicizaX.UI.Runtime
///
/// 如果重写当前方法 则同步OnOpen不会调用
///
- protected virtual UniTask OnOpenAsync()
+ protected virtual UniTask OnOpenAsync(CancellationToken cancellationToken = default)
{
OnOpen();
return UniTask.CompletedTask;
@@ -86,7 +87,7 @@ namespace AlicizaX.UI.Runtime
///
/// 如果重写当前方法 则同步OnClose不会调用
///
- protected virtual UniTask OnCloseAsync()
+ protected virtual UniTask OnCloseAsync(CancellationToken cancellationToken = default)
{
OnClose();
return UniTask.CompletedTask;
@@ -203,18 +204,18 @@ namespace AlicizaX.UI.Runtime
internal abstract void BindUIHolder(UIHolderObjectBase holder, UIBase owner);
- internal async UniTask InternalInitlized()
+ internal async UniTask InternalInitlized(CancellationToken cancellationToken = default)
{
if (!UIStateMachine.ValidateTransition(GetType().Name, _state, UIState.Initialized))
return;
_state = UIState.Initialized;
Holder.OnWindowInitEvent?.Invoke();
- await OnInitializeAsync();
+ await OnInitializeAsync(cancellationToken);
OnRegisterEvent(EventListenerProxy);
}
- internal async UniTask InternalOpen()
+ internal async UniTask InternalOpen(CancellationToken cancellationToken = default)
{
if (_state == UIState.Opened)
return; // Already open
@@ -225,11 +226,11 @@ namespace AlicizaX.UI.Runtime
_state = UIState.Opened;
Visible = true;
Holder.OnWindowBeforeShowEvent?.Invoke();
- await OnOpenAsync();
+ await OnOpenAsync(cancellationToken);
Holder.OnWindowAfterShowEvent?.Invoke();
}
- internal async UniTask InternalClose()
+ internal async UniTask InternalClose(CancellationToken cancellationToken = default)
{
if (_state != UIState.Opened)
return; // Not open, nothing to close
@@ -238,7 +239,7 @@ namespace AlicizaX.UI.Runtime
return;
Holder.OnWindowBeforeClosedEvent?.Invoke();
- await OnCloseAsync();
+ await OnCloseAsync(cancellationToken);
_state = UIState.Closed;
Visible = false;
Holder.OnWindowAfterClosedEvent?.Invoke();
diff --git a/Runtime/UI/UIBase/UIStateMachine.cs b/Runtime/UI/UIBase/UIStateMachine.cs
index c8a39ad..016963c 100644
--- a/Runtime/UI/UIBase/UIStateMachine.cs
+++ b/Runtime/UI/UIBase/UIStateMachine.cs
@@ -3,10 +3,7 @@ using System.Collections.Generic;
namespace AlicizaX.UI.Runtime
{
- ///
- /// UI State Machine - Validates state transitions for UI lifecycle.
- /// Helps catch bugs early and ensures proper UI lifecycle management.
- ///
+
internal static class UIStateMachine
{
private static readonly Dictionary> _validTransitions = new()
@@ -21,24 +18,11 @@ namespace AlicizaX.UI.Runtime
[UIState.Destroyed] = new() { }
};
- ///
- /// Checks if a state transition is valid.
- ///
- /// Current state
- /// Target state
- /// True if transition is valid
public static bool IsValidTransition(UIState from, UIState to)
{
return _validTransitions.TryGetValue(from, out var validStates) && validStates.Contains(to);
}
- ///
- /// Validates a state transition and logs error if invalid.
- ///
- /// Name of the UI for logging
- /// Current state
- /// Target state
- /// True if transition is valid
public static bool ValidateTransition(string uiName, UIState from, UIState to)
{
if (IsValidTransition(from, to))
@@ -48,11 +32,7 @@ namespace AlicizaX.UI.Runtime
return false;
}
- ///
- /// Gets all valid next states from the current state.
- ///
- /// Current state
- /// Set of valid next states
+
public static HashSet GetValidNextStates(UIState currentState)
{
return _validTransitions.TryGetValue(currentState, out var states)
@@ -60,9 +40,6 @@ namespace AlicizaX.UI.Runtime
: new HashSet();
}
- ///
- /// Gets a human-readable description of the state.
- ///
public static string GetStateDescription(UIState state)
{
return state switch