2026-04-21 14:24:36 +08:00
using AlicizaX ;
2025-10-11 15:18:09 +08:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Text ;
using UnityEditor ;
using UnityEngine ;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(MemoryPoolSetting))]
internal sealed class MemoryPoolComponentInspector : GameFrameworkInspector
{
2026-04-23 19:09:56 +08:00
private readonly Dictionary < string , List < int > > m_GroupedIndices = new Dictionary < string , List < int > > ( StringComparer . Ordinal ) ;
private readonly List < string > m_ActiveAssemblyKeys = new List < string > ( 16 ) ;
2025-10-11 15:18:09 +08:00
private readonly HashSet < string > m_OpenedItems = new HashSet < string > ( ) ;
2026-04-23 19:09:56 +08:00
private MemoryPoolInfo [ ] m_InfoBuffer = Array . Empty < MemoryPoolInfo > ( ) ;
2025-10-11 15:18:09 +08:00
2026-04-23 19:09:56 +08:00
private SerializedProperty m_EnableStrictCheck ;
private bool m_ShowFullClassName ;
2025-10-11 15:18:09 +08:00
public override void OnInspectorGUI ( )
{
base . OnInspectorGUI ( ) ;
serializedObject . Update ( ) ;
2026-04-23 19:09:56 +08:00
MemoryPoolSetting setting = ( MemoryPoolSetting ) target ;
if ( EditorApplication . isPlaying & & IsPrefabInHierarchy ( setting . gameObject ) )
2025-10-11 15:18:09 +08:00
{
2026-04-23 19:09:56 +08:00
DrawRuntimeInspector ( setting ) ;
}
else
{
EditorGUILayout . PropertyField ( m_EnableStrictCheck ) ;
}
2025-10-11 15:18:09 +08:00
2026-04-23 19:09:56 +08:00
serializedObject . ApplyModifiedProperties ( ) ;
Repaint ( ) ;
}
2026-04-21 14:24:36 +08:00
2026-04-23 19:09:56 +08:00
private void OnEnable ( )
{
m_EnableStrictCheck = serializedObject . FindProperty ( "m_EnableStrictCheck" ) ;
}
2026-04-21 14:24:36 +08:00
2026-04-23 19:09:56 +08:00
private void DrawRuntimeInspector ( MemoryPoolSetting setting )
{
bool enableStrictCheck = EditorGUILayout . Toggle ( "Enable Strict Check" , setting . EnableStrictCheck ) ;
if ( enableStrictCheck ! = setting . EnableStrictCheck )
setting . EnableStrictCheck = enableStrictCheck ;
2026-04-21 14:24:36 +08:00
2026-04-23 19:09:56 +08:00
EditorGUILayout . LabelField ( "Memory Pool Count" , MemoryPool . Count . ToString ( ) ) ;
m_ShowFullClassName = EditorGUILayout . Toggle ( "Show Full Class Name" , m_ShowFullClassName ) ;
2026-04-21 14:24:36 +08:00
2026-04-23 19:09:56 +08:00
int infoCount = FetchInfos ( ) ;
DrawOverview ( infoCount ) ;
2026-04-21 14:24:36 +08:00
2026-04-23 19:09:56 +08:00
EditorGUILayout . Space ( ) ;
EditorGUILayout . BeginHorizontal ( ) ;
if ( GUILayout . Button ( "Clear All Pools" ) )
MemoryPoolRegistry . ClearAll ( ) ;
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . Space ( ) ;
2025-10-11 15:18:09 +08:00
2026-04-23 19:09:56 +08:00
RebuildGroups ( infoCount ) ;
for ( int i = 0 ; i < m_ActiveAssemblyKeys . Count ; i + + )
{
string assemblyName = m_ActiveAssemblyKeys [ i ] ;
List < int > indices = m_GroupedIndices [ assemblyName ] ;
2025-10-11 15:18:09 +08:00
2026-04-23 19:09:56 +08:00
bool lastState = m_OpenedItems . Contains ( assemblyName ) ;
bool currentState = EditorGUILayout . Foldout ( lastState , assemblyName ) ;
if ( currentState ! = lastState )
{
2025-10-11 15:18:09 +08:00
if ( currentState )
2026-04-23 19:09:56 +08:00
m_OpenedItems . Add ( assemblyName ) ;
else
m_OpenedItems . Remove ( assemblyName ) ;
2025-10-11 15:18:09 +08:00
}
2026-04-23 19:09:56 +08:00
if ( ! currentState )
continue ;
indices . Sort ( m_ShowFullClassName ? CompareFullClassName : CompareNormalClassName ) ;
EditorGUILayout . BeginVertical ( "box" ) ;
string label = "Unused\tUsing\tAcquire\tRelease\tCreated\tHiWater\tMaxCap\tIdle\tArrLen" ;
EditorGUILayout . LabelField ( m_ShowFullClassName ? "Full Class Name" : "Class Name" , label ) ;
for ( int j = 0 ; j < indices . Count ; j + + )
DrawReferencePoolInfo ( indices [ j ] ) ;
if ( GUILayout . Button ( "Export CSV Data" ) )
ExportCsv ( assemblyName , indices ) ;
EditorGUILayout . EndVertical ( ) ;
EditorGUILayout . Separator ( ) ;
2025-10-11 15:18:09 +08:00
}
2026-04-23 19:09:56 +08:00
}
private int FetchInfos ( )
{
int poolCount = MemoryPool . Count ;
if ( m_InfoBuffer . Length < poolCount )
m_InfoBuffer = new MemoryPoolInfo [ GetBufferCapacity ( poolCount ) ] ;
return MemoryPool . GetAllMemoryPoolInfos ( m_InfoBuffer ) ;
}
private void DrawOverview ( int infoCount )
{
int totalUnused = 0 ;
int totalUsing = 0 ;
int totalArrayLen = 0 ;
for ( int i = 0 ; i < infoCount ; i + + )
2025-10-11 15:18:09 +08:00
{
2026-04-23 19:09:56 +08:00
ref MemoryPoolInfo info = ref m_InfoBuffer [ i ] ;
totalUnused + = info . UnusedCount ;
totalUsing + = info . UsingCount ;
totalArrayLen + = info . PoolArrayLength ;
2025-10-11 15:18:09 +08:00
}
2026-04-23 19:09:56 +08:00
EditorGUILayout . LabelField ( "Total Cached" , totalUnused . ToString ( ) ) ;
EditorGUILayout . LabelField ( "Total In Use" , totalUsing . ToString ( ) ) ;
EditorGUILayout . LabelField ( "Total Array Capacity" , totalArrayLen . ToString ( ) ) ;
2025-10-11 15:18:09 +08:00
}
2026-04-23 19:09:56 +08:00
private void RebuildGroups ( int infoCount )
2025-10-11 15:18:09 +08:00
{
2026-04-23 19:09:56 +08:00
foreach ( KeyValuePair < string , List < int > > pair in m_GroupedIndices )
pair . Value . Clear ( ) ;
m_ActiveAssemblyKeys . Clear ( ) ;
for ( int i = 0 ; i < infoCount ; i + + )
{
ref MemoryPoolInfo info = ref m_InfoBuffer [ i ] ;
string assemblyName = info . Type . Assembly . GetName ( ) . Name ;
if ( ! m_GroupedIndices . TryGetValue ( assemblyName , out List < int > indices ) )
{
indices = new List < int > ( 8 ) ;
m_GroupedIndices . Add ( assemblyName , indices ) ;
}
if ( indices . Count = = 0 )
m_ActiveAssemblyKeys . Add ( assemblyName ) ;
indices . Add ( i ) ;
}
m_ActiveAssemblyKeys . Sort ( StringComparer . Ordinal ) ;
2025-10-11 15:18:09 +08:00
}
2026-04-23 19:09:56 +08:00
private void DrawReferencePoolInfo ( int bufferIndex )
2025-10-11 15:18:09 +08:00
{
2026-04-23 19:09:56 +08:00
ref MemoryPoolInfo info = ref m_InfoBuffer [ bufferIndex ] ;
2026-04-21 14:24:36 +08:00
string name = m_ShowFullClassName ? info . Type . FullName : info . Type . Name ;
string values = Utility . Text . Format ( "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}" ,
info . UnusedCount , info . UsingCount ,
info . AcquireCount , info . ReleaseCount ,
info . CreateCount , info . HighWaterMark ,
info . MaxCapacity , info . IdleFrames , info . PoolArrayLength ) ;
EditorGUILayout . LabelField ( name , values ) ;
2025-10-11 15:18:09 +08:00
}
2026-04-23 19:09:56 +08:00
private void ExportCsv ( string assemblyName , List < int > indices )
2025-10-11 15:18:09 +08:00
{
2026-04-23 19:09:56 +08:00
string exportFileName = EditorUtility . SaveFilePanel ( "Export CSV Data" , string . Empty , Utility . Text . Format ( "Memory Pool Data - {0}.csv" , assemblyName ) , string . Empty ) ;
if ( string . IsNullOrEmpty ( exportFileName ) )
return ;
try
{
int index = 0 ;
string [ ] data = new string [ indices . Count + 1 ] ;
data [ index + + ] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Created,HighWaterMark,MaxCapacity,IdleFrames,ArrayLength" ;
for ( int i = 0 ; i < indices . Count ; i + + )
{
ref MemoryPoolInfo info = ref m_InfoBuffer [ indices [ i ] ] ;
data [ index + + ] = Utility . Text . Format ( "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}" ,
info . Type . Name , info . Type . FullName ,
info . UnusedCount , info . UsingCount ,
info . AcquireCount , info . ReleaseCount ,
info . CreateCount , info . HighWaterMark ,
info . MaxCapacity , info . IdleFrames , info . PoolArrayLength ) ;
}
File . WriteAllLines ( exportFileName , data , Encoding . UTF8 ) ;
Debug . Log ( Utility . Text . Format ( "Export memory pool CSV data to '{0}' success." , exportFileName ) ) ;
}
catch ( Exception exception )
{
Debug . LogError ( Utility . Text . Format ( "Export memory pool CSV data to '{0}' failure, exception is '{1}'." , exportFileName , exception ) ) ;
}
}
private int CompareNormalClassName ( int leftIndex , int rightIndex )
{
ref MemoryPoolInfo left = ref m_InfoBuffer [ leftIndex ] ;
ref MemoryPoolInfo right = ref m_InfoBuffer [ rightIndex ] ;
return left . Type . Name . CompareTo ( right . Type . Name ) ;
}
private int CompareFullClassName ( int leftIndex , int rightIndex )
{
ref MemoryPoolInfo left = ref m_InfoBuffer [ leftIndex ] ;
ref MemoryPoolInfo right = ref m_InfoBuffer [ rightIndex ] ;
return left . Type . FullName . CompareTo ( right . Type . FullName ) ;
}
private static int GetBufferCapacity ( int count )
{
int capacity = 8 ;
while ( capacity < count )
capacity < < = 1 ;
return capacity ;
2025-10-11 15:18:09 +08:00
}
}
}