1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "cc/debug/frame_rate_counter.h" 6 7 #include <algorithm> 8 #include <limits> 9 10 #include "base/metrics/histogram.h" 11 #include "cc/trees/proxy.h" 12 13 namespace cc { 14 15 // The following constants are measured in seconds. 16 17 // Two thresholds (measured in seconds) that describe what is considered to be a 18 // "no-op frame" that should not be counted. 19 // - if the frame is too fast, then given our compositor implementation, the 20 // frame probably was a no-op and did not draw. 21 // - if the frame is too slow, then there is probably not animating content, so 22 // we should not pollute the average. 23 static const double kFrameTooFast = 1.0 / 70.0; 24 static const double kFrameTooSlow = 1.0 / 4.0; 25 26 // If a frame takes longer than this threshold (measured in seconds) then we 27 // (naively) assume that it missed a screen refresh; that is, we dropped a 28 // frame. 29 // TODO(brianderson): Determine this threshold based on monitor refresh rate, 30 // crbug.com/138642. 31 static const double kDroppedFrameTime = 1.0 / 50.0; 32 33 // static 34 scoped_ptr<FrameRateCounter> FrameRateCounter::Create(bool has_impl_thread) { 35 return make_scoped_ptr(new FrameRateCounter(has_impl_thread)); 36 } 37 38 base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const { 39 DCHECK_GT(n, 0u); 40 DCHECK_LT(n, ring_buffer_.BufferSize()); 41 return ring_buffer_.ReadBuffer(n) - ring_buffer_.ReadBuffer(n - 1); 42 } 43 44 FrameRateCounter::FrameRateCounter(bool has_impl_thread) 45 : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {} 46 47 void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) { 48 ring_buffer_.SaveToBuffer(timestamp); 49 50 // Check if frame interval can be computed. 51 if (ring_buffer_.CurrentIndex() < 2) 52 return; 53 54 base::TimeDelta frame_interval_seconds = 55 RecentFrameInterval(ring_buffer_.BufferSize() - 1); 56 57 if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) { 58 UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay", 59 frame_interval_seconds.InMilliseconds(), 60 1, 61 120, 62 60); 63 } 64 65 if (!IsBadFrameInterval(frame_interval_seconds) && 66 frame_interval_seconds.InSecondsF() > kDroppedFrameTime) 67 ++dropped_frame_count_; 68 } 69 70 bool FrameRateCounter::IsBadFrameInterval( 71 base::TimeDelta interval_between_consecutive_frames) const { 72 double delta = interval_between_consecutive_frames.InSecondsF(); 73 bool scheduler_allows_double_frames = !has_impl_thread_; 74 bool interval_too_fast = 75 scheduler_allows_double_frames ? delta < kFrameTooFast : delta <= 0.0; 76 bool interval_too_slow = delta > kFrameTooSlow; 77 return interval_too_fast || interval_too_slow; 78 } 79 80 void FrameRateCounter::GetMinAndMaxFPS(double* min_fps, double* max_fps) const { 81 *min_fps = std::numeric_limits<double>::max(); 82 *max_fps = 0.0; 83 84 for (RingBufferType::Iterator it = --ring_buffer_.End(); it; --it) { 85 base::TimeDelta delta = RecentFrameInterval(it.index() + 1); 86 87 if (IsBadFrameInterval(delta)) 88 continue; 89 90 DCHECK_GT(delta.InSecondsF(), 0.f); 91 double fps = 1.0 / delta.InSecondsF(); 92 93 *min_fps = std::min(fps, *min_fps); 94 *max_fps = std::max(fps, *max_fps); 95 } 96 97 if (*min_fps > *max_fps) 98 *min_fps = *max_fps; 99 } 100 101 double FrameRateCounter::GetAverageFPS() const { 102 int frame_count = 0; 103 double frame_times_total = 0.0; 104 double average_fps = 0.0; 105 106 // Walk backwards through the samples looking for a run of good frame 107 // timings from which to compute the mean. 108 // 109 // Slow frames occur just because the user is inactive, and should be 110 // ignored. Fast frames are ignored if the scheduler is in single-thread 111 // mode in order to represent the true frame rate in spite of the fact that 112 // the first few swapbuffers happen instantly which skews the statistics 113 // too much for short lived animations. 114 // 115 // IsBadFrameInterval encapsulates the frame too slow/frame too fast logic. 116 117 for (RingBufferType::Iterator it = --ring_buffer_.End(); 118 it && frame_times_total < 1.0; 119 --it) { 120 base::TimeDelta delta = RecentFrameInterval(it.index() + 1); 121 122 if (!IsBadFrameInterval(delta)) { 123 frame_count++; 124 frame_times_total += delta.InSecondsF(); 125 } else if (frame_count) { 126 break; 127 } 128 } 129 130 if (frame_count) { 131 DCHECK_GT(frame_times_total, 0.0); 132 average_fps = frame_count / frame_times_total; 133 } 134 135 return average_fps; 136 } 137 138 } // namespace cc 139