189 lines
6.9 KiB
C#
189 lines
6.9 KiB
C#
using AlicizaX.Editor;
|
|
using AlicizaX.Timer.Runtime;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AlicizaX.Timer.Editor
|
|
{
|
|
[CustomEditor(typeof(TimerComponent))]
|
|
internal sealed class TimerComponentInspector : GameFrameworkInspector
|
|
{
|
|
private const double UPDATE_INTERVAL = 0.02d;
|
|
private const int DISPLAY_COUNT = 32;
|
|
private const int MIN_INITIAL_CAPACITY = 256;
|
|
private const int MAX_INITIAL_CAPACITY = 16384;
|
|
private const int CAPACITY_STEP = 256;
|
|
|
|
private readonly TimerDebugInfo[] _timerBuffer = new TimerDebugInfo[DISPLAY_COUNT];
|
|
private readonly TimerDebugInfo[] _staleBuffer = new TimerDebugInfo[DISPLAY_COUNT];
|
|
private double _lastUpdateTime;
|
|
private SerializedProperty _initialCapacityProperty;
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
base.OnInspectorGUI();
|
|
serializedObject.Update();
|
|
DrawConfiguration();
|
|
serializedObject.ApplyModifiedProperties();
|
|
DrawRuntimeDebugInfo();
|
|
RequestRuntimeRepaint();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
_initialCapacityProperty = serializedObject.FindProperty("_initialCapacity");
|
|
}
|
|
|
|
private void DrawConfiguration()
|
|
{
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Configuration", EditorStyles.boldLabel);
|
|
|
|
int capacity = _initialCapacityProperty.intValue;
|
|
int sliderValue = EditorGUILayout.IntSlider("Initial Capacity", capacity, MIN_INITIAL_CAPACITY, MAX_INITIAL_CAPACITY);
|
|
sliderValue = AlignCapacity(sliderValue);
|
|
if (sliderValue != capacity)
|
|
{
|
|
_initialCapacityProperty.intValue = sliderValue;
|
|
}
|
|
|
|
EditorGUILayout.HelpBox(Utility.Text.Format("Rounded by {0}. Runtime allocates timer pages during Awake/prewarm.", CAPACITY_STEP), MessageType.None);
|
|
}
|
|
|
|
private void DrawRuntimeDebugInfo()
|
|
{
|
|
if (!EditorApplication.isPlaying)
|
|
{
|
|
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (!AppServices.TryGet<ITimerService>(out ITimerService timerService))
|
|
{
|
|
EditorGUILayout.HelpBox("Timer service is not initialized.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (!(timerService is ITimerDebugService timerDebugService))
|
|
{
|
|
EditorGUILayout.HelpBox("Timer debug service is not available.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
timerDebugService.GetStatistics(out int activeCount, out int poolCapacity, out int peakActiveCount, out int freeCount);
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Runtime Debug", EditorStyles.boldLabel);
|
|
DrawStatistic("Active Timers", activeCount);
|
|
DrawStatistic("Pool Capacity", poolCapacity);
|
|
DrawStatistic("Peak Active Count", peakActiveCount);
|
|
DrawStatistic("Free Slots", freeCount);
|
|
DrawUsageBar("Active Usage", activeCount, poolCapacity);
|
|
DrawUsageBar("Peak Usage", peakActiveCount, poolCapacity);
|
|
|
|
DrawTimerList(timerDebugService, activeCount);
|
|
DrawStaleTimerList(timerDebugService, activeCount);
|
|
}
|
|
|
|
private void DrawTimerList(ITimerDebugService timerDebugService, int activeCount)
|
|
{
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Active Timer Sample", EditorStyles.boldLabel);
|
|
|
|
if (activeCount <= 0)
|
|
{
|
|
EditorGUILayout.LabelField("No active timers.");
|
|
return;
|
|
}
|
|
|
|
int timerCount = timerDebugService.GetAllTimers(_timerBuffer);
|
|
if (activeCount > DISPLAY_COUNT)
|
|
{
|
|
EditorGUILayout.HelpBox(Utility.Text.Format("Showing first {0} timers of {1}.", timerCount, activeCount), MessageType.Info);
|
|
}
|
|
|
|
for (int i = 0; i < timerCount; i++)
|
|
{
|
|
DrawTimerInfo(ref _timerBuffer[i]);
|
|
}
|
|
}
|
|
|
|
private void DrawStaleTimerList(ITimerDebugService timerDebugService, int activeCount)
|
|
{
|
|
if (activeCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!(timerDebugService is ITimerEditorDebugService editorDebugService))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int staleCount = editorDebugService.GetStaleOneShotTimers(_staleBuffer);
|
|
if (staleCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.HelpBox("Long-lived one-shot timers detected.", MessageType.Warning);
|
|
for (int i = 0; i < staleCount; i++)
|
|
{
|
|
TimerDebugInfo info = _staleBuffer[i];
|
|
EditorGUILayout.LabelField(Utility.Text.Format("ID {0}", info.TimerHandle), Utility.Text.Format("Age {0:F1}s | Left {1:F2}s", info.Age, info.LeftTime));
|
|
}
|
|
}
|
|
|
|
private static void DrawTimerInfo(ref TimerDebugInfo info)
|
|
{
|
|
byte flags = info.Flags;
|
|
string mode = (flags & TimerDebugFlags.Loop) != 0 ? "Loop" : "Once";
|
|
string scale = (flags & TimerDebugFlags.Unscaled) != 0 ? "Unscaled" : "Scaled";
|
|
string state = (flags & TimerDebugFlags.Running) != 0 ? "Running" : "Paused";
|
|
EditorGUILayout.LabelField(
|
|
Utility.Text.Format("ID {0} | {1} | {2} | {3}", info.TimerHandle, mode, scale, state),
|
|
Utility.Text.Format("Left {0:F2}s | Duration {1:F2}s", info.LeftTime, info.Duration));
|
|
}
|
|
|
|
private static void DrawStatistic(string label, int value)
|
|
{
|
|
EditorGUILayout.LabelField(label, value.ToString(), EditorStyles.boldLabel);
|
|
}
|
|
|
|
private static void DrawUsageBar(string label, int value, int capacity)
|
|
{
|
|
float ratio = capacity > 0 ? (float)value / capacity : 0f;
|
|
EditorGUILayout.Slider(label, ratio, 0f, 1f);
|
|
}
|
|
|
|
private static int AlignCapacity(int value)
|
|
{
|
|
int aligned = ((value + CAPACITY_STEP - 1) / CAPACITY_STEP) * CAPACITY_STEP;
|
|
if (aligned < MIN_INITIAL_CAPACITY)
|
|
{
|
|
return MIN_INITIAL_CAPACITY;
|
|
}
|
|
|
|
return aligned > MAX_INITIAL_CAPACITY ? MAX_INITIAL_CAPACITY : aligned;
|
|
}
|
|
|
|
private void RequestRuntimeRepaint()
|
|
{
|
|
if (!EditorApplication.isPlaying)
|
|
{
|
|
return;
|
|
}
|
|
|
|
double currentTime = EditorApplication.timeSinceStartup;
|
|
if (currentTime - _lastUpdateTime < UPDATE_INTERVAL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_lastUpdateTime = currentTime;
|
|
Repaint();
|
|
}
|
|
}
|
|
}
|