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_ReferencePoolInfos = new Dictionary>(StringComparer.Ordinal); private readonly HashSet m_OpenedItems = new HashSet(); private SerializedProperty m_EnableStrictCheck = null; private bool m_ShowFullClassName = false; public override void OnInspectorGUI() { base.OnInspectorGUI(); serializedObject.Update(); MemoryPoolSetting t = (MemoryPoolSetting)target; if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject)) { bool enableStrictCheck = EditorGUILayout.Toggle("Enable Strict Check", t.EnableStrictCheck); if (enableStrictCheck != t.EnableStrictCheck) { t.EnableStrictCheck = enableStrictCheck; } EditorGUILayout.LabelField("Memory Pool Count", MemoryPool.Count.ToString()); m_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName); // 全局统计 MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos(); int totalUnused = 0, totalUsing = 0, totalArrayLen = 0; foreach (var info in referencePoolInfos) { 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()); EditorGUILayout.Space(); // 全局操作按钮 EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Clear All Pools")) { MemoryPoolRegistry.ClearAll(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); // 按 Assembly 分组 m_ReferencePoolInfos.Clear(); foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos) { string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name; if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List results)) { results = new List(); m_ReferencePoolInfos.Add(assemblyName, results); } results.Add(referencePoolInfo); } foreach (KeyValuePair> assemblyReferencePoolInfo in m_ReferencePoolInfos) { bool lastState = m_OpenedItems.Contains(assemblyReferencePoolInfo.Key); bool currentState = EditorGUILayout.Foldout(lastState, assemblyReferencePoolInfo.Key); if (currentState != lastState) { if (currentState) m_OpenedItems.Add(assemblyReferencePoolInfo.Key); else m_OpenedItems.Remove(assemblyReferencePoolInfo.Key); } if (currentState) { EditorGUILayout.BeginVertical("box"); { var label = "Unused\tUsing\tAcquire\tRelease\tCreated\tHiWater\tMaxCap\tIdle\tArrLen"; EditorGUILayout.LabelField(m_ShowFullClassName ? "Full Class Name" : "Class Name", label); assemblyReferencePoolInfo.Value.Sort(Comparison); foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value) { DrawReferencePoolInfo(referencePoolInfo); } if (GUILayout.Button("Export CSV Data")) { string exportFileName = EditorUtility.SaveFilePanel("Export CSV Data", string.Empty, Utility.Text.Format("Memory Pool Data - {0}.csv", assemblyReferencePoolInfo.Key), string.Empty); if (!string.IsNullOrEmpty(exportFileName)) { try { int index = 0; string[] data = new string[assemblyReferencePoolInfo.Value.Count + 1]; data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Created,HighWaterMark,MaxCapacity,IdleFrames,ArrayLength"; foreach (MemoryPoolInfo info in assemblyReferencePoolInfo.Value) { 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)); } } } } EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); } } } else { EditorGUILayout.PropertyField(m_EnableStrictCheck); } serializedObject.ApplyModifiedProperties(); Repaint(); } private void OnEnable() { m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck"); } private void DrawReferencePoolInfo(MemoryPoolInfo info) { 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 int Comparison(MemoryPoolInfo a, MemoryPoolInfo b) { if (m_ShowFullClassName) return a.Type.FullName.CompareTo(b.Type.FullName); else return a.Type.Name.CompareTo(b.Type.Name); } } }