/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of OmiLAXR. */ using System.ComponentModel; using UnityEngine; namespace OmiLAXR.Context { /// /// Learning context component that tracks and monitors frame rate performance. /// Provides both instantaneous and smoothed average FPS measurements for performance analytics. /// Uses exponential moving average to provide stable average measurements without memory overhead. /// [AddComponentMenu("OmiLAXR / Scenario Context / FPS Monitor")] [DisallowMultipleComponent] [Description("Tracks Frames per Seconds.")] public sealed class FpsMonitor : LearningContext { /// /// Static reference to the singleton instance. /// private static FpsMonitor _instance; /// /// Singleton accessor for the FPS monitor instance. /// Ensures consistent performance monitoring across the application. /// public static FpsMonitor Instance => GetInstance(ref _instance); /// /// Current instantaneous frames per second measurement. /// Updated every frame based on the current frame time. /// [SerializeField, InspectorName("FPS"), ReadOnly] private int currentFPS; /// /// Smoothed average frames per second using exponential moving average. /// Provides a stable measurement that reduces noise from frame time variations. /// [SerializeField, InspectorName("Average FPS"), ReadOnly] private float averageFPS; /// /// Public accessor for the current instantaneous FPS value. /// public int CurrentFPS => currentFPS; /// /// Public accessor for the current smoothed average FPS value. /// public float CurrentAverageFPS => averageFPS; /// /// Smoothed frame delta time for calculating instantaneous FPS. /// Uses a smoothing factor to reduce noise in frame time measurements. /// private float _deltaTime; /// /// Time interval (in seconds) between average FPS updates. /// Controls how frequently the exponential moving average is recalculated. /// private float _fpsUpdateInterval = 0.5f; /// /// Timer tracking time since last average FPS update. /// private float _fpsTimer; /// /// Flag indicating if this is the first average calculation. /// Used to initialize the exponential moving average with the first measurement. /// private bool _isFirstAverage = true; /// /// Smoothing factor for exponential moving average calculation. /// Range: 0-1, where higher values give more weight to recent measurements. /// private float _smoothingFactor = 0.1f; /// /// Unity Update method called once per frame. /// Calculates instantaneous FPS and updates the smoothed average periodically. /// private void Update() { // Smooth the frame delta time to reduce noise _deltaTime += (Time.unscaledDeltaTime - _deltaTime) * 0.1f; // Calculate instantaneous FPS from smoothed delta time currentFPS = (int)(1.0f / _deltaTime); // Update timer for average FPS calculation _fpsTimer += Time.unscaledDeltaTime; // Update average FPS at specified intervals if (_fpsTimer >= _fpsUpdateInterval) { if (_isFirstAverage) { // Initialize average with first measurement averageFPS = currentFPS; _isFirstAverage = false; } else { // Calculate exponential moving average // Formula: newAvg = currentValue * alpha + oldAvg * (1 - alpha) averageFPS = currentFPS * _smoothingFactor + averageFPS * (1 - _smoothingFactor); } // Reset timer for next update _fpsTimer = 0.0f; } } } }