// 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 "CCFrameRateCounter.h" #include #include "CCProxy.h" #include namespace cc { const double CCFrameRateCounter::kFrameTooFast = 1.0 / 70.0; // measured in seconds const double CCFrameRateCounter::kFrameTooSlow = 1.0 / 12.0; const double CCFrameRateCounter::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 CCFrameRateCounter::create() { return make_scoped_ptr(new CCFrameRateCounter()); } inline base::TimeDelta CCFrameRateCounter::frameInterval(int frameNumber) const { return m_timeStampHistory[frameIndex(frameNumber)] - m_timeStampHistory[frameIndex(frameNumber - 1)]; } inline int CCFrameRateCounter::frameIndex(int frameNumber) const { return safeMod(frameNumber, kTimeStampHistorySize); } CCFrameRateCounter::CCFrameRateCounter() : 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 CCFrameRateCounter::markBeginningOfFrame(base::TimeTicks timestamp) { m_timeStampHistory[frameIndex(m_currentFrameNumber)] = timestamp; base::TimeDelta frameIntervalSeconds = frameInterval(m_currentFrameNumber); if (CCProxy::hasImplThread() && m_currentFrameNumber > 0) { double drawDelayMs = frameIntervalSeconds.InMillisecondsF(); WebKit::Platform::current()->histogramCustomCounts("Renderer4.CompositorThreadImplDrawDelay", static_cast(drawDelayMs), 1, 120, 60); } if (!isBadFrameInterval(frameIntervalSeconds) && frameIntervalSeconds.InSecondsF() > kDroppedFrameTime) ++m_droppedFrameCount; } void CCFrameRateCounter::markEndOfFrame() { m_currentFrameNumber += 1; } bool CCFrameRateCounter::isBadFrameInterval(base::TimeDelta intervalBetweenConsecutiveFrames) const { bool schedulerAllowsDoubleFrames = !CCProxy::hasImplThread(); bool intervalTooFast = schedulerAllowsDoubleFrames && intervalBetweenConsecutiveFrames.InSecondsF() < kFrameTooFast; bool intervalTooSlow = intervalBetweenConsecutiveFrames.InSecondsF() > kFrameTooSlow; return intervalTooFast || intervalTooSlow; } bool CCFrameRateCounter::isBadFrame(int frameNumber) const { return isBadFrameInterval(frameInterval(frameNumber)); } void CCFrameRateCounter::getAverageFPSAndStandardDeviation(double& averageFPS, double& standardDeviation) const { int frame = m_currentFrameNumber - 1; averageFPS = 0; int averageFPSCount = 0; double fpsVarianceNumerator = 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. while (1) { if (!isBadFrame(frame)) { averageFPSCount++; base::TimeDelta secForLastFrame = m_timeStampHistory[frameIndex(frame)] - m_timeStampHistory[frameIndex(frame - 1)]; double x = 1.0 / secForLastFrame.InSecondsF(); double deltaFromAverage = x - averageFPS; // Change with caution - numerics. http://en.wikipedia.org/wiki/Standard_deviation averageFPS = averageFPS + deltaFromAverage / averageFPSCount; fpsVarianceNumerator = fpsVarianceNumerator + deltaFromAverage * (x - averageFPS); } if (averageFPSCount && isBadFrame(frame)) { // We've gathered a run of good samples, so stop. break; } --frame; if (frameIndex(frame) == frameIndex(m_currentFrameNumber) || frame < 0) { // We've gone through all available historical data, so stop. break; } } standardDeviation = sqrt(fpsVarianceNumerator / averageFPSCount); } base::TimeTicks CCFrameRateCounter::timeStampOfRecentFrame(int n) { ASSERT(n >= 0 && n < kTimeStampHistorySize); int desiredIndex = (frameIndex(m_currentFrameNumber) + n) % kTimeStampHistorySize; return m_timeStampHistory[desiredIndex]; } } // namespace cc