281 lines
12 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|