Files
2026-07-13 12:49:17 +08:00

172 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using Unity.Profiling;
using Unity.Profiling.LowLevel.Unsafe;
using UnityEngine.Profiling;
using UProfiler = UnityEngine.Profiling.Profiler;
namespace MCPForUnity.Editor.Tools.Graphics
{
internal static class RenderingStatsOps
{
private static readonly (string counterName, string jsonKey)[] COUNTER_MAP = new[]
{
("Draw Calls Count", "draw_calls"),
("Batches Count", "batches"),
("SetPass Calls Count", "set_pass_calls"),
("Triangles Count", "triangles"),
("Vertices Count", "vertices"),
("Dynamic Batches Count", "dynamic_batches"),
("Dynamic Batched Draw Calls Count", "dynamic_batched_draw_calls"),
("Static Batches Count", "static_batches"),
("Static Batched Draw Calls Count", "static_batched_draw_calls"),
("Instanced Batches Count", "instanced_batches"),
("Instanced Batched Draw Calls Count", "instanced_batched_draw_calls"),
("Shadow Casters Count", "shadow_casters"),
("Render Textures Count", "render_textures"),
("Render Textures Bytes", "render_textures_bytes"),
("Used Textures Count", "used_textures"),
("Used Textures Bytes", "used_textures_bytes"),
("Render Textures Changes Count", "render_target_changes"),
("Visible Skinned Meshes Count", "visible_skinned_meshes"),
};
// === stats_get ===
internal static object GetStats(JObject @params)
{
var stats = new Dictionary<string, object>();
foreach (var (counterName, jsonKey) in COUNTER_MAP)
{
using var recorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, counterName);
stats[jsonKey] = recorder.Valid ? recorder.CurrentValue : 0;
}
return new
{
success = true,
message = "Rendering stats captured.",
data = stats
};
}
// === stats_list_counters ===
internal static object ListCounters(JObject @params)
{
var p = new ToolParams(@params);
string categoryName = p.Get("category");
// Default to "Render" category to avoid massive payloads (all categories = 300K+ chars)
ProfilerCategory category = ProfilerCategory.Render;
if (!string.IsNullOrEmpty(categoryName))
{
category = TryResolveCategory(categoryName);
}
var allHandles = new List<ProfilerRecorderHandle>();
ProfilerRecorderHandle.GetAvailable(allHandles);
var counters = allHandles
.Select(h => ProfilerRecorderHandle.GetDescription(h))
.Where(d => string.Equals(d.Category.Name, category.Name, StringComparison.OrdinalIgnoreCase))
.Select(d => new
{
name = d.Name,
category = d.Category.Name,
unit = d.UnitType.ToString()
})
.OrderBy(c => c.name).ToList();
return new
{
success = true,
message = $"Found {counters.Count} counters in category '{category.Name}'.",
data = new { counters }
};
}
// === stats_set_scene_debug_mode ===
internal static object SetSceneDebugMode(JObject @params)
{
var p = new ToolParams(@params);
string modeName = p.Get("mode");
if (string.IsNullOrEmpty(modeName))
{
var validModes = string.Join(", ", Enum.GetNames(typeof(DrawCameraMode)).Take(20));
return new ErrorResponse(
$"'mode' parameter required. Options: {validModes}");
}
if (!Enum.TryParse<DrawCameraMode>(modeName, true, out var drawMode))
{
var validModes = string.Join(", ", Enum.GetNames(typeof(DrawCameraMode)).Take(20));
return new ErrorResponse($"Unknown mode '{modeName}'. Valid: {validModes}");
}
var sceneView = SceneView.lastActiveSceneView;
if (sceneView == null)
return new ErrorResponse("No active Scene View found.");
sceneView.cameraMode = SceneView.GetBuiltinCameraMode(drawMode);
sceneView.Repaint();
return new
{
success = true,
message = $"Scene debug mode set to '{drawMode}'."
};
}
// === stats_get_memory ===
internal static object GetMemory(JObject @params)
{
var data = new Dictionary<string, object>
{
["totalAllocatedMB"] = Math.Round(UProfiler.GetTotalAllocatedMemoryLong() / (1024.0 * 1024.0), 2),
["totalReservedMB"] = Math.Round(UProfiler.GetTotalReservedMemoryLong() / (1024.0 * 1024.0), 2),
["totalUnusedReservedMB"] = Math.Round(UProfiler.GetTotalUnusedReservedMemoryLong() / (1024.0 * 1024.0), 2),
["monoUsedMB"] = Math.Round(UProfiler.GetMonoUsedSizeLong() / (1024.0 * 1024.0), 2),
["monoHeapMB"] = Math.Round(UProfiler.GetMonoHeapSizeLong() / (1024.0 * 1024.0), 2),
["graphicsDriverMB"] = Math.Round(UProfiler.GetAllocatedMemoryForGraphicsDriver() / (1024.0 * 1024.0), 2),
};
return new
{
success = true,
message = "Memory stats captured.",
data
};
}
// --- Helper: Try to resolve a ProfilerCategory by name ---
private static ProfilerCategory TryResolveCategory(string name)
{
// ProfilerCategory has static properties for well-known categories
switch (name.ToLowerInvariant())
{
case "render": return ProfilerCategory.Render;
case "scripts": return ProfilerCategory.Scripts;
case "memory": return ProfilerCategory.Memory;
case "physics": return ProfilerCategory.Physics;
case "animation": return ProfilerCategory.Animation;
case "audio": return ProfilerCategory.Audio;
case "lighting": return ProfilerCategory.Lighting;
case "network": return ProfilerCategory.Network;
case "gui": 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;
default: return ProfilerCategory.Render;
}
}
}
}