com.alicizax.unity.framework/Runtime/Debugger/DebuggerComponent.TimerInformationWindow.cs
2026-04-28 16:29:44 +08:00

281 lines
12 KiB
C#

using AlicizaX.Timer.Runtime;
using UnityEngine;
using UnityEngine.UIElements;
namespace AlicizaX.Debugger.Runtime
{
public sealed partial class DebuggerComponent
{
private sealed class TimerInformationWindow : ScrollableDebuggerWindowBase
{
private const int MAX_DISPLAY_COUNT = 50;
private const float REFRESH_INTERVAL = 0.25f;
private const string OVERFLOW_NOTE = "Showing first 50 active timers.";
private const string EMPTY_NOTE = "No active timers.";
private const string SAMPLE_NOTE = "Zero-allocation runtime sample view.";
private struct RowView
{
public VisualElement Root;
public VisualElement LoopIndicator;
public VisualElement ScaleIndicator;
public VisualElement StateIndicator;
public VisualElement Fill;
}
private ITimerService _mTimerDebug;
private readonly TimerDebugInfo[] m_TimerInfos = new TimerDebugInfo[MAX_DISPLAY_COUNT];
private readonly RowView[] m_TimerRows = new RowView[MAX_DISPLAY_COUNT];
private VisualElement m_ActiveUsageFill;
private VisualElement m_PeakUsageFill;
private VisualElement m_FreeUsageFill;
private VisualElement m_OverflowNote;
private VisualElement m_EmptyNote;
private float m_RefreshCountdown;
public override void Initialize(params object[] args)
{
_mTimerDebug = AppServices.Require<ITimerService>();
}
public override void OnEnter()
{
m_RefreshCountdown = 0f;
RefreshContent();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
m_RefreshCountdown -= realElapseSeconds;
if (m_RefreshCountdown > 0f)
{
return;
}
m_RefreshCountdown = REFRESH_INTERVAL;
RefreshContent();
}
protected override void BuildWindow(VisualElement root)
{
if (_mTimerDebug == null)
{
return;
}
root.Add(CreateActionButton("Refresh", RefreshContent, DebuggerTheme.ButtonSurfaceActive, DebuggerTheme.PrimaryText));
VisualElement overview = CreateSection("Timer Pool Overview", out VisualElement overviewCard);
overviewCard.Add(CreateUsageRow("Active Usage", DebuggerTheme.Accent, out m_ActiveUsageFill));
overviewCard.Add(CreateUsageRow("Peak Usage", DebuggerTheme.Warning, out m_PeakUsageFill));
overviewCard.Add(CreateUsageRow("Free Capacity", DebuggerTheme.Positive, out m_FreeUsageFill));
root.Add(overview);
VisualElement sample = CreateSection("Timer Sample", out VisualElement sampleCard);
sampleCard.Add(CreateNoteLabel(SAMPLE_NOTE, DebuggerTheme.SecondaryText));
m_OverflowNote = CreateNoteLabel(OVERFLOW_NOTE, new Color(1f, 0.5f, 0f));
m_OverflowNote.style.display = DisplayStyle.None;
sampleCard.Add(m_OverflowNote);
m_EmptyNote = CreateNoteLabel(EMPTY_NOTE, DebuggerTheme.SecondaryText);
m_EmptyNote.style.display = DisplayStyle.None;
sampleCard.Add(m_EmptyNote);
for (int i = 0; i < MAX_DISPLAY_COUNT; i++)
{
m_TimerRows[i] = CreateTimerRow(sampleCard);
m_TimerRows[i].Root.style.display = DisplayStyle.None;
}
root.Add(sample);
RefreshContent();
}
private void RefreshContent()
{
if (_mTimerDebug == null || m_ActiveUsageFill == null)
{
return;
}
_mTimerDebug.GetStatistics(out int activeCount, out int poolCapacity, out int peakActiveCount, out int freeCount);
float capacity = poolCapacity > 0 ? poolCapacity : 1f;
SetFillRatio(m_ActiveUsageFill, activeCount / capacity);
SetFillRatio(m_PeakUsageFill, peakActiveCount / capacity);
SetFillRatio(m_FreeUsageFill, freeCount / capacity);
if (activeCount <= 0)
{
m_EmptyNote.style.display = DisplayStyle.Flex;
m_OverflowNote.style.display = DisplayStyle.None;
SetTimerRowsVisible(0);
return;
}
m_EmptyNote.style.display = DisplayStyle.None;
int timerCount = _mTimerDebug.GetAllTimers(m_TimerInfos);
int displayCount = timerCount > MAX_DISPLAY_COUNT ? MAX_DISPLAY_COUNT : timerCount;
m_OverflowNote.style.display = activeCount > MAX_DISPLAY_COUNT ? DisplayStyle.Flex : DisplayStyle.None;
for (int i = 0; i < displayCount; i++)
{
UpdateTimerRow(ref m_TimerRows[i], ref m_TimerInfos[i]);
}
SetTimerRowsVisible(displayCount);
}
private void SetTimerRowsVisible(int visibleCount)
{
for (int i = 0; i < MAX_DISPLAY_COUNT; i++)
{
m_TimerRows[i].Root.style.display = i < visibleCount ? DisplayStyle.Flex : DisplayStyle.None;
}
}
private static VisualElement CreateUsageRow(string title, Color fillColor, out VisualElement fill)
{
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
VisualElement row = new VisualElement();
row.style.flexDirection = FlexDirection.Column;
row.style.marginBottom = 8f * scale;
Label titleLabel = new Label(title);
titleLabel.style.color = DebuggerTheme.SecondaryText;
titleLabel.style.fontSize = 16f * scale;
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
titleLabel.style.marginBottom = 4f * scale;
row.Add(titleLabel);
VisualElement track = new VisualElement();
track.style.height = 14f * scale;
track.style.backgroundColor = DebuggerTheme.PanelSurfaceStrong;
track.style.borderTopLeftRadius = 4f * scale;
track.style.borderTopRightRadius = 4f * scale;
track.style.borderBottomLeftRadius = 4f * scale;
track.style.borderBottomRightRadius = 4f * scale;
track.style.overflow = Overflow.Hidden;
fill = new VisualElement();
fill.style.height = Length.Percent(100f);
fill.style.width = Length.Percent(0f);
fill.style.backgroundColor = fillColor;
track.Add(fill);
row.Add(track);
return row;
}
private static VisualElement CreateNoteLabel(string text, Color color)
{
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
Label label = new Label(text);
label.style.color = color;
label.style.fontSize = 15f * scale;
label.style.marginBottom = 6f * scale;
label.style.whiteSpace = WhiteSpace.Normal;
return label;
}
private static RowView CreateTimerRow(VisualElement parent)
{
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
VisualElement row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.alignItems = Align.Center;
row.style.height = 20f * scale;
row.style.marginBottom = 4f * scale;
VisualElement loopIndicator = CreateIndicator(6f * scale, DebuggerTheme.Accent);
VisualElement scaleIndicator = CreateIndicator(6f * scale, DebuggerTheme.Warning);
VisualElement stateIndicator = CreateIndicator(6f * scale, DebuggerTheme.Positive);
row.Add(loopIndicator);
row.Add(scaleIndicator);
row.Add(stateIndicator);
VisualElement track = new VisualElement();
track.style.flexGrow = 1f;
track.style.height = 14f * scale;
track.style.backgroundColor = DebuggerTheme.PanelSurfaceStrong;
track.style.borderTopLeftRadius = 4f * scale;
track.style.borderTopRightRadius = 4f * scale;
track.style.borderBottomLeftRadius = 4f * scale;
track.style.borderBottomRightRadius = 4f * scale;
track.style.overflow = Overflow.Hidden;
VisualElement fill = new VisualElement();
fill.style.height = Length.Percent(100f);
fill.style.width = Length.Percent(0f);
fill.style.backgroundColor = DebuggerTheme.Positive;
track.Add(fill);
row.Add(track);
parent.Add(row);
RowView view;
view.Root = row;
view.LoopIndicator = loopIndicator;
view.ScaleIndicator = scaleIndicator;
view.StateIndicator = stateIndicator;
view.Fill = fill;
return view;
}
private static VisualElement CreateIndicator(float size, Color color)
{
VisualElement indicator = new VisualElement();
indicator.style.width = size;
indicator.style.height = size;
indicator.style.marginRight = size;
indicator.style.backgroundColor = color;
indicator.style.opacity = 0.2f;
return indicator;
}
private static void UpdateTimerRow(ref RowView row, ref TimerDebugInfo info)
{
byte flags = info.Flags;
bool isRunning = (flags & TimerDebugFlags.Running) != 0;
bool isLoop = (flags & TimerDebugFlags.Loop) != 0;
bool isUnscaled = (flags & TimerDebugFlags.Unscaled) != 0;
float duration = info.Duration;
float ratio = duration > 0f ? info.LeftTime / duration : 0f;
if (ratio < 0f)
{
ratio = 0f;
}
else if (ratio > 1f)
{
ratio = 1f;
}
row.Root.style.display = DisplayStyle.Flex;
row.LoopIndicator.style.opacity = isLoop ? 1f : 0.2f;
row.ScaleIndicator.style.opacity = isUnscaled ? 1f : 0.35f;
row.ScaleIndicator.style.backgroundColor = isUnscaled ? DebuggerTheme.Warning : DebuggerTheme.Accent;
row.StateIndicator.style.opacity = isRunning ? 1f : 0.45f;
row.StateIndicator.style.backgroundColor = isRunning ? DebuggerTheme.Positive : DebuggerTheme.Warning;
row.Fill.style.width = Length.Percent(ratio * 100f);
row.Fill.style.backgroundColor = isRunning
? ratio <= 0.2f ? DebuggerTheme.Warning : DebuggerTheme.Positive
: DebuggerTheme.SecondaryText;
}
private static void SetFillRatio(VisualElement fill, float ratio)
{
if (ratio < 0f)
{
ratio = 0f;
}
else if (ratio > 1f)
{
ratio = 1f;
}
fill.style.width = Length.Percent(ratio * 100f);
}
}
}
}