using AlicizaX; 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 { private readonly Dictionary> m_GroupedIndices = new Dictionary>(StringComparer.Ordinal); private readonly List m_ActiveAssemblyKeys = new List(16); private readonly HashSet m_OpenedItems = new HashSet(); private MemoryPoolInfo[] m_InfoBuffer = Array.Empty(); private SerializedProperty m_EnableStrictCheck; private bool m_ShowFullClassName; public override void OnInspectorGUI() { base.OnInspectorGUI(); serializedObject.Update(); MemoryPoolSetting setting = (MemoryPoolSetting)target; if (EditorApplication.isPlaying && IsPrefabInHierarchy(setting.gameObject)) { DrawRuntimeInspector(setting); } else { EditorGUILayout.PropertyField(m_EnableStrictCheck); } serializedObject.ApplyModifiedProperties(); Repaint(); } private void OnEnable() { m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck"); } private void DrawRuntimeInspector(MemoryPoolSetting setting) { bool enableStrictCheck = EditorGUILayout.Toggle("Enable Strict Check", setting.EnableStrictCheck); if (enableStrictCheck != setting.EnableStrictCheck) setting.EnableStrictCheck = enableStrictCheck; EditorGUILayout.LabelField("Memory Pool Count", MemoryPool.Count.ToString()); m_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName); int infoCount = FetchInfos(); DrawOverview(infoCount); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Clear All Pools")) MemoryPoolRegistry.ClearAll(); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); RebuildGroups(infoCount); for (int i = 0; i < m_ActiveAssemblyKeys.Count; i++) { string assemblyName = m_ActiveAssemblyKeys[i]; List indices = m_GroupedIndices[assemblyName]; bool lastState = m_OpenedItems.Contains(assemblyName); bool currentState = EditorGUILayout.Foldout(lastState, assemblyName); if (currentState != lastState) { if (currentState) m_OpenedItems.Add(assemblyName); else m_OpenedItems.Remove(assemblyName); } 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(); } } 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++) { ref MemoryPoolInfo info = ref m_InfoBuffer[i]; totalUnused += info.UnusedCount; totalUsing += info.UsingCount; totalArrayLen += info.PoolArrayLength; } EditorGUILayout.LabelField("Total Cached", totalUnused.ToString()); EditorGUILayout.LabelField("Total In Use", totalUsing.ToString()); EditorGUILayout.LabelField("Total Array Capacity", totalArrayLen.ToString()); } private void RebuildGroups(int infoCount) { foreach (KeyValuePair> 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 indices)) { indices = new List(8); m_GroupedIndices.Add(assemblyName, indices); } if (indices.Count == 0) m_ActiveAssemblyKeys.Add(assemblyName); indices.Add(i); } m_ActiveAssemblyKeys.Sort(StringComparer.Ordinal); } private void DrawReferencePoolInfo(int bufferIndex) { ref MemoryPoolInfo info = ref m_InfoBuffer[bufferIndex]; 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); } private void ExportCsv(string assemblyName, List indices) { 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; } } }