using System;
using Newtonsoft.Json;
using Unity.Pipeline.Commands;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
namespace Unity.Pipeline.Editor.Commands.Observability
{
///
/// Render statistics for the most recently rendered Editor frame, read from
/// . These reflect the last frame the Editor drew (Game/Scene view), not
/// an averaged measurement — treat them as a snapshot.
///
[Serializable]
public class RenderStats
{
/// Draw calls issued for the last rendered frame.
[JsonProperty("drawCalls")]
public int DrawCalls { get; set; }
/// Total batches (dynamic + static + instanced) for the last rendered frame.
[JsonProperty("batches")]
public int Batches { get; set; }
/// SetPass calls (shader pass switches) for the last rendered frame.
[JsonProperty("setPassCalls")]
public int SetPassCalls { get; set; }
/// Triangles submitted for the last rendered frame.
[JsonProperty("triangles")]
public int Triangles { get; set; }
/// Vertices submitted for the last rendered frame.
[JsonProperty("vertices")]
public int Vertices { get; set; }
}
///
/// Process memory usage read from . Values are in bytes and are always
/// available at runtime regardless of whether the Profiler window is open.
///
[Serializable]
public class MemoryStats
{
/// Total memory currently allocated by Unity (bytes).
[JsonProperty("totalAllocatedBytes")]
public long TotalAllocatedBytes { get; set; }
/// Total memory reserved by Unity (bytes).
[JsonProperty("totalReservedBytes")]
public long TotalReservedBytes { get; set; }
/// Mono managed heap memory in use (bytes).
[JsonProperty("monoUsedBytes")]
public long MonoUsedBytes { get; set; }
/// Mono managed heap size reserved (bytes).
[JsonProperty("monoHeapBytes")]
public long MonoHeapBytes { get; set; }
}
///
/// Latest CPU/GPU frame timing from . The Frame Timing Manager
/// must have a timing captured for to be true; when unavailable the
/// timing fields are left at zero.
///
[Serializable]
public class FrameTimingStats
{
/// True when a frame timing was captured and the milliseconds fields are meaningful.
[JsonProperty("available")]
public bool Available { get; set; }
/// Total CPU frame time in milliseconds.
[JsonProperty("cpuFrameTimeMs")]
public double CpuFrameTimeMs { get; set; }
/// Total GPU frame time in milliseconds.
[JsonProperty("gpuFrameTimeMs")]
public double GpuFrameTimeMs { get; set; }
/// CPU main-thread frame time in milliseconds.
[JsonProperty("cpuMainThreadFrameTimeMs")]
public double CpuMainThreadFrameTimeMs { get; set; }
}
///
/// Composite performance snapshot returned by get_performance_stats: render counters,
/// process memory, and the latest frame timing.
///
[Serializable]
public class PerformanceStats
{
/// Render counters for the last rendered Editor frame.
[JsonProperty("render")]
public RenderStats Render { get; set; }
/// Process memory usage at capture time.
[JsonProperty("memory")]
public MemoryStats Memory { get; set; }
/// Latest CPU/GPU frame timing, if one was captured.
[JsonProperty("frameTiming")]
public FrameTimingStats FrameTiming { get; set; }
}
///
/// Read-only telemetry command (CLI-198) that snapshots render, memory, and frame-timing stats so
/// an agent can reason about Editor performance without opening the Profiler or Stats overlay.
///
public static class PerformanceCommands
{
[CliCommand("get_performance_stats", "Read render, memory, and frame-timing stats (structured, read-only).")]
public static PerformanceStats GetPerformanceStats()
{
return new PerformanceStats
{
Render = new RenderStats
{
// UnityStats reflects the last frame rendered by the Editor (Game/Scene view).
DrawCalls = UnityStats.drawCalls,
Batches = UnityStats.dynamicBatches + UnityStats.staticBatches + UnityStats.instancedBatches,
SetPassCalls = UnityStats.setPassCalls,
Triangles = UnityStats.triangles,
Vertices = UnityStats.vertices
},
Memory = new MemoryStats
{
TotalAllocatedBytes = Profiler.GetTotalAllocatedMemoryLong(),
TotalReservedBytes = Profiler.GetTotalReservedMemoryLong(),
MonoUsedBytes = Profiler.GetMonoUsedSizeLong(),
MonoHeapBytes = Profiler.GetMonoHeapSizeLong()
},
FrameTiming = CaptureFrameTiming()
};
}
///
/// Capture the latest CPU/GPU frame timing. The Frame Timing Manager only yields data once a
/// timing has been captured (and platform support exists), so any failure or empty result is
/// reported as = false rather than throwing.
///
private static FrameTimingStats CaptureFrameTiming()
{
try
{
FrameTimingManager.CaptureFrameTimings();
var timings = new FrameTiming[1];
uint received = FrameTimingManager.GetLatestTimings(1, timings);
if (received > 0)
{
return new FrameTimingStats
{
Available = true,
CpuFrameTimeMs = timings[0].cpuFrameTime,
GpuFrameTimeMs = timings[0].gpuFrameTime,
CpuMainThreadFrameTimeMs = timings[0].cpuMainThreadFrameTime
};
}
}
catch
{
// Frame timing is platform-dependent and may be unsupported; report as unavailable.
}
return new FrameTimingStats { Available = false };
}
}
}