void SetBottleneck(PerformanceBottleneck performanceBottleneck, SimulatorAdaptivePerformanceSubsystem subsystem) { if (subsystem == null) { return; } var targetFrameRate = Application.targetFrameRate; // default target framerate is -1 to use default platform framerate so we assume it's 60 if (targetFrameRate == -1) { targetFrameRate = 60; } var currentTargetFramerateHalfMS = 1.0f / targetFrameRate / 2.0f; var currentTargetFramerateMS = 1.0f / targetFrameRate; switch (performanceBottleneck) { case PerformanceBottleneck.CPU: // averageOverallFrametime > targetFramerate && averageCpuFrametime >= averageOverallFrametime subsystem.NextCpuFrameTime = currentTargetFramerateMS + 0.001f; subsystem.NextGpuFrameTime = currentTargetFramerateHalfMS; subsystem.NextOverallFrameTime = currentTargetFramerateMS + 0.001f; break; case PerformanceBottleneck.GPU: // averageOverallFrametime > targetFramerate && averageGpuFrametime >= averageOverallFrametime subsystem.NextCpuFrameTime = currentTargetFramerateHalfMS; subsystem.NextGpuFrameTime = currentTargetFramerateMS + 0.001f; subsystem.NextOverallFrameTime = currentTargetFramerateMS + 0.001f; break; case PerformanceBottleneck.TargetFrameRate: // averageOverallFrametime == targetFramerate subsystem.NextCpuFrameTime = currentTargetFramerateHalfMS; subsystem.NextGpuFrameTime = currentTargetFramerateHalfMS; subsystem.NextOverallFrameTime = currentTargetFramerateMS; break; //PerformanceBottleneck.Unknowe - averageOverallFrametime > targetFramerate default: subsystem.NextCpuFrameTime = currentTargetFramerateHalfMS; subsystem.NextGpuFrameTime = currentTargetFramerateHalfMS; subsystem.NextOverallFrameTime = currentTargetFramerateMS + 0.001f; break; } }
IEnumerator NextBottleneck(bool timeout = false) { switching = true; stateChangeIterations--; timeOuttimer = timeOut; Debug.Log($"State change = {stateChangeIterations}"); switch (targetBottleneck) { case PerformanceBottleneck.CPU: yield return(StartCoroutine(LogResult("GPU", timeout))); factory.FlushObjects(); factory.RunTest = true; state = "Ramping up GPU Load"; factory.prefab = gpuLoader; factory.spawnAmount = 0.1f; targetBottleneck = PerformanceBottleneck.GPU; break; case PerformanceBottleneck.GPU: yield return(StartCoroutine(LogResult("TargetFrameRate", timeout))); factory.FlushObjects(); factory.RunTest = false; state = "Waiting for TargetFrameRate"; targetBottleneck = PerformanceBottleneck.TargetFrameRate; break; case PerformanceBottleneck.TargetFrameRate: yield return(StartCoroutine(LogResult("CPU", timeout))); factory.FlushObjects(); factory.RunTest = true; state = "Ramping up CPU Load"; factory.prefab = cpuLoader; factory.spawnAmount = 1; targetBottleneck = PerformanceBottleneck.CPU; break; } switching = false; }
void StartTest(PerformanceBottleneck target) { LogResult(); factory.FlushObjects(); switch (target) { case PerformanceBottleneck.GPU: factory.RunTest = true; factory.prefab = gpuLoader; factory.spawnAmount = 50f; factory.LimitCount = 50; targetBottleneck = PerformanceBottleneck.GPU; testRunning = true; break; case PerformanceBottleneck.TargetFrameRate: factory.RunTest = false; state = ""; targetBottleneck = PerformanceBottleneck.TargetFrameRate; testRunning = false; break; case PerformanceBottleneck.CPU: factory.RunTest = true; factory.prefab = cpuLoader; factory.spawnAmount = 1; factory.LimitCount = 2000; targetBottleneck = PerformanceBottleneck.CPU; testRunning = true; break; } state = $"Changed to {target} load"; watch.Reset(); watch.Start(); LogResult(); }
void Start() { factory = FindObjectOfType <SampleFactory>(); bottleneckUI = FindObjectOfType <BottleneckUI>(); factory.RunTest = false; ap = Holder.Instance; if (ap == null || !ap.Active) { state = "Adaptive Performance not active"; Debug.Log("[AP Boost] Adaptive Performance not active"); return; } else { watch.Start(); state = "Waiting on Load"; targetBottleneck = PerformanceBottleneck.TargetFrameRate; Debug.LogFormat("[AP Boost] Starting Test Timestamp : {0} s , Label : {1} , Objects : {2} \n", watch.ElapsedMilliseconds / 1000f, state, factory.internalObjs); } bottleneckStatus.text = state; ap.PerformanceStatus.PerformanceBoostChangeEvent += OnBoostModeEvent; }
private void UpdateSubsystem() { Provider.PerformanceDataRecord updateResult = m_Subsystem.Update(); m_PerformanceMetrics.CurrentCpuLevel = updateResult.CpuPerformanceLevel; m_PerformanceMetrics.CurrentGpuLevel = updateResult.GpuPerformanceLevel; m_ThermalMetrics.WarningLevel = updateResult.WarningLevel; m_ThermalMetrics.TemperatureLevel = updateResult.TemperatureLevel; if (!m_JustResumed) { // Update overall frame time m_OverallFrameTime.AddValue(m_Subsystem.Capabilities.HasFlag(Provider.Feature.OverallFrameTime) ? updateResult.OverallFrameTime : Time.unscaledDeltaTime); AddNonNegativeValue(m_GpuFrameTime, updateResult.GpuFrameTime); AddNonNegativeValue(m_CpuFrameTime, m_CpuFrameTimeProvider != null ? m_CpuFrameTimeProvider.CpuFrameTime : updateResult.CpuFrameTime); m_TemperatureTrend.Update(updateResult.TemperatureTrend, updateResult.TemperatureLevel, updateResult.ChangeFlags.HasFlag(Provider.Feature.TemperatureLevel), Time.time); } else { m_TemperatureTrend.Reset(updateResult.TemperatureTrend, updateResult.TemperatureLevel, Time.time); m_JustResumed = false; } m_ThermalMetrics.TemperatureTrend = m_TemperatureTrend.ThermalTrend; // Update frame timing info and calculate performance bottleneck m_FrameTiming.AverageFrameTime = m_OverallFrameTime.GetAverage(); m_FrameTiming.CurrentFrameTime = m_OverallFrameTime.GetMostRecentValue(); m_FrameTiming.AverageGpuFrameTime = m_GpuFrameTime.GetAverage(); m_FrameTiming.CurrentGpuFrameTime = m_GpuFrameTime.GetMostRecentValue(); m_FrameTiming.AverageCpuFrameTime = m_CpuFrameTime.GetAverage(); m_FrameTiming.CurrentCpuFrameTime = m_CpuFrameTime.GetMostRecentValue(); float targerFrameRate = EffectiveTargetFrameRate(); float targetFrameTime = -1.0f; if (targerFrameRate > 0) { targetFrameTime = 1.0f / targerFrameRate; } if (m_OverallFrameTime.GetNumValues() == m_OverallFrameTime.GetSampleWindowSize() && m_GpuFrameTime.GetNumValues() == m_GpuFrameTime.GetSampleWindowSize()) { PerformanceBottleneck bottleneck = BottleneckUtil.DetermineBottleneck(m_PerformanceMetrics.PerformanceBottleneck, m_FrameTiming.AverageCpuFrameTime, m_FrameTiming.AverageGpuFrameTime, m_FrameTiming.AverageFrameTime, targetFrameTime); if (bottleneck != m_PerformanceMetrics.PerformanceBottleneck) { m_PerformanceMetrics.PerformanceBottleneck = bottleneck; var args = new PerformanceBottleneckChangeEventArgs(); args.PerformanceBottleneck = bottleneck; if (PerformanceBottleneckChangeEvent != null) { PerformanceBottleneckChangeEvent.Invoke(args); } } } if (updateResult.ChangeFlags.HasFlag(Provider.Feature.WarningLevel) || updateResult.ChangeFlags.HasFlag(Provider.Feature.TemperatureLevel) || updateResult.ChangeFlags.HasFlag(Provider.Feature.TemperatureTrend)) { if (ThermalEvent != null) { ThermalEvent.Invoke(m_ThermalMetrics); } } // Update PerformanceControlMode if (updateResult.ChangeFlags.HasFlag(Provider.Feature.PerformanceLevelControl)) { if (updateResult.PerformanceLevelControlAvailable) { if (AutomaticPerformanceControl) { m_DevicePerfControl.PerformanceControlMode = PerformanceControlMode.Automatic; } else { m_DevicePerfControl.PerformanceControlMode = PerformanceControlMode.Manual; } } else { m_DevicePerfControl.PerformanceControlMode = PerformanceControlMode.System; } } // Apply performance levels according to PerformanceControlMode m_AutoPerformanceLevelController.TargetFrameTime = targetFrameTime; m_AutoPerformanceLevelController.Enabled = (m_DevicePerfControl.PerformanceControlMode == PerformanceControlMode.Automatic); PerformanceLevelChangeEventArgs levelChangeEventArgs = new PerformanceLevelChangeEventArgs(); if (m_DevicePerfControl.PerformanceControlMode != PerformanceControlMode.System) { if (m_AutoPerformanceLevelController.Enabled) { if (m_NewUserPerformanceLevelRequest) { m_AutoPerformanceLevelController.Override(m_RequestedCpuLevel, m_RequestedGpuLevel); levelChangeEventArgs.ManualOverride = true; } m_AutoPerformanceLevelController.Update(); } else { m_DevicePerfControl.CpuLevel = m_RequestedCpuLevel; m_DevicePerfControl.GpuLevel = m_RequestedGpuLevel; } } if (m_DevicePerfControl.Update(out levelChangeEventArgs) && PerformanceLevelChangeEvent != null) { PerformanceLevelChangeEvent.Invoke(levelChangeEventArgs); } m_PerformanceMetrics.CurrentCpuLevel = m_DevicePerfControl.CurrentCpuLevel; m_PerformanceMetrics.CurrentGpuLevel = m_DevicePerfControl.CurrentGpuLevel; m_NewUserPerformanceLevelRequest = false; }
private void UpdateSubsystem() { Provider.PerformanceDataRecord updateResult = m_Subsystem.Update(); m_ThermalMetrics.WarningLevel = updateResult.WarningLevel; m_ThermalMetrics.TemperatureLevel = updateResult.TemperatureLevel; if (!m_JustResumed) { // Update overall frame time if (!m_UseProviderOverallFrameTime) { AccumulateTimingValue(ref m_OverallFrameTimeAccu, Time.unscaledDeltaTime); } if (WillCurrentFrameRender()) { AddNonNegativeValue(m_OverallFrameTime, m_UseProviderOverallFrameTime ? updateResult.OverallFrameTime : m_OverallFrameTimeAccu); AddNonNegativeValue(m_GpuFrameTime, m_GpuFrameTimeProvider == null ? updateResult.GpuFrameTime : m_GpuFrameTimeProvider.GpuFrameTime); AddNonNegativeValue(m_CpuFrameTime, m_CpuFrameTimeProvider == null ? updateResult.CpuFrameTime : m_CpuFrameTimeProvider.CpuFrameTime); m_OverallFrameTimeAccu = 0.0f; } m_TemperatureTrend.Update(updateResult.TemperatureTrend, updateResult.TemperatureLevel, updateResult.ChangeFlags.HasFlag(Provider.Feature.TemperatureLevel), Time.time); } else { m_TemperatureTrend.Reset(); m_JustResumed = false; } m_ThermalMetrics.TemperatureTrend = m_TemperatureTrend.ThermalTrend; // Update frame timing info and calculate performance bottleneck const float invalidTimingValue = -1.0f; m_FrameTiming.AverageFrameTime = m_OverallFrameTime.GetAverageOr(invalidTimingValue); m_FrameTiming.CurrentFrameTime = m_OverallFrameTime.GetMostRecentValueOr(invalidTimingValue); m_FrameTiming.AverageGpuFrameTime = m_GpuFrameTime.GetAverageOr(invalidTimingValue); m_FrameTiming.CurrentGpuFrameTime = m_GpuFrameTime.GetMostRecentValueOr(invalidTimingValue); m_FrameTiming.AverageCpuFrameTime = m_CpuFrameTime.GetAverageOr(invalidTimingValue); m_FrameTiming.CurrentCpuFrameTime = m_CpuFrameTime.GetMostRecentValueOr(invalidTimingValue); float targerFrameRate = EffectiveTargetFrameRate(); float targetFrameTime = -1.0f; if (targerFrameRate > 0) { targetFrameTime = 1.0f / targerFrameRate; } bool triggerPerformanceBottleneckChangeEvent = false; bool triggerThermalEventEvent = false; var performanceBottleneckChangeEventArgs = new PerformanceBottleneckChangeEventArgs(); if (m_OverallFrameTime.GetNumValues() == m_OverallFrameTime.GetSampleWindowSize() && m_GpuFrameTime.GetNumValues() == m_GpuFrameTime.GetSampleWindowSize() && m_CpuFrameTime.GetNumValues() == m_CpuFrameTime.GetSampleWindowSize()) { PerformanceBottleneck bottleneck = BottleneckUtil.DetermineBottleneck(m_PerformanceMetrics.PerformanceBottleneck, m_FrameTiming.AverageCpuFrameTime, m_FrameTiming.AverageGpuFrameTime, m_FrameTiming.AverageFrameTime, targetFrameTime); if (bottleneck != m_PerformanceMetrics.PerformanceBottleneck) { m_PerformanceMetrics.PerformanceBottleneck = bottleneck; performanceBottleneckChangeEventArgs.PerformanceBottleneck = bottleneck; triggerPerformanceBottleneckChangeEvent = (PerformanceBottleneckChangeEvent != null); } } triggerThermalEventEvent = (ThermalEvent != null) && (updateResult.ChangeFlags.HasFlag(Provider.Feature.WarningLevel) || updateResult.ChangeFlags.HasFlag(Provider.Feature.TemperatureLevel) || updateResult.ChangeFlags.HasFlag(Provider.Feature.TemperatureTrend)); // The Subsystem may have changed the current levels (e.g. "timeout" of Samsung subsystem) if (updateResult.ChangeFlags.HasFlag(Provider.Feature.CpuPerformanceLevel)) { m_DevicePerfControl.CurrentCpuLevel = updateResult.CpuPerformanceLevel; } if (updateResult.ChangeFlags.HasFlag(Provider.Feature.GpuPerformanceLevel)) { m_DevicePerfControl.CurrentGpuLevel = updateResult.GpuPerformanceLevel; } // Update PerformanceControlMode if (updateResult.ChangeFlags.HasFlag(Provider.Feature.PerformanceLevelControl)) { if (updateResult.PerformanceLevelControlAvailable) { if (AutomaticPerformanceControl) { m_DevicePerfControl.PerformanceControlMode = PerformanceControlMode.Automatic; } else { m_DevicePerfControl.PerformanceControlMode = PerformanceControlMode.Manual; } } else { m_DevicePerfControl.PerformanceControlMode = PerformanceControlMode.System; } } // Apply performance levels according to PerformanceControlMode m_AutoPerformanceLevelController.TargetFrameTime = targetFrameTime; m_AutoPerformanceLevelController.Enabled = (m_DevicePerfControl.PerformanceControlMode == PerformanceControlMode.Automatic); PerformanceLevelChangeEventArgs levelChangeEventArgs = new PerformanceLevelChangeEventArgs(); if (m_DevicePerfControl.PerformanceControlMode != PerformanceControlMode.System) { if (m_AutoPerformanceLevelController.Enabled) { if (m_NewUserPerformanceLevelRequest) { m_AutoPerformanceLevelController.Override(m_RequestedCpuLevel, m_RequestedGpuLevel); levelChangeEventArgs.ManualOverride = true; } m_AutoPerformanceLevelController.Update(); } else { m_DevicePerfControl.CpuLevel = m_RequestedCpuLevel; m_DevicePerfControl.GpuLevel = m_RequestedGpuLevel; } } if (m_DevicePerfControl.Update(out levelChangeEventArgs) && PerformanceLevelChangeEvent != null) { PerformanceLevelChangeEvent.Invoke(levelChangeEventArgs); } m_PerformanceMetrics.CurrentCpuLevel = m_DevicePerfControl.CurrentCpuLevel; m_PerformanceMetrics.CurrentGpuLevel = m_DevicePerfControl.CurrentGpuLevel; m_NewUserPerformanceLevelRequest = false; // PerformanceLevelChangeEvent triggers before those since it's useful for the user to know when the auto cpu/gpu level controller already made adjustments if (triggerThermalEventEvent) { ThermalEvent.Invoke(m_ThermalMetrics); } if (triggerPerformanceBottleneckChangeEvent) { PerformanceBottleneckChangeEvent(performanceBottleneckChangeEventArgs); } }
public static PerformanceBottleneck DetermineBottleneck(PerformanceBottleneck prevBottleneck, float averageCpuFrameTime, float averageGpuFrametime, float averageOverallFrametime, float targetFrameTime) { if (HittingFrameRateLimit(averageOverallFrametime, prevBottleneck == PerformanceBottleneck.TargetFrameRate ? 0.03f : 0.02f, targetFrameTime)) { return(PerformanceBottleneck.TargetFrameRate); } if (averageGpuFrametime >= averageOverallFrametime) { // GPU is active all the time? It's probably the bottleneck return(PerformanceBottleneck.GPU); } else if (averageCpuFrameTime >= averageOverallFrametime) { return(PerformanceBottleneck.CPU); } else { bool wasGpuBound = prevBottleneck == PerformanceBottleneck.GPU; bool wasCpuBound = prevBottleneck == PerformanceBottleneck.CPU; float gpuUtilization = averageGpuFrametime / averageOverallFrametime; float cpuUtilization = averageCpuFrameTime / averageOverallFrametime; // very high main thread CPU time => most likely CPU bound float highCpuUtilThreshold = wasCpuBound ? 0.87f : 0.90f; if (cpuUtilization > highCpuUtilThreshold) { return(PerformanceBottleneck.CPU); } // GPU is active almost all the time? It's probably the bottleneck float highGpuUtilThreshold = wasGpuBound ? 0.87f : 0.90f; if (averageGpuFrametime > highGpuUtilThreshold) { return(PerformanceBottleneck.GPU); } if (averageGpuFrametime > averageCpuFrameTime) { // higher GPU time compared to CPU time? => might be GPU bound // but we can only be somewhat sure if we have relatively high GPU utilization float gpuUtilizationThreshold = wasGpuBound ? 0.7f : 0.72f; if (gpuUtilization > gpuUtilizationThreshold) { // significantly higher GPU time compared to CPU time? float gpuFactor = wasGpuBound ? 0.72f : 0.70f; if (averageGpuFrametime * gpuFactor > averageCpuFrameTime) { return(PerformanceBottleneck.GPU); } } } else { float cpuUtilizationThreshold = wasCpuBound ? 0.5f : 0.52f; if (cpuUtilization > cpuUtilizationThreshold && averageGpuFrametime < averageCpuFrameTime) { // higher CPU time compared to GPU time? float cpuFactor = wasCpuBound ? 0.85f : 0.80f; if (averageCpuFrameTime * cpuFactor > averageGpuFrametime) { return(PerformanceBottleneck.CPU); } } } } return(PerformanceBottleneck.Unknown); }