2026-04-21 13:01:55 +08:00
|
|
|
using System;
|
2025-09-05 19:46:30 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
2026-04-21 13:01:55 +08:00
|
|
|
using UnityEngine.UIElements;
|
2025-09-05 19:46:30 +08:00
|
|
|
|
|
|
|
|
namespace AlicizaX.Debugger.Runtime
|
|
|
|
|
{
|
|
|
|
|
public sealed partial class DebuggerComponent
|
|
|
|
|
{
|
2026-04-21 13:01:55 +08:00
|
|
|
private sealed class ReferencePoolInformationWindow : PollingDebuggerWindowBase
|
2025-09-05 19:46:30 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
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>();
|
2026-04-21 13:01:55 +08:00
|
|
|
private bool m_ShowFullClassName;
|
2026-04-23 19:09:56 +08:00
|
|
|
private Toggle m_ShowFullClassNameToggle;
|
|
|
|
|
|
|
|
|
|
public ReferencePoolInformationWindow()
|
|
|
|
|
{
|
|
|
|
|
m_NormalIndexComparer = CompareNormalClassName;
|
|
|
|
|
m_FullIndexComparer = CompareFullClassName;
|
|
|
|
|
}
|
2025-09-05 19:46:30 +08:00
|
|
|
|
2026-04-21 13:01:55 +08:00
|
|
|
protected override void BuildWindow(VisualElement root)
|
2025-09-05 19:46:30 +08:00
|
|
|
{
|
2026-04-21 14:24:36 +08:00
|
|
|
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
|
2026-04-23 19:09:56 +08:00
|
|
|
int infoCount = FetchInfos();
|
2026-04-21 14:24:36 +08:00
|
|
|
|
|
|
|
|
VisualElement overview = CreateSection("Memory Pool Overview", out VisualElement overviewCard);
|
2026-04-21 13:01:55 +08:00
|
|
|
overviewCard.Add(CreateRow("Enable Strict Check", MemoryPool.EnableStrictCheck.ToString()));
|
2026-04-21 14:24:36 +08:00
|
|
|
overviewCard.Add(CreateRow("Pool Type Count", MemoryPool.Count.ToString()));
|
|
|
|
|
|
|
|
|
|
int totalUnused = 0;
|
|
|
|
|
int totalUsing = 0;
|
|
|
|
|
int totalArrayLen = 0;
|
2026-04-23 19:09:56 +08:00
|
|
|
for (int i = 0; i < infoCount; i++)
|
2026-04-21 14:24:36 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
ref MemoryPoolInfo info = ref m_InfoBuffer[i];
|
2026-04-21 14:24:36 +08:00
|
|
|
totalUnused += info.UnusedCount;
|
|
|
|
|
totalUsing += info.UsingCount;
|
|
|
|
|
totalArrayLen += info.PoolArrayLength;
|
|
|
|
|
}
|
2026-04-23 19:09:56 +08:00
|
|
|
|
2026-04-21 14:24:36 +08:00
|
|
|
overviewCard.Add(CreateRow("Total Cached Objects", totalUnused.ToString()));
|
|
|
|
|
overviewCard.Add(CreateRow("Total In Use", totalUsing.ToString()));
|
|
|
|
|
overviewCard.Add(CreateRow("Total Array Capacity", totalArrayLen.ToString()));
|
|
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
m_ShowFullClassNameToggle = CreateConsoleFilterToggle("Show Full ClassName", m_ShowFullClassName, DebuggerTheme.PrimaryText, OnShowFullClassNameChanged);
|
|
|
|
|
overviewCard.Add(m_ShowFullClassNameToggle);
|
2026-04-21 14:24:36 +08:00
|
|
|
|
|
|
|
|
VisualElement buttonRow = new VisualElement();
|
|
|
|
|
buttonRow.style.flexDirection = FlexDirection.Row;
|
|
|
|
|
buttonRow.style.marginTop = 8f * scale;
|
2026-04-23 19:09:56 +08:00
|
|
|
buttonRow.Add(CreateActionButton("Clear All Pools", OnClearAllPools, DebuggerTheme.Danger));
|
2026-04-21 14:24:36 +08:00
|
|
|
overviewCard.Add(buttonRow);
|
2026-04-21 13:01:55 +08:00
|
|
|
root.Add(overview);
|
2025-09-05 19:46:30 +08:00
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
RebuildGroups(infoCount);
|
|
|
|
|
for (int i = 0; i < m_ActiveAssemblyKeys.Count; i++)
|
2025-09-05 19:46:30 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
string assemblyKey = m_ActiveAssemblyKeys[i];
|
|
|
|
|
List<int> indices = m_GroupedIndices[assemblyKey];
|
|
|
|
|
indices.Sort(m_ShowFullClassName ? m_FullIndexComparer : m_NormalIndexComparer);
|
|
|
|
|
|
|
|
|
|
VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyKey), out VisualElement card);
|
|
|
|
|
for (int j = 0; j < indices.Count; j++)
|
2025-09-05 19:46:30 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
card.Add(CreatePoolInfoItem(in m_InfoBuffer[indices[j]], m_ShowFullClassName, scale));
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
2026-04-23 19:09:56 +08:00
|
|
|
|
|
|
|
|
root.Add(section);
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
2026-04-23 19:09:56 +08:00
|
|
|
}
|
2025-09-05 19:46:30 +08:00
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
private int FetchInfos()
|
|
|
|
|
{
|
|
|
|
|
int poolCount = MemoryPool.Count;
|
|
|
|
|
if (m_InfoBuffer.Length < poolCount)
|
|
|
|
|
m_InfoBuffer = new MemoryPoolInfo[GetBufferCapacity(poolCount)];
|
2026-04-21 14:24:36 +08:00
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
return MemoryPool.GetAllMemoryPoolInfos(m_InfoBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
2026-04-21 13:01:55 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
indices = new List<int>(8);
|
|
|
|
|
m_GroupedIndices.Add(assemblyName, indices);
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
if (indices.Count == 0)
|
|
|
|
|
m_ActiveAssemblyKeys.Add(assemblyName);
|
|
|
|
|
|
|
|
|
|
indices.Add(i);
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
2026-04-23 19:09:56 +08:00
|
|
|
|
|
|
|
|
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();
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
private int CompareNormalClassName(int leftIndex, int rightIndex)
|
2025-09-05 19:46:30 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
ref MemoryPoolInfo left = ref m_InfoBuffer[leftIndex];
|
|
|
|
|
ref MemoryPoolInfo right = ref m_InfoBuffer[rightIndex];
|
|
|
|
|
return left.Type.Name.CompareTo(right.Type.Name);
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
private int CompareFullClassName(int leftIndex, int rightIndex)
|
2025-09-05 19:46:30 +08:00
|
|
|
{
|
2026-04-23 19:09:56 +08:00
|
|
|
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;
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
2026-04-21 13:01:55 +08:00
|
|
|
|
2026-04-23 19:09:56 +08:00
|
|
|
private static VisualElement CreatePoolInfoItem(in MemoryPoolInfo info, bool showFullName, float scale)
|
2026-04-21 13:01:55 +08:00
|
|
|
{
|
|
|
|
|
VisualElement item = CreateCard();
|
|
|
|
|
item.style.marginBottom = 8f * scale;
|
|
|
|
|
item.style.backgroundColor = DebuggerTheme.PanelSurfaceAlt;
|
|
|
|
|
|
2026-04-21 14:24:36 +08:00
|
|
|
string title = showFullName ? info.Type.FullName : info.Type.Name;
|
2026-04-21 13:01:55 +08:00
|
|
|
Label titleLabel = new Label(title ?? string.Empty);
|
|
|
|
|
titleLabel.style.color = DebuggerTheme.PrimaryText;
|
|
|
|
|
titleLabel.style.fontSize = 16f * scale;
|
|
|
|
|
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
|
|
|
|
titleLabel.style.whiteSpace = WhiteSpace.Normal;
|
|
|
|
|
titleLabel.style.marginBottom = 6f * scale;
|
2026-04-21 14:24:36 +08:00
|
|
|
item.Add(titleLabel);
|
2026-04-21 13:01:55 +08:00
|
|
|
|
2026-04-21 14:24:36 +08:00
|
|
|
string stats = Utility.Text.Format(
|
|
|
|
|
"Unused {0} | Using {1} | Acquire {2} | Release {3} | Created {4}",
|
|
|
|
|
info.UnusedCount, info.UsingCount,
|
|
|
|
|
info.AcquireCount, info.ReleaseCount,
|
|
|
|
|
info.CreateCount);
|
|
|
|
|
Label statsLabel = new Label(stats);
|
|
|
|
|
statsLabel.style.color = DebuggerTheme.SecondaryText;
|
|
|
|
|
statsLabel.style.fontSize = 14f * scale;
|
|
|
|
|
statsLabel.style.whiteSpace = WhiteSpace.Normal;
|
|
|
|
|
statsLabel.style.marginBottom = 4f * scale;
|
|
|
|
|
item.Add(statsLabel);
|
2026-04-21 13:01:55 +08:00
|
|
|
|
2026-04-21 14:24:36 +08:00
|
|
|
string recycleStatus = Utility.Text.Format(
|
|
|
|
|
"HighWater {0} | MaxCap {1} | Idle {2}f | Array {3}",
|
|
|
|
|
info.HighWaterMark, info.MaxCapacity,
|
|
|
|
|
info.IdleFrames, info.PoolArrayLength);
|
|
|
|
|
Label recycleLabel = new Label(recycleStatus);
|
|
|
|
|
recycleLabel.style.fontSize = 13f * scale;
|
|
|
|
|
recycleLabel.style.whiteSpace = WhiteSpace.Normal;
|
|
|
|
|
|
|
|
|
|
if (info.IdleFrames >= 300)
|
|
|
|
|
recycleLabel.style.color = DebuggerTheme.Warning;
|
|
|
|
|
else if (info.IdleFrames >= 200)
|
|
|
|
|
recycleLabel.style.color = new Color(0.9f, 0.7f, 0.3f);
|
|
|
|
|
else
|
|
|
|
|
recycleLabel.style.color = DebuggerTheme.SecondaryText;
|
|
|
|
|
|
|
|
|
|
item.Add(recycleLabel);
|
2026-04-21 13:01:55 +08:00
|
|
|
return item;
|
|
|
|
|
}
|
2025-09-05 19:46:30 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|