优化ObjectPool和MemoryPool性能
This commit is contained in:
parent
8c3f2c99cc
commit
aa37eecf8b
@ -11,12 +11,13 @@ namespace AlicizaX.Editor
|
||||
[CustomEditor(typeof(MemoryPoolSetting))]
|
||||
internal sealed class MemoryPoolComponentInspector : GameFrameworkInspector
|
||||
{
|
||||
private readonly Dictionary<string, List<MemoryPoolInfo>> m_ReferencePoolInfos = new Dictionary<string, List<MemoryPoolInfo>>(StringComparer.Ordinal);
|
||||
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 = null;
|
||||
|
||||
private bool m_ShowFullClassName = false;
|
||||
private SerializedProperty m_EnableStrictCheck;
|
||||
private bool m_ShowFullClassName;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
@ -24,114 +25,10 @@ namespace AlicizaX.Editor
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
MemoryPoolSetting t = (MemoryPoolSetting)target;
|
||||
|
||||
if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject))
|
||||
MemoryPoolSetting setting = (MemoryPoolSetting)target;
|
||||
if (EditorApplication.isPlaying && IsPrefabInHierarchy(setting.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<MemoryPoolInfo> results))
|
||||
{
|
||||
results = new List<MemoryPoolInfo>();
|
||||
m_ReferencePoolInfos.Add(assemblyName, results);
|
||||
}
|
||||
results.Add(referencePoolInfo);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<MemoryPoolInfo>> 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();
|
||||
}
|
||||
}
|
||||
DrawRuntimeInspector(setting);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -139,7 +36,6 @@ namespace AlicizaX.Editor
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
@ -148,8 +44,116 @@ namespace AlicizaX.Editor
|
||||
m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck");
|
||||
}
|
||||
|
||||
private void DrawReferencePoolInfo(MemoryPoolInfo info)
|
||||
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,
|
||||
@ -159,12 +163,58 @@ namespace AlicizaX.Editor
|
||||
EditorGUILayout.LabelField(name, values);
|
||||
}
|
||||
|
||||
private int Comparison(MemoryPoolInfo a, MemoryPoolInfo b)
|
||||
private void ExportCsv(string assemblyName, List<int> indices)
|
||||
{
|
||||
if (m_ShowFullClassName)
|
||||
return a.Type.FullName.CompareTo(b.Type.FullName);
|
||||
else
|
||||
return a.Type.Name.CompareTo(b.Type.Name);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ namespace AlicizaX.Editor
|
||||
internal sealed class ObjectPoolComponentInspector : GameFrameworkInspector
|
||||
{
|
||||
private readonly HashSet<string> m_OpenedItems = new HashSet<string>();
|
||||
private ObjectPoolBase[] m_ObjectPools = new ObjectPoolBase[1];
|
||||
private ObjectInfo[] m_ObjectInfos = new ObjectInfo[1];
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
@ -30,10 +32,11 @@ namespace AlicizaX.Editor
|
||||
{
|
||||
EditorGUILayout.LabelField("Object Pool Count", t.Count.ToString());
|
||||
|
||||
ObjectPoolBase[] objectPools = t.GetAllObjectPools(true);
|
||||
foreach (ObjectPoolBase objectPool in objectPools)
|
||||
int objectPoolCount = EnsureObjectPoolBuffer(t.Count);
|
||||
objectPoolCount = t.GetAllObjectPools(true, m_ObjectPools);
|
||||
for (int i = 0; i < objectPoolCount; i++)
|
||||
{
|
||||
DrawObjectPool(objectPool);
|
||||
DrawObjectPool(m_ObjectPools[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,12 +72,14 @@ namespace AlicizaX.Editor
|
||||
EditorGUILayout.LabelField("Used Count", objectPool.Count.ToString());
|
||||
EditorGUILayout.LabelField("Expire Time", objectPool.ExpireTime.ToString());
|
||||
EditorGUILayout.LabelField("Priority", objectPool.Priority.ToString());
|
||||
ObjectInfo[] objectInfos = objectPool.GetAllObjectInfos();
|
||||
if (objectInfos.Length > 0)
|
||||
int objectInfoCount = EnsureObjectInfoBuffer(objectPool.Count);
|
||||
objectInfoCount = objectPool.GetAllObjectInfos(m_ObjectInfos);
|
||||
if (objectInfoCount > 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("Name", objectPool.AllowMultiSpawn ? "Locked\tCount\tFlag\tLast Use Time" : "Locked\tIn Use\tFlag\tLast Use Time");
|
||||
foreach (ObjectInfo objectInfo in objectInfos)
|
||||
for (int i = 0; i < objectInfoCount; i++)
|
||||
{
|
||||
ObjectInfo objectInfo = m_ObjectInfos[i];
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
|
||||
string lastUse = Utility.Text.Format("{0:F1}s ago", UnityEngine.Time.realtimeSinceStartup - objectInfo.LastUseTime);
|
||||
@ -109,10 +114,11 @@ namespace AlicizaX.Editor
|
||||
try
|
||||
{
|
||||
int index = 0;
|
||||
string[] data = new string[objectInfos.Length + 1];
|
||||
string[] data = new string[objectInfoCount + 1];
|
||||
data[index++] = Utility.Text.Format("Name,Locked,{0},Custom Can Release Flag,Last Use Time", objectPool.AllowMultiSpawn ? "Count" : "In Use");
|
||||
foreach (ObjectInfo objectInfo in objectInfos)
|
||||
for (int i = 0; i < objectInfoCount; i++)
|
||||
{
|
||||
ObjectInfo objectInfo = m_ObjectInfos[i];
|
||||
string csvLastUse = Utility.Text.Format("{0:F1}s ago", UnityEngine.Time.realtimeSinceStartup - objectInfo.LastUseTime);
|
||||
data[index++] = objectPool.AllowMultiSpawn
|
||||
? Utility.Text.Format("{0},{1},{2},{3},{4}", objectInfo.Name, objectInfo.Locked, objectInfo.SpawnCount, objectInfo.CustomCanReleaseFlag, csvLastUse)
|
||||
@ -139,5 +145,27 @@ namespace AlicizaX.Editor
|
||||
EditorGUILayout.Separator();
|
||||
}
|
||||
}
|
||||
|
||||
private int EnsureObjectPoolBuffer(int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
if (m_ObjectPools == null || m_ObjectPools.Length < count)
|
||||
m_ObjectPools = new ObjectPoolBase[count];
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private int EnsureObjectInfoBuffer(int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
if (m_ObjectInfos == null || m_ObjectInfos.Length < count)
|
||||
m_ObjectInfos = new ObjectInfo[count];
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,10 +9,14 @@ namespace AlicizaX.Debugger.Runtime
|
||||
private sealed class ObjectPoolInformationWindow : PollingDebuggerWindowBase
|
||||
{
|
||||
private IObjectPoolService m_ObjectPoolService;
|
||||
private IObjectPoolServiceDebugView m_ObjectPoolDebugView;
|
||||
private ObjectPoolBase[] m_ObjectPools;
|
||||
private ObjectInfo[] m_ObjectInfos;
|
||||
|
||||
public override void Initialize(params object[] args)
|
||||
{
|
||||
m_ObjectPoolService = AppServices.Require<IObjectPoolService>();
|
||||
m_ObjectPoolDebugView = m_ObjectPoolService as IObjectPoolServiceDebugView;
|
||||
}
|
||||
|
||||
protected override void BuildWindow(VisualElement root)
|
||||
@ -26,10 +30,13 @@ namespace AlicizaX.Debugger.Runtime
|
||||
overviewCard.Add(CreateRow("Object Pool Count", m_ObjectPoolService.Count.ToString()));
|
||||
root.Add(overview);
|
||||
|
||||
ObjectPoolBase[] objectPools = m_ObjectPoolService.GetAllObjectPools(true);
|
||||
for (int i = 0; i < objectPools.Length; i++)
|
||||
int objectPoolCount = EnsureObjectPoolBuffer(m_ObjectPoolService.Count);
|
||||
objectPoolCount = m_ObjectPoolDebugView != null
|
||||
? m_ObjectPoolDebugView.GetAllObjectPools(true, m_ObjectPools)
|
||||
: 0;
|
||||
for (int i = 0; i < objectPoolCount; i++)
|
||||
{
|
||||
ObjectPoolBase objectPool = objectPools[i];
|
||||
ObjectPoolBase objectPool = m_ObjectPools[i];
|
||||
VisualElement section = CreateSection(Utility.Text.Format("Object Pool: {0}", objectPool.FullName), out VisualElement card);
|
||||
card.Add(CreateRow("Name", objectPool.Name));
|
||||
card.Add(CreateRow("Type", objectPool.ObjectType.FullName));
|
||||
@ -39,16 +46,17 @@ namespace AlicizaX.Debugger.Runtime
|
||||
card.Add(CreateRow("Expire Time", objectPool.ExpireTime.ToString()));
|
||||
card.Add(CreateRow("Priority", objectPool.Priority.ToString()));
|
||||
|
||||
ObjectInfo[] objectInfos = objectPool.GetAllObjectInfos();
|
||||
if (objectInfos.Length <= 0)
|
||||
int objectInfoCount = EnsureObjectInfoBuffer(objectPool.Count);
|
||||
objectInfoCount = objectPool.GetAllObjectInfos(m_ObjectInfos);
|
||||
if (objectInfoCount <= 0)
|
||||
{
|
||||
card.Add(CreateRow("Entries", "Object Pool is Empty ..."));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < objectInfos.Length; j++)
|
||||
for (int j = 0; j < objectInfoCount; j++)
|
||||
{
|
||||
ObjectInfo info = objectInfos[j];
|
||||
ObjectInfo info = m_ObjectInfos[j];
|
||||
string title = string.IsNullOrEmpty(info.Name) ? "<None>" : info.Name;
|
||||
string content = Utility.Text.Format(
|
||||
"Locked {0} | {1} {2} | Flag {3} | Last Use {4}",
|
||||
@ -64,6 +72,36 @@ namespace AlicizaX.Debugger.Runtime
|
||||
root.Add(section);
|
||||
}
|
||||
}
|
||||
|
||||
private int EnsureObjectPoolBuffer(int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
if (m_ObjectPools == null || m_ObjectPools.Length == 0)
|
||||
m_ObjectPools = new ObjectPoolBase[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_ObjectPools == null || m_ObjectPools.Length < count)
|
||||
m_ObjectPools = new ObjectPoolBase[count];
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private int EnsureObjectInfoBuffer(int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
if (m_ObjectInfos == null || m_ObjectInfos.Length == 0)
|
||||
m_ObjectInfos = new ObjectInfo[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_ObjectInfos == null || m_ObjectInfos.Length < count)
|
||||
m_ObjectInfos = new ObjectInfo[count];
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,98 +9,145 @@ namespace AlicizaX.Debugger.Runtime
|
||||
{
|
||||
private sealed class ReferencePoolInformationWindow : PollingDebuggerWindowBase
|
||||
{
|
||||
private readonly Dictionary<string, List<MemoryPoolInfo>> m_ReferencePoolInfos = new Dictionary<string, List<MemoryPoolInfo>>(StringComparer.Ordinal);
|
||||
private readonly Comparison<MemoryPoolInfo> m_NormalClassNameComparer = NormalClassNameComparer;
|
||||
private readonly Comparison<MemoryPoolInfo> m_FullClassNameComparer = FullClassNameComparer;
|
||||
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 Comparison<int> m_NormalIndexComparer;
|
||||
private readonly Comparison<int> m_FullIndexComparer;
|
||||
|
||||
private MemoryPoolInfo[] m_InfoBuffer = Array.Empty<MemoryPoolInfo>();
|
||||
private bool m_ShowFullClassName;
|
||||
private Toggle showFullClassNameToggle;
|
||||
private Toggle m_ShowFullClassNameToggle;
|
||||
|
||||
public ReferencePoolInformationWindow()
|
||||
{
|
||||
m_NormalIndexComparer = CompareNormalClassName;
|
||||
m_FullIndexComparer = CompareFullClassName;
|
||||
}
|
||||
|
||||
protected override void BuildWindow(VisualElement root)
|
||||
{
|
||||
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
|
||||
int infoCount = FetchInfos();
|
||||
|
||||
// ---- Overview Section ----
|
||||
VisualElement overview = CreateSection("Memory Pool Overview", out VisualElement overviewCard);
|
||||
overviewCard.Add(CreateRow("Enable Strict Check", MemoryPool.EnableStrictCheck.ToString()));
|
||||
overviewCard.Add(CreateRow("Pool Type Count", MemoryPool.Count.ToString()));
|
||||
|
||||
// 统计总缓存对象数和总数组容量
|
||||
MemoryPoolInfo[] allInfos = MemoryPool.GetAllMemoryPoolInfos();
|
||||
int totalUnused = 0;
|
||||
int totalUsing = 0;
|
||||
int totalArrayLen = 0;
|
||||
foreach (var info in allInfos)
|
||||
for (int i = 0; i < infoCount; i++)
|
||||
{
|
||||
ref MemoryPoolInfo info = ref m_InfoBuffer[i];
|
||||
totalUnused += info.UnusedCount;
|
||||
totalUsing += info.UsingCount;
|
||||
totalArrayLen += info.PoolArrayLength;
|
||||
}
|
||||
|
||||
overviewCard.Add(CreateRow("Total Cached Objects", totalUnused.ToString()));
|
||||
overviewCard.Add(CreateRow("Total In Use", totalUsing.ToString()));
|
||||
overviewCard.Add(CreateRow("Total Array Capacity", totalArrayLen.ToString()));
|
||||
|
||||
showFullClassNameToggle = CreateConsoleFilterToggle("Show Full ClassName", m_ShowFullClassName, DebuggerTheme.PrimaryText, value => m_ShowFullClassName = value);
|
||||
overviewCard.Add(showFullClassNameToggle);
|
||||
m_ShowFullClassNameToggle = CreateConsoleFilterToggle("Show Full ClassName", m_ShowFullClassName, DebuggerTheme.PrimaryText, OnShowFullClassNameChanged);
|
||||
overviewCard.Add(m_ShowFullClassNameToggle);
|
||||
|
||||
// ---- 操作按钮 ----
|
||||
VisualElement buttonRow = new VisualElement();
|
||||
buttonRow.style.flexDirection = FlexDirection.Row;
|
||||
buttonRow.style.marginTop = 8f * scale;
|
||||
|
||||
buttonRow.Add(CreateActionButton("Clear All Pools", () =>
|
||||
{
|
||||
MemoryPoolRegistry.ClearAll();
|
||||
Rebuild();
|
||||
}, DebuggerTheme.Danger));
|
||||
|
||||
buttonRow.Add(CreateActionButton("Clear All Pools", OnClearAllPools, DebuggerTheme.Danger));
|
||||
overviewCard.Add(buttonRow);
|
||||
root.Add(overview);
|
||||
|
||||
// ---- 按 Assembly 分组 ----
|
||||
m_ReferencePoolInfos.Clear();
|
||||
foreach (MemoryPoolInfo info in allInfos)
|
||||
RebuildGroups(infoCount);
|
||||
for (int i = 0; i < m_ActiveAssemblyKeys.Count; i++)
|
||||
{
|
||||
string assemblyName = info.Type.Assembly.GetName().Name;
|
||||
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List<MemoryPoolInfo> results))
|
||||
{
|
||||
results = new List<MemoryPoolInfo>();
|
||||
m_ReferencePoolInfos.Add(assemblyName, results);
|
||||
}
|
||||
results.Add(info);
|
||||
}
|
||||
string assemblyKey = m_ActiveAssemblyKeys[i];
|
||||
List<int> indices = m_GroupedIndices[assemblyKey];
|
||||
indices.Sort(m_ShowFullClassName ? m_FullIndexComparer : m_NormalIndexComparer);
|
||||
|
||||
foreach (KeyValuePair<string, List<MemoryPoolInfo>> assemblyInfo in m_ReferencePoolInfos)
|
||||
VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyKey), out VisualElement card);
|
||||
for (int j = 0; j < indices.Count; j++)
|
||||
{
|
||||
assemblyInfo.Value.Sort(m_ShowFullClassName ? m_FullClassNameComparer : m_NormalClassNameComparer);
|
||||
VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyInfo.Key), out VisualElement card);
|
||||
|
||||
if (assemblyInfo.Value.Count <= 0)
|
||||
{
|
||||
card.Add(CreateRow("State", "Memory Pool is Empty ..."));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < assemblyInfo.Value.Count; i++)
|
||||
{
|
||||
card.Add(CreatePoolInfoItem(assemblyInfo.Value[i], m_ShowFullClassName, scale));
|
||||
}
|
||||
card.Add(CreatePoolInfoItem(in m_InfoBuffer[indices[j]], m_ShowFullClassName, scale));
|
||||
}
|
||||
|
||||
root.Add(section);
|
||||
}
|
||||
}
|
||||
|
||||
private static int NormalClassNameComparer(MemoryPoolInfo a, MemoryPoolInfo b)
|
||||
private int FetchInfos()
|
||||
{
|
||||
return a.Type.Name.CompareTo(b.Type.Name);
|
||||
int poolCount = MemoryPool.Count;
|
||||
if (m_InfoBuffer.Length < poolCount)
|
||||
m_InfoBuffer = new MemoryPoolInfo[GetBufferCapacity(poolCount)];
|
||||
|
||||
return MemoryPool.GetAllMemoryPoolInfos(m_InfoBuffer);
|
||||
}
|
||||
|
||||
private static int FullClassNameComparer(MemoryPoolInfo a, MemoryPoolInfo b)
|
||||
private void RebuildGroups(int infoCount)
|
||||
{
|
||||
return a.Type.FullName.CompareTo(b.Type.FullName);
|
||||
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);
|
||||
}
|
||||
|
||||
private static VisualElement CreatePoolInfoItem(MemoryPoolInfo info, bool showFullName, float scale)
|
||||
if (indices.Count == 0)
|
||||
m_ActiveAssemblyKeys.Add(assemblyName);
|
||||
|
||||
indices.Add(i);
|
||||
}
|
||||
|
||||
m_ActiveAssemblyKeys.Sort(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private void OnShowFullClassNameChanged(bool value)
|
||||
{
|
||||
if (m_ShowFullClassName == value)
|
||||
return;
|
||||
|
||||
m_ShowFullClassName = value;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void OnClearAllPools()
|
||||
{
|
||||
MemoryPoolRegistry.ClearAll();
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static VisualElement CreatePoolInfoItem(in MemoryPoolInfo info, bool showFullName, float scale)
|
||||
{
|
||||
VisualElement item = CreateCard();
|
||||
item.style.marginBottom = 8f * scale;
|
||||
@ -115,7 +162,6 @@ namespace AlicizaX.Debugger.Runtime
|
||||
titleLabel.style.marginBottom = 6f * scale;
|
||||
item.Add(titleLabel);
|
||||
|
||||
// 基础统计
|
||||
string stats = Utility.Text.Format(
|
||||
"Unused {0} | Using {1} | Acquire {2} | Release {3} | Created {4}",
|
||||
info.UnusedCount, info.UsingCount,
|
||||
@ -128,7 +174,6 @@ namespace AlicizaX.Debugger.Runtime
|
||||
statsLabel.style.marginBottom = 4f * scale;
|
||||
item.Add(statsLabel);
|
||||
|
||||
// 回收策略状态
|
||||
string recycleStatus = Utility.Text.Format(
|
||||
"HighWater {0} | MaxCap {1} | Idle {2}f | Array {3}",
|
||||
info.HighWaterMark, info.MaxCapacity,
|
||||
@ -137,7 +182,6 @@ namespace AlicizaX.Debugger.Runtime
|
||||
recycleLabel.style.fontSize = 13f * scale;
|
||||
recycleLabel.style.whiteSpace = WhiteSpace.Normal;
|
||||
|
||||
// 根据空闲帧数着色:接近回收阈值时变色
|
||||
if (info.IdleFrames >= 300)
|
||||
recycleLabel.style.color = DebuggerTheme.Warning;
|
||||
else if (info.IdleFrames >= 200)
|
||||
|
||||
@ -250,9 +250,9 @@ namespace AlicizaX
|
||||
get => s_Count;
|
||||
}
|
||||
|
||||
internal static MemoryPoolInfo GetInfo()
|
||||
internal static void GetInfo(ref MemoryPoolInfo info)
|
||||
{
|
||||
return new MemoryPoolInfo(
|
||||
info.Set(
|
||||
typeof(T), s_Count,
|
||||
s_CurrentInUse,
|
||||
s_AcquireCount, s_ReleaseCount,
|
||||
|
||||
@ -41,6 +41,11 @@ namespace AlicizaX
|
||||
return MemoryPoolRegistry.GetAllInfos();
|
||||
}
|
||||
|
||||
public static int GetAllMemoryPoolInfos(MemoryPoolInfo[] infos)
|
||||
{
|
||||
return MemoryPoolRegistry.GetAllInfos(infos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有内存池。
|
||||
/// </summary>
|
||||
|
||||
@ -4,21 +4,21 @@ using System.Runtime.InteropServices;
|
||||
namespace AlicizaX
|
||||
{
|
||||
/// <summary>
|
||||
/// 内存池信息。
|
||||
/// Memory pool snapshot info.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct MemoryPoolInfo
|
||||
{
|
||||
private readonly Type _type;
|
||||
private readonly int _unusedCount;
|
||||
private readonly int _usingCount;
|
||||
private readonly int _acquireCount;
|
||||
private readonly int _releaseCount;
|
||||
private readonly int _createCount;
|
||||
private readonly int _highWaterMark;
|
||||
private readonly int _maxCapacity;
|
||||
private readonly int _idleFrames;
|
||||
private readonly int _poolArrayLength;
|
||||
private Type _type;
|
||||
private int _unusedCount;
|
||||
private int _usingCount;
|
||||
private int _acquireCount;
|
||||
private int _releaseCount;
|
||||
private int _createCount;
|
||||
private int _highWaterMark;
|
||||
private int _maxCapacity;
|
||||
private int _idleFrames;
|
||||
private int _poolArrayLength;
|
||||
|
||||
public MemoryPoolInfo(Type type, int unusedCount, int usingCount,
|
||||
int acquireCount, int releaseCount, int createCount,
|
||||
@ -37,54 +37,41 @@ namespace AlicizaX
|
||||
_poolArrayLength = poolArrayLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 池类型。
|
||||
/// </summary>
|
||||
public Type Type => _type;
|
||||
|
||||
/// <summary>
|
||||
/// 池中空闲对象数量(可立即借出)。
|
||||
/// </summary>
|
||||
public int UnusedCount => _unusedCount;
|
||||
|
||||
/// <summary>
|
||||
/// 当前被借出、尚未归还的对象数量。
|
||||
/// </summary>
|
||||
public int UsingCount => _usingCount;
|
||||
|
||||
/// <summary>
|
||||
/// 累计 Acquire 调用次数(仅开发模式有效)。
|
||||
/// </summary>
|
||||
public int AcquireCount => _acquireCount;
|
||||
|
||||
/// <summary>
|
||||
/// 累计 Release 调用次数(仅开发模式有效)。
|
||||
/// </summary>
|
||||
public int ReleaseCount => _releaseCount;
|
||||
|
||||
/// <summary>
|
||||
/// 累计 new T() 创建次数(池不够时的实际分配,仅开发模式有效)。
|
||||
/// </summary>
|
||||
public int CreateCount => _createCount;
|
||||
|
||||
/// <summary>
|
||||
/// 近期峰值并发使用量。回收策略据此决定保留多少对象。
|
||||
/// </summary>
|
||||
public int HighWaterMark => _highWaterMark;
|
||||
|
||||
/// <summary>
|
||||
/// 池容量硬上限。超出后 Release 的对象直接丢弃交给 GC。
|
||||
/// </summary>
|
||||
public int MaxCapacity => _maxCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 连续无 Acquire 的帧数。>=300 开始温和回收,>=900 激进回收+高水位衰减。
|
||||
/// </summary>
|
||||
public int IdleFrames => _idleFrames;
|
||||
|
||||
/// <summary>
|
||||
/// 底层 T[] 数组的实际长度。反映真实内存占用(含空槽)。
|
||||
/// </summary>
|
||||
public int PoolArrayLength => _poolArrayLength;
|
||||
|
||||
internal void Set(Type type, int unusedCount, int usingCount,
|
||||
int acquireCount, int releaseCount, int createCount,
|
||||
int highWaterMark, int maxCapacity,
|
||||
int idleFrames, int poolArrayLength)
|
||||
{
|
||||
_type = type;
|
||||
_unusedCount = unusedCount;
|
||||
_usingCount = usingCount;
|
||||
_acquireCount = acquireCount;
|
||||
_releaseCount = releaseCount;
|
||||
_createCount = createCount;
|
||||
_highWaterMark = highWaterMark;
|
||||
_maxCapacity = maxCapacity;
|
||||
_idleFrames = idleFrames;
|
||||
_poolArrayLength = poolArrayLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ namespace AlicizaX
|
||||
public delegate void ReleaseHandler(IMemory memory);
|
||||
public delegate void ClearHandler();
|
||||
public delegate void IntHandler(int value);
|
||||
public delegate MemoryPoolInfo GetInfoHandler();
|
||||
public delegate void GetInfoHandler(ref MemoryPoolInfo info);
|
||||
|
||||
public readonly AcquireHandler Acquire;
|
||||
public readonly ReleaseHandler Release;
|
||||
@ -99,12 +99,29 @@ namespace AlicizaX
|
||||
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
|
||||
}
|
||||
|
||||
public static int GetAllInfos(MemoryPoolInfo[] infos)
|
||||
{
|
||||
if (infos == null)
|
||||
throw new ArgumentNullException(nameof(infos));
|
||||
|
||||
int count = s_Handles.Count;
|
||||
if (infos.Length < count)
|
||||
throw new ArgumentException("Target buffer is too small.", nameof(infos));
|
||||
|
||||
int i = 0;
|
||||
foreach (var kv in s_Handles)
|
||||
{
|
||||
kv.Value.GetInfo(ref infos[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static MemoryPoolInfo[] GetAllInfos()
|
||||
{
|
||||
var infos = new MemoryPoolInfo[s_Handles.Count];
|
||||
int i = 0;
|
||||
foreach (var kv in s_Handles)
|
||||
infos[i++] = kv.Value.GetInfo();
|
||||
GetAllInfos(infos);
|
||||
return infos;
|
||||
}
|
||||
|
||||
@ -158,7 +175,9 @@ namespace AlicizaX
|
||||
|
||||
if (s_Handles.TryGetValue(type, out var handle))
|
||||
{
|
||||
int unused = handle.GetInfo().UnusedCount;
|
||||
MemoryPoolInfo info = default;
|
||||
handle.GetInfo(ref info);
|
||||
int unused = info.UnusedCount;
|
||||
handle.Shrink(unused - count);
|
||||
return;
|
||||
}
|
||||
@ -166,7 +185,9 @@ namespace AlicizaX
|
||||
EnsureRegistered(type);
|
||||
if (s_Handles.TryGetValue(type, out handle))
|
||||
{
|
||||
int unused = handle.GetInfo().UnusedCount;
|
||||
MemoryPoolInfo info = default;
|
||||
handle.GetInfo(ref info);
|
||||
int unused = info.UnusedCount;
|
||||
handle.Shrink(unused - count);
|
||||
return;
|
||||
}
|
||||
|
||||
7
Runtime/ObjectPool/IObjectPoolDebugView.cs
Normal file
7
Runtime/ObjectPool/IObjectPoolDebugView.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace AlicizaX.ObjectPool
|
||||
{
|
||||
internal interface IObjectPoolDebugView
|
||||
{
|
||||
int GetAllObjectInfos(ObjectInfo[] results);
|
||||
}
|
||||
}
|
||||
11
Runtime/ObjectPool/IObjectPoolDebugView.cs.meta
Normal file
11
Runtime/ObjectPool/IObjectPoolDebugView.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b99f3fc658c4a3d4f80218ab7113341e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AlicizaX.ObjectPool
|
||||
{
|
||||
public readonly struct ObjectPoolCreateOptions
|
||||
@ -53,11 +51,6 @@ namespace AlicizaX.ObjectPool
|
||||
ObjectPoolBase GetObjectPool(Type objectType);
|
||||
ObjectPoolBase GetObjectPool(Type objectType, string name);
|
||||
|
||||
ObjectPoolBase[] GetAllObjectPools();
|
||||
ObjectPoolBase[] GetAllObjectPools(bool sort);
|
||||
void GetAllObjectPools(List<ObjectPoolBase> results);
|
||||
void GetAllObjectPools(bool sort, List<ObjectPoolBase> results);
|
||||
|
||||
IObjectPool<T> CreatePool<T>(ObjectPoolCreateOptions options = default) where T : ObjectBase;
|
||||
ObjectPoolBase CreatePool(Type objectType, ObjectPoolCreateOptions options = default);
|
||||
|
||||
|
||||
7
Runtime/ObjectPool/IObjectPoolServiceDebugView.cs
Normal file
7
Runtime/ObjectPool/IObjectPoolServiceDebugView.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace AlicizaX.ObjectPool
|
||||
{
|
||||
internal interface IObjectPoolServiceDebugView
|
||||
{
|
||||
int GetAllObjectPools(bool sort, ObjectPoolBase[] results);
|
||||
}
|
||||
}
|
||||
11
Runtime/ObjectPool/IObjectPoolServiceDebugView.cs.meta
Normal file
11
Runtime/ObjectPool/IObjectPoolServiceDebugView.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e3fa6e0005d58a4eba0e005ee613c61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AlicizaX.ObjectPool
|
||||
{
|
||||
|
||||
public abstract class ObjectPoolBase
|
||||
public abstract class ObjectPoolBase : IObjectPoolDebugView
|
||||
{
|
||||
private readonly string m_Name;
|
||||
private string m_FullName;
|
||||
|
||||
public ObjectPoolBase() : this(null) { }
|
||||
|
||||
@ -17,7 +16,15 @@ namespace AlicizaX.ObjectPool
|
||||
|
||||
public string Name => m_Name;
|
||||
|
||||
public string FullName => new TypeNamePair(ObjectType, m_Name).ToString();
|
||||
public string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_FullName == null)
|
||||
m_FullName = new TypeNamePair(ObjectType, m_Name).ToString();
|
||||
return m_FullName;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Type ObjectType { get; }
|
||||
public abstract int Count { get; }
|
||||
@ -39,8 +46,7 @@ namespace AlicizaX.ObjectPool
|
||||
public abstract void Release(int toReleaseCount);
|
||||
public abstract void ReleaseAllUnused();
|
||||
|
||||
public abstract ObjectInfo[] GetAllObjectInfos();
|
||||
public abstract void GetAllObjectInfos(List<ObjectInfo> results);
|
||||
public abstract int GetAllObjectInfos(ObjectInfo[] results);
|
||||
|
||||
internal abstract void Update(float elapseSeconds, float realElapseSeconds);
|
||||
internal abstract void Shutdown();
|
||||
|
||||
@ -28,9 +28,12 @@ namespace AlicizaX
|
||||
svc.OnLowMemory();
|
||||
}
|
||||
|
||||
public ObjectPoolBase[] GetAllObjectPools(bool sort)
|
||||
internal int GetAllObjectPools(bool sort, ObjectPoolBase[] results)
|
||||
{
|
||||
return _mObjectPoolService.GetAllObjectPools(sort);
|
||||
if (_mObjectPoolService is IObjectPoolServiceDebugView debugView)
|
||||
return debugView.GetAllObjectPools(sort, results);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,26 +184,16 @@ namespace AlicizaX.ObjectPool
|
||||
return SpawnAny(name);
|
||||
|
||||
if (!m_AvailableNameHeadMap.TryGetValue(name, out int head)) return null;
|
||||
ref var slot = ref m_Slots[head];
|
||||
if (!slot.IsAlive() || slot.SpawnCount != 0 || !string.Equals(slot.Obj.Name, name, StringComparison.Ordinal))
|
||||
throw new GameFrameworkException($"Object pool '{FullName}' available-name head is inconsistent.");
|
||||
|
||||
float now = Time.realtimeSinceStartup;
|
||||
int current = head;
|
||||
while (current >= 0)
|
||||
{
|
||||
ref var slot = ref m_Slots[current];
|
||||
if (slot.IsAlive() && string.Equals(slot.Obj.Name, name)
|
||||
&& slot.SpawnCount == 0)
|
||||
{
|
||||
SpawnSlot(current, now);
|
||||
SpawnSlot(head, now);
|
||||
ValidateState();
|
||||
return slot.Obj;
|
||||
}
|
||||
|
||||
current = slot.NextAvailableByName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool CanSpawn() => CanSpawn(string.Empty);
|
||||
|
||||
@ -213,18 +203,7 @@ namespace AlicizaX.ObjectPool
|
||||
if (m_AllowMultiSpawn)
|
||||
return m_AllNameHeadMap.ContainsKey(name);
|
||||
|
||||
if (!m_AvailableNameHeadMap.TryGetValue(name, out int head)) return false;
|
||||
|
||||
int current = head;
|
||||
while (current >= 0)
|
||||
{
|
||||
ref var slot = ref m_Slots[current];
|
||||
if (slot.IsAlive() && slot.SpawnCount == 0 && string.Equals(slot.Obj.Name, name))
|
||||
return true;
|
||||
current = slot.NextAvailableByName;
|
||||
}
|
||||
|
||||
return false;
|
||||
return m_AvailableNameHeadMap.ContainsKey(name);
|
||||
}
|
||||
|
||||
public void Unspawn(T obj)
|
||||
@ -334,34 +313,28 @@ namespace AlicizaX.ObjectPool
|
||||
ValidateState();
|
||||
}
|
||||
|
||||
public override ObjectInfo[] GetAllObjectInfos()
|
||||
{
|
||||
var list = new ObjectInfo[m_TargetMap.Count];
|
||||
int write = 0;
|
||||
for (int i = 0; i < m_SlotCount && write < list.Length; i++)
|
||||
{
|
||||
ref var slot = ref m_Slots[i];
|
||||
if (!slot.IsAlive()) continue;
|
||||
list[write++] = new ObjectInfo(slot.Obj.Name, slot.Obj.Locked,
|
||||
slot.Obj.CustomCanReleaseFlag,
|
||||
slot.Obj.LastUseTime, slot.SpawnCount);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void GetAllObjectInfos(List<ObjectInfo> results)
|
||||
public override int GetAllObjectInfos(ObjectInfo[] results)
|
||||
{
|
||||
if (results == null) throw new GameFrameworkException("Results is invalid.");
|
||||
results.Clear();
|
||||
|
||||
int write = 0;
|
||||
int capacity = results.Length;
|
||||
for (int i = 0; i < m_SlotCount; i++)
|
||||
{
|
||||
ref var slot = ref m_Slots[i];
|
||||
if (!slot.IsAlive()) continue;
|
||||
results.Add(new ObjectInfo(slot.Obj.Name, slot.Obj.Locked,
|
||||
|
||||
if (write < capacity)
|
||||
{
|
||||
results[write] = new ObjectInfo(slot.Obj.Name, slot.Obj.Locked,
|
||||
slot.Obj.CustomCanReleaseFlag,
|
||||
slot.Obj.LastUseTime, slot.SpawnCount));
|
||||
slot.Obj.LastUseTime, slot.SpawnCount);
|
||||
}
|
||||
|
||||
write++;
|
||||
}
|
||||
|
||||
return write;
|
||||
}
|
||||
|
||||
internal override void OnLowMemory()
|
||||
@ -704,7 +677,7 @@ namespace AlicizaX.ObjectPool
|
||||
{
|
||||
ref var slot = ref m_Slots[idx];
|
||||
if (slot.PrevAvailableByName >= 0 || slot.NextAvailableByName >= 0)
|
||||
return;
|
||||
throw new GameFrameworkException($"Object pool '{FullName}' available-name chain is inconsistent.");
|
||||
|
||||
string objectName = slot.Obj.Name ?? string.Empty;
|
||||
if (m_AvailableNameTailMap.TryGetValue(objectName, out int tail))
|
||||
@ -766,6 +739,7 @@ namespace AlicizaX.ObjectPool
|
||||
return null;
|
||||
|
||||
SpawnSlot(head, now);
|
||||
ValidateState();
|
||||
return slot.Obj;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ namespace AlicizaX.ObjectPool
|
||||
{
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IServiceTickable
|
||||
internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IObjectPoolServiceDebugView, IServiceTickable
|
||||
{
|
||||
private const float DefaultAutoReleaseInterval = float.MaxValue;
|
||||
private const int DefaultCapacity = int.MaxValue;
|
||||
@ -15,7 +15,7 @@ namespace AlicizaX.ObjectPool
|
||||
private readonly Dictionary<TypeNamePair, ObjectPoolBase> m_ObjectPools;
|
||||
private readonly List<ObjectPoolBase> m_ObjectPoolList;
|
||||
private readonly Dictionary<ObjectPoolBase, int> m_ObjectPoolIndexMap;
|
||||
private readonly List<ObjectPoolBase> m_CachedAllObjectPools;
|
||||
private readonly List<ObjectPoolBase> m_CachedSortedObjectPools;
|
||||
private readonly Comparison<ObjectPoolBase> m_ObjectPoolComparer;
|
||||
|
||||
public ObjectPoolService()
|
||||
@ -23,7 +23,7 @@ namespace AlicizaX.ObjectPool
|
||||
m_ObjectPools = new Dictionary<TypeNamePair, ObjectPoolBase>();
|
||||
m_ObjectPoolList = new List<ObjectPoolBase>();
|
||||
m_ObjectPoolIndexMap = new Dictionary<ObjectPoolBase, int>(AlicizaX.ReferenceComparer<ObjectPoolBase>.Instance);
|
||||
m_CachedAllObjectPools = new List<ObjectPoolBase>();
|
||||
m_CachedSortedObjectPools = new List<ObjectPoolBase>();
|
||||
m_ObjectPoolComparer = ObjectPoolComparer;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ namespace AlicizaX.ObjectPool
|
||||
m_ObjectPools.Clear();
|
||||
m_ObjectPoolList.Clear();
|
||||
m_ObjectPoolIndexMap.Clear();
|
||||
m_CachedAllObjectPools.Clear();
|
||||
m_CachedSortedObjectPools.Clear();
|
||||
}
|
||||
|
||||
// ========== Has ==========
|
||||
@ -90,24 +90,23 @@ namespace AlicizaX.ObjectPool
|
||||
|
||||
// ========== GetAll ==========
|
||||
|
||||
public ObjectPoolBase[] GetAllObjectPools() => m_ObjectPoolList.ToArray();
|
||||
|
||||
public ObjectPoolBase[] GetAllObjectPools(bool sort)
|
||||
{
|
||||
if (!sort) return m_ObjectPoolList.ToArray();
|
||||
var results = new List<ObjectPoolBase>(m_ObjectPoolList);
|
||||
results.Sort(m_ObjectPoolComparer);
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
public void GetAllObjectPools(List<ObjectPoolBase> results) => GetAllObjectPools(false, results);
|
||||
|
||||
public void GetAllObjectPools(bool sort, List<ObjectPoolBase> results)
|
||||
int IObjectPoolServiceDebugView.GetAllObjectPools(bool sort, ObjectPoolBase[] results)
|
||||
{
|
||||
if (results == null) throw new GameFrameworkException("Results is invalid.");
|
||||
results.Clear();
|
||||
results.AddRange(m_ObjectPoolList);
|
||||
if (sort) results.Sort(m_ObjectPoolComparer);
|
||||
|
||||
List<ObjectPoolBase> source = m_ObjectPoolList;
|
||||
if (sort)
|
||||
{
|
||||
CacheSortedObjectPools();
|
||||
source = m_CachedSortedObjectPools;
|
||||
}
|
||||
|
||||
int count = source.Count;
|
||||
int copyCount = results.Length < count ? results.Length : count;
|
||||
for (int i = 0; i < copyCount; i++)
|
||||
results[i] = source[i];
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// ========== Create (single entry point) ==========
|
||||
@ -190,16 +189,16 @@ namespace AlicizaX.ObjectPool
|
||||
|
||||
public void Release()
|
||||
{
|
||||
GetAllObjectPools(true, m_CachedAllObjectPools);
|
||||
for (int i = 0; i < m_CachedAllObjectPools.Count; i++)
|
||||
m_CachedAllObjectPools[i].Release();
|
||||
CacheSortedObjectPools();
|
||||
for (int i = 0; i < m_CachedSortedObjectPools.Count; i++)
|
||||
m_CachedSortedObjectPools[i].Release();
|
||||
}
|
||||
|
||||
public void ReleaseAllUnused()
|
||||
{
|
||||
GetAllObjectPools(true, m_CachedAllObjectPools);
|
||||
for (int i = 0; i < m_CachedAllObjectPools.Count; i++)
|
||||
m_CachedAllObjectPools[i].ReleaseAllUnused();
|
||||
CacheSortedObjectPools();
|
||||
for (int i = 0; i < m_CachedSortedObjectPools.Count; i++)
|
||||
m_CachedSortedObjectPools[i].ReleaseAllUnused();
|
||||
}
|
||||
|
||||
// ========== Low memory ==========
|
||||
@ -230,6 +229,13 @@ namespace AlicizaX.ObjectPool
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CacheSortedObjectPools()
|
||||
{
|
||||
m_CachedSortedObjectPools.Clear();
|
||||
m_CachedSortedObjectPools.AddRange(m_ObjectPoolList);
|
||||
m_CachedSortedObjectPools.Sort(m_ObjectPoolComparer);
|
||||
}
|
||||
|
||||
private void RemovePoolFromList(ObjectPoolBase pool)
|
||||
{
|
||||
if (!m_ObjectPoolIndexMap.TryGetValue(pool, out int index))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user