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, bool software) { 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 if (software) { 59 UMA_HISTOGRAM_CUSTOM_COUNTS( 60 "Renderer4.SoftwareCompositorThreadImplDrawDelay", 61 frame_interval_seconds.InMilliseconds(), 62 1, 63 120, 64 60); 65 } else { 66 UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay", 67 frame_interval_seconds.InMilliseconds(), 68 1, 69 120, 70 60); 71 } 72 } 73 74 if (!IsBadFrameInterval(frame_interval_seconds) && 75 frame_interval_seconds.InSecondsF() > kDroppedFrameTime) 76 dropped_frame_count_ += 77 frame_interval_seconds.InSecondsF() / kDroppedFrameTime; 78 } 79 80 bool FrameRateCounter::IsBadFrameInterval( 81 base::TimeDelta interval_between_consecutive_frames) const { 82 double delta = interval_between_consecutive_frames.InSecondsF(); 83 bool scheduler_allows_double_frames = !has_impl_thread_; 84 bool interval_too_fast = 85 scheduler_allows_double_frames ? delta < kFrameTooFast : delta <= 0.0; 86 bool interval_too_slow = delta > kFrameTooSlow; 87 return interval_too_fast || interval_too_slow; 88 } 89 90 void FrameRateCounter::GetMinAndMaxFPS(double* min_fps, double* max_fps) const { 91 *min_fps = std::numeric_limits<double>::max(); 92 *max_fps = 0.0; 93 94 for (RingBufferType::Iterator it = --ring_buffer_.End(); it; --it) { 95 base::TimeDelta delta = RecentFrameInterval(it.index() + 1); 96 97 if (IsBadFrameInterval(delta)) 98 continue; 99 100 DCHECK_GT(delta.InSecondsF(), 0.f); 101 double fps = 1.0 / delta.InSecondsF(); 102 103 *min_fps = std::min(fps, *min_fps); 104 *max_fps = std::max(fps, *max_fps); 105 } 106 107 if (*min_fps > *max_fps) 108 *min_fps = *max_fps; 109 } 110 111 double FrameRateCounter::GetAverageFPS() const { 112 int frame_count = 0; 113 double frame_times_total = 0.0; 114 double average_fps = 0.0; 115 116 // Walk backwards through the samples looking for a run of good frame 117 // timings from which to compute the mean. 118 // 119 // Slow frames occur just because the user is inactive, and should be 120 // ignored. Fast frames are ignored if the scheduler is in single-thread 121 // mode in order to represent the true frame rate in spite of the fact that 122 // the first few swapbuffers happen instantly which skews the statistics 123 // too much for short lived animations. 124 // 125 // IsBadFrameInterval encapsulates the frame too slow/frame too fast logic. 126 127 for (RingBufferType::Iterator it = --ring_buffer_.End(); 128 it && frame_times_total < 1.0; 129 --it) { 130 base::TimeDelta delta = RecentFrameInterval(it.index() + 1); 131 132 if (!IsBadFrameInterval(delta)) { 133 frame_count++; 134 frame_times_total += delta.InSecondsF(); 135 } else if (frame_count) { 136 break; 137 } 138 } 139 140 if (frame_count) { 141 DCHECK_GT(frame_times_total, 0.0); 142 average_fps = frame_count / frame_times_total; 143 } 144 145 return average_fps; 146 } 147 148 } // namespace cc 149