using Cinemachine; using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Rendering.Universal; using UnityEngine.SceneManagement; using UnityEngine.Serialization; using UnityEngine.UIElements; namespace Benchmarking { public enum DataType { FrameTime, FPS, CPUTime, CPURenderTime, GPUTime, Count, } public class PerformanceTest : MonoBehaviour { private static PerformanceTest _instance; public static PerformanceTest instance { get { if (_instance == null) _instance = FindAnyObjectByType(); return _instance; } } public bool _autoStart = true; [FormerlySerializedAs("m_Stages")] public List _stages; [SerializeField] public float _waitTime = 5f; // TODO: Remove _framesToCapture [SerializeField] public int _framesToCapture = 500; [SerializeField] [FormerlySerializedAs("m_TestDataVisualTreeReference")] private VisualTreeAsset _testDataVisualTreeReference; [SerializeField] private int _currentTimingRefreshCount = 10; private int _currentTimingRefreshCounter = 11; private FrameData _maxCurrentTiming = new FrameData(0); [SerializeField] private bool _liveRefreshGraph = true; [SerializeField] private GameObject _cameraPrefab; [SerializeField] private EventSystem _eventSystem; public void RefreshEventSystem() { EventSystem.SetUITookitEventSystemOverride(_eventSystem); } public bool liveRefreshGraph => _liveRefreshGraph; private int _currentStageIndex; private PerformanceTestStage _currentStage => _stages[_currentStageIndex]; private Camera _testCamera; public Camera testCamera { get { if (_testCamera == null) CreateCamera(); return _testCamera; } } private UIDocument _UIDocument; private TextElement _currentDataTypeLabel, _currentTimingLabel, _currentTimingUnitLabel; private Button _changeDataButtonNext, _changeDataButtonPrev, _closeButton; private static DataType _displayedDataType = DataType.FrameTime; public static DataType displayedDataType => _displayedDataType; private int _previousSleepTimeout; private int _previousTargetFrameRate = -1; private int _previousVSyncCount = 0; public void SetCurrentTiming(FrameData currentFrameData) { _maxCurrentTiming.MaxWith(currentFrameData); if (_currentTimingRefreshCounter > _currentTimingRefreshCount) { _currentTimingLabel.text = _maxCurrentTiming.GetValueString(_displayedDataType); _currentTimingLabel.style.color = GetColorForFrameData(_maxCurrentTiming); _currentTimingRefreshCounter = 0; _maxCurrentTiming = new FrameData(0); } _currentTimingRefreshCounter++; } public static bool RunningBenchmark => _instance != null; const float k_frameLineMul = 1000f, k_cpuLineMul = 400f, k_cpuRenderLineMul = 200f, k_gpuLineMul = 400f; private static Dictionary k_fpsThresholds = new() { { 30, Color.red }, { 45, Color.yellow }, { 60, Color.green }, { 90, Color.cyan }, { 120, Color.blue } }; private static Color s_worstColor = Color.red; private static Dictionary s_timingThresholds; public static Dictionary timingThresholds { get { if (s_timingThresholds == null) { s_timingThresholds = new Dictionary(); foreach (var kvp in k_fpsThresholds) { s_timingThresholds.Add(new FrameData() { frameTime = k_frameLineMul / kvp.Key, cpuTime = k_cpuLineMul / kvp.Key, cpuRenderTime = k_cpuRenderLineMul / kvp.Key, gpuTime = k_gpuLineMul / kvp.Key, }, kvp.Value); } s_worstColor = s_timingThresholds.First().Value; } return s_timingThresholds; } } public static Color GetColorForFrameData( FrameData frameData) { Color c = s_worstColor; if (_displayedDataType == DataType.FPS) { foreach (var kvp in s_timingThresholds) if (frameData.fps < kvp.Key.fps) return c; else c = kvp.Value; } else { foreach (var kvp in s_timingThresholds) if (frameData.frameTime < kvp.Key.frameTime) c = kvp.Value; } return c; } private void Awake() { //Destroy if assigned if (_instance != null) { Destroy(this); return; } _instance = this; DontDestroyOnLoad(this); var playerManager = FindObjectOfType(); if (playerManager != null) { playerManager.gameObject.SetActive(false); } UnityEngine.Cursor.lockState = CursorLockMode.None; } void Start() { Time.maximumDeltaTime = 120; Application.runInBackground = true; QualitySettings.vSyncCount = 0; _UIDocument = GetComponent(); var rootVE = _UIDocument.rootVisualElement; _currentDataTypeLabel = rootVE.Q(name: "DataTypeLabel"); _currentTimingLabel = rootVE.Q(name: "CurrentTiming"); _currentTimingUnitLabel = rootVE.Q(name: "CurrentTimingUnit"); _changeDataButtonNext = rootVE.Q