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(); 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.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(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 { ["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; } } } }