chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Unity.Profiling;
|
||||
using Unity.Profiling.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MCPForUnity.Editor.Tools.Profiler
|
||||
{
|
||||
internal static class CounterOps
|
||||
{
|
||||
internal static async Task<object> GetCountersAsync(JObject @params)
|
||||
{
|
||||
var p = new ToolParams(@params);
|
||||
var categoryResult = p.GetRequired("category");
|
||||
if (!categoryResult.IsSuccess)
|
||||
return new ErrorResponse(categoryResult.ErrorMessage);
|
||||
|
||||
string categoryName = categoryResult.Value;
|
||||
var resolved = ResolveCategory(categoryName, out string categoryError);
|
||||
if (resolved == null)
|
||||
return new ErrorResponse(categoryError);
|
||||
ProfilerCategory category = resolved.Value;
|
||||
|
||||
// Get counter names: explicit list or discover all in category
|
||||
var counterNames = GetRequestedCounters(p, category);
|
||||
if (counterNames.Count == 0)
|
||||
return new SuccessResponse($"No counters found in category '{categoryName}'.", new
|
||||
{
|
||||
category = categoryName,
|
||||
counters = new Dictionary<string, object>()
|
||||
});
|
||||
|
||||
// Start recorders
|
||||
var recorders = new List<ProfilerRecorder>();
|
||||
foreach (string name in counterNames)
|
||||
{
|
||||
recorders.Add(ProfilerRecorder.StartNew(category, name));
|
||||
}
|
||||
|
||||
var data = new Dictionary<string, object>();
|
||||
try
|
||||
{
|
||||
// Wait 1 frame for recorders to accumulate data
|
||||
await WaitOneFrameAsync();
|
||||
|
||||
// Read values — use GetSample(0) for last completed frame data;
|
||||
// CurrentValue is always 0 for per-frame render counters.
|
||||
for (int i = 0; i < recorders.Count; i++)
|
||||
{
|
||||
var recorder = recorders[i];
|
||||
string name = counterNames[i];
|
||||
long value = 0;
|
||||
if (recorder.Valid && recorder.Count > 0)
|
||||
value = recorder.GetSample(0).Value;
|
||||
else if (recorder.Valid)
|
||||
value = recorder.CurrentValue;
|
||||
data[name] = value;
|
||||
data[name + "_valid"] = recorder.Valid;
|
||||
data[name + "_unit"] = recorder.Valid ? recorder.UnitType.ToString() : "Unknown";
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var recorder in recorders)
|
||||
recorder.Dispose();
|
||||
}
|
||||
|
||||
return new SuccessResponse($"Captured {counterNames.Count} counter(s) from '{categoryName}'.", new
|
||||
{
|
||||
category = categoryName,
|
||||
counters = data,
|
||||
});
|
||||
}
|
||||
|
||||
private static List<string> GetRequestedCounters(ToolParams p, ProfilerCategory category)
|
||||
{
|
||||
var explicitCounters = p.GetStringArray("counters");
|
||||
if (explicitCounters != null && explicitCounters.Length > 0)
|
||||
return explicitCounters.ToList();
|
||||
|
||||
var allHandles = new List<ProfilerRecorderHandle>();
|
||||
ProfilerRecorderHandle.GetAvailable(allHandles);
|
||||
return allHandles
|
||||
.Select(h => ProfilerRecorderHandle.GetDescription(h))
|
||||
.Where(d => string.Equals(d.Category.Name, category.Name, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(d => d.Name)
|
||||
.OrderBy(n => n)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static Task WaitOneFrameAsync()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
int remaining = 2;
|
||||
|
||||
void Tick()
|
||||
{
|
||||
if (--remaining > 0) return;
|
||||
EditorApplication.update -= Tick;
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
|
||||
EditorApplication.update += Tick;
|
||||
try { EditorApplication.QueuePlayerLoopUpdate(); } catch { /* throttled editor */ }
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private static readonly string[] ValidCategories = new[]
|
||||
{
|
||||
"Render", "Scripts", "Memory", "Physics",
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
"Physics2D",
|
||||
#endif
|
||||
"Animation",
|
||||
"Audio", "Lighting", "Network", "Gui", "UI", "Ai", "Video",
|
||||
"Loading", "Input", "Vr", "Internal", "Particles", "FileIO", "VirtualTexturing"
|
||||
};
|
||||
|
||||
internal static ProfilerCategory? ResolveCategory(string name, out string error)
|
||||
{
|
||||
error = null;
|
||||
switch (name.ToLowerInvariant())
|
||||
{
|
||||
case "render": return ProfilerCategory.Render;
|
||||
case "scripts": return ProfilerCategory.Scripts;
|
||||
case "memory": return ProfilerCategory.Memory;
|
||||
case "physics": return ProfilerCategory.Physics;
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
case "physics2d": return ProfilerCategory.Physics2D;
|
||||
#endif
|
||||
case "animation": return ProfilerCategory.Animation;
|
||||
case "audio": return ProfilerCategory.Audio;
|
||||
case "lighting": return ProfilerCategory.Lighting;
|
||||
case "network": return ProfilerCategory.Network;
|
||||
case "gui": case "ui": return ProfilerCategory.Gui;
|
||||
case "ai": return ProfilerCategory.Ai;
|
||||
case "video": return ProfilerCategory.Video;
|
||||
case "loading": return ProfilerCategory.Loading;
|
||||
case "input": return ProfilerCategory.Input;
|
||||
case "vr": return ProfilerCategory.Vr;
|
||||
case "internal": return ProfilerCategory.Internal;
|
||||
case "particles": return ProfilerCategory.Particles;
|
||||
case "fileio": return ProfilerCategory.FileIO;
|
||||
case "virtualtexturing": return ProfilerCategory.VirtualTexturing;
|
||||
default:
|
||||
error = $"Unknown category '{name}'. Valid: {string.Join(", ", ValidCategories)}";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user