using System; using System.Collections.Generic; using System.Diagnostics; using UnityEngine; public static class Perf { public struct Stat { public string name; public int calls; public double msTotal; public double msMax; public double MsAvg => calls > 0 ? msTotal / calls : 0; } private static readonly Dictionary _stats = new(256); // Reset chaque frame (appelé par le HUD) public static void ResetFrame() { // On garde les clés et on remet les compteurs var keys = _tempKeys; keys.Clear(); foreach (var kv in _stats) keys.Add(kv.Key); foreach (var k in keys) { var s = _stats[k]; s.calls = 0; s.msTotal = 0; s.msMax = 0; _stats[k] = s; } } private static readonly List _tempKeys = new(256); public readonly struct Scope : IDisposable { private readonly string _name; private readonly long _start; private readonly bool _enabled; public Scope(string name) { _name = name; _enabled = PerfHud.Enabled; // toggle global _start = _enabled ? Stopwatch.GetTimestamp() : 0; } public void Dispose() { if (!_enabled) return; long end = Stopwatch.GetTimestamp(); double ms = (end - _start) * 1000.0 / Stopwatch.Frequency; if (!_stats.TryGetValue(_name, out var s)) { s = new Stat { name = _name }; } s.calls += 1; s.msTotal += ms; if (ms > s.msMax) s.msMax = ms; _stats[_name] = s; } } public static Scope Measure(string name) => new Scope(name); public static List GetTop(int topN, bool sortByMax = false) { _topCache.Clear(); foreach (var kv in _stats) { // Ignore ce qui n’a pas été appelé cette frame if (kv.Value.calls > 0) _topCache.Add(kv.Value); } _topCache.Sort((a, b) => { double va = sortByMax ? a.msMax : a.msTotal; double vb = sortByMax ? b.msMax : b.msTotal; return vb.CompareTo(va); }); if (_topCache.Count > topN) _topCache.RemoveRange(topN, _topCache.Count - topN); return _topCache; } private static readonly List _topCache = new(64); }