com.alicizax.unity.framework/Editor/Inspector/MemoryPoolComponentInspector.cs

221 lines
8.5 KiB
C#

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<string, List<int>> m_GroupedIndices = new Dictionary<string, List<int>>(StringComparer.Ordinal);
private readonly List<string> m_ActiveAssemblyKeys = new List<string>(16);
private readonly HashSet<string> m_OpenedItems = new HashSet<string>();
private MemoryPoolInfo[] m_InfoBuffer = Array.Empty<MemoryPoolInfo>();
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<int> 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<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);
}
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<int> 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;
}
}
}