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;
            bool triggerPerformanceBoostChangeEvent   = false;
            var  performanceBottleneckChangeEventArgs = new PerformanceBottleneckChangeEventArgs();
            var  performanceBoostChangeEventArgs      = new PerformanceBoostChangeEventArgs();

            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) || m_AutomaticPerformanceControlChanged)
            {
                m_AutomaticPerformanceControlChanged = false;
                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;
                }
            }

            triggerPerformanceBoostChangeEvent = (PerformanceBoostChangeEvent != null) &&
                                                 (updateResult.ChangeFlags.HasFlag(Provider.Feature.CpuPerformanceBoost) ||
                                                  updateResult.ChangeFlags.HasFlag(Provider.Feature.GpuPerformanceBoost));

            // The Subsystem may have changed the current modes (e.g. "timeout" of Samsung subsystem)
            if (updateResult.ChangeFlags.HasFlag(Provider.Feature.CpuPerformanceBoost))
            {
                if (m_DevicePerfControl.CpuPerformanceBoost != updateResult.CpuPerformanceBoost)
                {
                    m_DevicePerfControl.CpuPerformanceBoost = updateResult.CpuPerformanceBoost;
                    m_RequestedCpuBoost = updateResult.CpuPerformanceBoost;
                }
            }

            if (updateResult.ChangeFlags.HasFlag(Provider.Feature.GpuPerformanceBoost))
            {
                if (m_DevicePerfControl.GpuPerformanceBoost != updateResult.GpuPerformanceBoost)
                {
                    m_DevicePerfControl.GpuPerformanceBoost = updateResult.GpuPerformanceBoost;
                    m_RequestedGpuBoost = updateResult.GpuPerformanceBoost;
                }
            }

            if (m_NewUserCpuPerformanceBoostRequest && PerformanceBoostChangeEvent != null)
            {
                m_NewUserCpuPerformanceBoostRequest = false;

                m_Subsystem.PerformanceLevelControl.EnableCpuBoost();
            }

            if (m_NewUserGpuPerformanceBoostRequest && PerformanceBoostChangeEvent != null)
            {
                m_NewUserGpuPerformanceBoostRequest = false;

                m_Subsystem.PerformanceLevelControl.EnableGpuBoost();
            }

            if (m_DevicePerfControl.Update(out levelChangeEventArgs) && PerformanceLevelChangeEvent != null)
            {
                PerformanceLevelChangeEvent.Invoke(levelChangeEventArgs);
            }

            m_PerformanceMetrics.CurrentCpuLevel = m_DevicePerfControl.CurrentCpuLevel;
            m_PerformanceMetrics.CurrentGpuLevel = m_DevicePerfControl.CurrentGpuLevel;

            m_PerformanceMetrics.CpuPerformanceBoost = m_DevicePerfControl.CpuPerformanceBoost;
            m_PerformanceMetrics.GpuPerformanceBoost = m_DevicePerfControl.GpuPerformanceBoost;

            m_NewUserPerformanceLevelRequest = false;

            if (updateResult.ChangeFlags.HasFlag(Provider.Feature.ClusterInfo))
            {
                m_PerformanceMetrics.ClusterInfo = updateResult.ClusterInfo;
            }
            // PerformanceLevelChangeEvent and BoostModeChangeEvent 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);
            }
            if (triggerPerformanceBoostChangeEvent)
            {
                performanceBoostChangeEventArgs.CpuBoost = m_DevicePerfControl.CpuPerformanceBoost;
                performanceBoostChangeEventArgs.GpuBoost = m_DevicePerfControl.GpuPerformanceBoost;
                PerformanceBoostChangeEvent(performanceBoostChangeEventArgs);
            }
        }
 private void LogBoostEvent(PerformanceBoostChangeEventArgs ev)
 {
     APLog.Debug("[perf event] CPU boost: {0}, GPU boost: {1}", ev.CpuBoost, ev.GpuBoost);
 }