private void Start() { Logger = base.Logger; _showCounter = Config.Bind("General", "Toggle counter and reset stats", new KeyboardShortcut(KeyCode.U, KeyCode.LeftShift), "Key to enable and disable the plugin."); _shown = Config.Bind("General", "Enable", false, "Monitor performance statistics and show them on the screen. When disabled the plugin has no effect on performance."); _showPluginStats = Config.Bind("General", "Enable monitoring plugins", true, "Count time each plugin takes every frame to execute. Only detects MonoBehaviour event methods, so results might be lower than expected. Has a small performance penalty."); _showUnityMethodStats = Config.Bind("General", "Show detailed frame stats", true, "Show how much time was spent by Unity in each part of the frame, for example how long it took to run all Update methods."); try { var procMem = MemoryInfo.QueryProcessMemStatus(); var memorystatus = MemoryInfo.QuerySystemMemStatus(); if (procMem.WorkingSetSize <= 0 || memorystatus.ullTotalPhys <= 0) { throw new IOException("Empty data was returned"); } _measureMemory = Config.Bind("General", "Show memory and GC stats", true, "Show memory usage of the process, free available physical memory and garbage collector statistics (if available)."); } catch (Exception ex) { Logger.LogWarning("Memory statistics are not available - " + ex.Message); } _position = Config.Bind("Interface", "Screen position", TextAnchor.LowerRight, "Which corner of the screen to display the statistics in."); _counterColor = Config.Bind("Interface", "Color of the text", CounterColors.White, "Color of the displayed stats. Outline has a performance hit but it always easy to see."); _position.SettingChanged += (sender, args) => UpdateLooks(); _counterColor.SettingChanged += (sender, args) => UpdateLooks(); _shown.SettingChanged += (sender, args) => { UpdateLooks(); SetCapturingEnabled(_shown.Value); }; _showPluginStats.SettingChanged += (sender, args) => SetCapturingEnabled(_shown.Value); OnEnable(); }
private IEnumerator Start() { _measurementStopwatch = new Stopwatch(); var totalStopwatch = new Stopwatch(); var nanosecPerTick = (float)(1000 * 1000 * 100) / Stopwatch.Frequency; var msScale = 1f / (nanosecPerTick * 1000f); var gcPreviousAmount = 0L; while (true) { // Waits until right after last Update yield return(null); _updateTime.Sample(TakeMeasurement()); // Waits until right after last OnGUI yield return(_waitForEndOfFrame); // If no OnGUI was executed somehow, make sure to log the render time if (!_onGuiHit) { _renderTime.Sample(TakeMeasurement()); _onGuiHit = true; } CanProcessOnGui = false; _onGuiTime.Sample(TakeMeasurement()); // Stop until FixedUpdate so it gets counted accurately (skip other end of frame stuff) _measurementStopwatch.Reset(); // Get actual frame round-time _frameTime.Sample(totalStopwatch.ElapsedTicks); totalStopwatch.Reset(); totalStopwatch.Start(); // Calculate only once at end of frame so all data is from a single frame var avgFrame = _frameTime.GetAverage(); var fps = 1000000f / (avgFrame / nanosecPerTick); fString.Append(fps, 2, 2).Append(" FPS"); if (_showUnityMethodStats.Value) { var avgFixed = _fixedUpdateTime.GetAverage(); var avgUpdate = _updateTime.GetAverage(); var avgYield = _yieldTime.GetAverage(); var avgLate = _lateUpdateTime.GetAverage(); var avgRender = _renderTime.GetAverage(); var avgGui = _onGuiTime.GetAverage(); var totalCapturedTicks = avgFixed + avgUpdate + avgYield + avgLate + avgRender + avgGui; var otherTicks = avgFrame - totalCapturedTicks; // Print floats with 1 decimal precision i.e. XX.X and padding of 2, // meaning we assume we always get XX.X value fString.Append(", ").Append(avgFrame * msScale, 2, 2); fString.Append("ms\nFixed: ").Append(avgFixed * msScale, 2, 2); fString.Append("ms\nUpdate: ").Append(avgUpdate * msScale, 2, 2); fString.Append("ms\nYield/anim: ").Append(avgYield * msScale, 2, 2); fString.Append("ms\nLate: ").Append(avgLate * msScale, 2, 2); fString.Append("ms\nRender/VSync: ").Append(avgRender * msScale, 2, 2); fString.Append("ms\nOnGUI: ").Append(avgGui * msScale, 2, 2); fString.Append("ms\nOther: ").Append(otherTicks * msScale, 2, 2).Append("ms"); } if (_measureMemory != null && _measureMemory.Value) { var procMem = MemoryInfo.QueryProcessMemStatus(); var currentMem = procMem.WorkingSetSize / 1024 / 1024; var memorystatus = MemoryInfo.QuerySystemMemStatus(); var freeMem = memorystatus.ullAvailPhys / 1024 / 1024; fString.Append("\nRAM: ").Append((uint)currentMem).Append("MB used, "); fString.Append((uint)freeMem).Append("MB free"); var totalGcMemBytes = GC.GetTotalMemory(false); if (totalGcMemBytes != 0) { var gcDelta = totalGcMemBytes - gcPreviousAmount; var totalGcMem = totalGcMemBytes / 1024 / 1024; _gcAddedSize.Sample(gcDelta); fString.Append("\nGC: ").Append((int)totalGcMem).Append("MB ("); fString.Append(_gcAddedSize.GetAverageFloat() / 1024, 2, 4).Append("KB/s)"); //fString.Append(Mathf.RoundToInt(_gcAddedSize.GetAverage() * fps / 1024)).Append("KB/s)"); gcPreviousAmount = totalGcMemBytes; } // Check if current GC supports generations var gcGens = GC.MaxGeneration; if (gcGens > 0) { fString.Append("\nGC hits:"); for (var g = 0; g < gcGens; g++) { var collections = GC.CollectionCount(g); fString.Append(' ').Append(g).Append(':').Append(collections); } } } var plugList = PluginCounter.SlowPlugins; if (plugList != null) { if (plugList.Count > 0) { plugList.Sort(_comparer); int len = plugList.Count; for (int i = 0; i < len && i < 5; i++) { var kvav = plugList[i]; var maxName = kvav.Key.Length > 20 ? 20 : kvav.Key.Length; fString.Append("\n[").Append(kvav.Key, 0, maxName).Append(": ").Append(kvav.Value * msScale, 1, 2).Append("ms]"); } } else { fString.Append("\nNo slow plugins"); } } _frameOutputText = fString.Finalize(); _measurementStopwatch.Reset(); } }
private IEnumerator Start() { _measurementStopwatch = new Stopwatch(); var totalStopwatch = new Stopwatch(); var nanosecPerTick = (float)(1000 * 1000 * 100) / Stopwatch.Frequency; var msScale = 1f / (nanosecPerTick * 1000f); while (true) { // Waits until right after last Update yield return null; _updateTime.Sample(TakeMeasurement()); // Waits until right after last OnGUI yield return new WaitForEndOfFrame(); // If no OnGUI was executed somehow, make sure to log the render time if (!_onGuiHit) { _renderTime.Sample(TakeMeasurement()); _onGuiHit = true; } CanProcessOnGui = false; _onGuiTime.Sample(TakeMeasurement()); // Stop until FixedUpdate so it gets counted accurately (skip other end of frame stuff) _measurementStopwatch.Reset(); // Get actual frame round-time _frameTime.Sample(totalStopwatch.ElapsedTicks); totalStopwatch.Reset(); totalStopwatch.Start(); // Calculate only once at end of frame so all data is from a single frame var avgFrame = _frameTime.GetAverage(); var fps = 1000000f / (avgFrame / nanosecPerTick); // Reuse the SB to reduce amount of created garbage _outputStringBuilder.Length = 0; _outputStringBuilder.Append(fps.ToString("0.0")); _outputStringBuilder.Append(" FPS"); if (_showUnityMethodStats.Value) { var avgFixed = _fixedUpdateTime.GetAverage(); var avgUpdate = _updateTime.GetAverage(); var avgYield = _yieldTime.GetAverage(); var avgLate = _lateUpdateTime.GetAverage(); var avgRender = _renderTime.GetAverage(); var avgGui = _onGuiTime.GetAverage(); var totalCapturedTicks = avgFixed + avgUpdate + avgYield + avgLate + avgRender + avgGui; var otherTicks = avgFrame - totalCapturedTicks; // todo split into append calls to reduce GC pressure? low impact _outputStringBuilder.Append($", {avgFrame * msScale,5:0.0}ms\nFixed: {avgFixed * msScale,5:0.0}ms\nUpdate: {avgUpdate * msScale,5:0.0}ms\nYield/anim: {avgYield * msScale,5:0.0}ms\nLate: {avgLate * msScale,5:0.0}ms\nRender/VSync: {avgRender * msScale,5:0.0}ms\nOnGUI: {avgGui * msScale,5:0.0}ms\nOther: {otherTicks * msScale,5:0.0}ms"); } if (_measureMemory != null && _measureMemory.Value) { _outputStringBuilder.AppendLine(); var procMem = MemoryInfo.QueryProcessMemStatus(); var currentMem = procMem.WorkingSetSize / 1024 / 1024; _outputStringBuilder.Append($"RAM: {currentMem}MB used"); var totalGcMemBytes = GC.GetTotalMemory(false); if (totalGcMemBytes != 0) { var totalGcMem = totalGcMemBytes / 1024 / 1024; _outputStringBuilder.Append($" ({totalGcMem}MB in GC)"); } var memorystatus = MemoryInfo.QuerySystemMemStatus(); var freeMem = memorystatus.ullAvailPhys / 1024 / 1024; //var allMem = memorystatus.ullTotalPhys / 1024 / 1024; _outputStringBuilder.Append($", {freeMem}MB free"); // Check if current GC supports generations var gcGens = GC.MaxGeneration; if (gcGens > 0) { _outputStringBuilder.AppendLine(); _outputStringBuilder.Append("GC hits:"); for (var g = 0; g < gcGens; g++) { var collections = GC.CollectionCount(g); _outputStringBuilder.Append($" {g}:{collections}"); } } } if (PluginCounter.StringOutput != null) { _outputStringBuilder.AppendLine(); _outputStringBuilder.Append(PluginCounter.StringOutput); } _frameOutputText = _outputStringBuilder.ToString(); } }