// Copyright 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "config.h" #include "cc/frame_rate_counter.h" #include #include "base/metrics/histogram.h" #include "cc/proxy.h" namespace cc { const double FrameRateCounter::kFrameTooFast = 1.0 / 70.0; // measured in seconds const double FrameRateCounter::kFrameTooSlow = 1.0 / 12.0; const double FrameRateCounter::kDroppedFrameTime = 1.0 / 50.0; // safeMod works on -1, returning m-1 in that case. static inline int safeMod(int number, int modulus) { return (number + modulus) % modulus; } // static scoped_ptr FrameRateCounter::create() { return make_scoped_ptr(new FrameRateCounter()); } inline base::TimeDelta FrameRateCounter::frameInterval(int frameNumber) const { return m_timeStampHistory[frameIndex(frameNumber)] - m_timeStampHistory[frameIndex(frameNumber - 1)]; } inline int FrameRateCounter::frameIndex(int frameNumber) const { return safeMod(frameNumber, kTimeStampHistorySize); } FrameRateCounter::FrameRateCounter() : m_currentFrameNumber(1) , m_droppedFrameCount(0) { m_timeStampHistory[0] = base::TimeTicks::Now(); m_timeStampHistory[1] = m_timeStampHistory[0]; for (int i = 2; i < kTimeStampHistorySize; i++) m_timeStampHistory[i] = base::TimeTicks(); } void FrameRateCounter::markBeginningOfFrame(base::TimeTicks timestamp) { m_timeStampHistory[frameIndex(m_currentFrameNumber)] = timestamp; base::TimeDelta frameIntervalSeconds = frameInterval(m_currentFrameNumber); if (Proxy::hasImplThread() && m_currentFrameNumber > 0) { HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay", frameIntervalSeconds.InMilliseconds(), 1, 120, 60); } if (!isBadFrameInterval(frameIntervalSeconds) && frameIntervalSeconds.InSecondsF() > kDroppedFrameTime) ++m_droppedFrameCount; } void FrameRateCounter::markEndOfFrame() { m_currentFrameNumber += 1; } bool FrameRateCounter::isBadFrameInterval(base::TimeDelta intervalBetweenConsecutiveFrames) const { double delta = intervalBetweenConsecutiveFrames.InSecondsF(); bool schedulerAllowsDoubleFrames = !Proxy::hasImplThread(); bool intervalTooFast = schedulerAllowsDoubleFrames ? delta < kFrameTooFast : delta <= 0.0; bool intervalTooSlow = delta > kFrameTooSlow; return intervalTooFast || intervalTooSlow; } bool FrameRateCounter::isBadFrame(int frameNumber) const { return isBadFrameInterval(frameInterval(frameNumber)); } void FrameRateCounter::getAverageFPSAndStandardDeviation(double& averageFPS, double& standardDeviation) const { int frameNumber = m_currentFrameNumber - 1; int frameCount = 0; double fpsVarianceNumerator = 0; averageFPS = 0; standardDeviation = 0; // Walk backwards through the samples looking for a run of good frame // timings from which to compute the mean and standard deviation. // // Slow frames occur just because the user is inactive, and should be // ignored. Fast frames are ignored if the scheduler is in single-thread // mode in order to represent the true frame rate in spite of the fact that // the first few swapbuffers happen instantly which skews the statistics // too much for short lived animations. // // isBadFrame encapsulates the frame too slow/frame too fast logic. // Go through all available historical data. while (frameIndex(frameNumber) != frameIndex(m_currentFrameNumber) && frameNumber >= 0) { base::TimeDelta delta = frameInterval(frameNumber); if (!isBadFrameInterval(delta)) { frameCount++; double x = 1.0 / delta.InSecondsF(); double deltaFromAverage = x - averageFPS; // Change with caution - numerics. http://en.wikipedia.org/wiki/Standard_deviation averageFPS += deltaFromAverage / frameCount; fpsVarianceNumerator += deltaFromAverage * (x - averageFPS); } else if (frameCount) // We've gathered a run of good samples, so stop. break; frameNumber--; } if (frameCount) standardDeviation = sqrt(fpsVarianceNumerator / frameCount); } base::TimeTicks FrameRateCounter::timeStampOfRecentFrame(int n) const { DCHECK(n >= 0); DCHECK(n < kTimeStampHistorySize); int desiredIndex = (frameIndex(m_currentFrameNumber) + n) % kTimeStampHistorySize; return m_timeStampHistory[desiredIndex]; } } // namespace cc