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