summaryrefslogtreecommitdiffstats
path: root/cc/debug/frame_rate_counter.cc
blob: c43270dcf1fa51140bc432785e02bc048385d810 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 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 "cc/debug/frame_rate_counter.h"

#include <algorithm>
#include <limits>

#include "base/metrics/histogram.h"
#include "cc/trees/proxy.h"

namespace cc {

// The following constants are measured in seconds.

// Two thresholds (measured in seconds) that describe what is considered to be a
// "no-op frame" that should not be counted.
// - if the frame is too fast, then given our compositor implementation, the
// frame probably was a no-op and did not draw.
// - if the frame is too slow, then there is probably not animating content, so
// we should not pollute the average.
static const double kFrameTooFast = 1.0 / 70.0;
static const double kFrameTooSlow = 1.5;

// If a frame takes longer than this threshold (measured in seconds) then we
// (naively) assume that it missed a screen refresh; that is, we dropped a
// frame.
// TODO(brianderson): Determine this threshold based on monitor refresh rate,
// crbug.com/138642.
static const double kDroppedFrameTime = 1.0 / 50.0;

// static
scoped_ptr<FrameRateCounter> FrameRateCounter::Create(bool has_impl_thread) {
  return make_scoped_ptr(new FrameRateCounter(has_impl_thread));
}

base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const {
  DCHECK_GT(n, 0u);
  DCHECK_LT(n, ring_buffer_.BufferSize());
  return ring_buffer_.ReadBuffer(n) - ring_buffer_.ReadBuffer(n - 1);
}

FrameRateCounter::FrameRateCounter(bool has_impl_thread)
    : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {}

void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp, bool software) {
  ring_buffer_.SaveToBuffer(timestamp);

  // Check if frame interval can be computed.
  if (ring_buffer_.CurrentIndex() < 2)
    return;

  base::TimeDelta frame_interval_seconds =
      RecentFrameInterval(ring_buffer_.BufferSize() - 1);

  if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) {
    if (software) {
      UMA_HISTOGRAM_CUSTOM_COUNTS(
          "Renderer4.SoftwareCompositorThreadImplDrawDelay",
          frame_interval_seconds.InMilliseconds(),
          1,
          120,
          60);
    } else {
      UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay",
                                  frame_interval_seconds.InMilliseconds(),
                                  1,
                                  120,
                                  60);
    }
  }

  if (!IsBadFrameInterval(frame_interval_seconds) &&
      frame_interval_seconds.InSecondsF() > kDroppedFrameTime)
    dropped_frame_count_ +=
        frame_interval_seconds.InSecondsF() / kDroppedFrameTime;
}

bool FrameRateCounter::IsBadFrameInterval(
    base::TimeDelta interval_between_consecutive_frames) const {
  double delta = interval_between_consecutive_frames.InSecondsF();
  bool scheduler_allows_double_frames = !has_impl_thread_;
  bool interval_too_fast =
      scheduler_allows_double_frames ? delta < kFrameTooFast : delta <= 0.0;
  bool interval_too_slow = delta > kFrameTooSlow;
  return interval_too_fast || interval_too_slow;
}

void FrameRateCounter::GetMinAndMaxFPS(double* min_fps, double* max_fps) const {
  *min_fps = std::numeric_limits<double>::max();
  *max_fps = 0.0;

  for (RingBufferType::Iterator it = --ring_buffer_.End(); it; --it) {
    base::TimeDelta delta = RecentFrameInterval(it.index() + 1);

    if (IsBadFrameInterval(delta))
      continue;

    DCHECK_GT(delta.InSecondsF(), 0.f);
    double fps = 1.0 / delta.InSecondsF();

    *min_fps = std::min(fps, *min_fps);
    *max_fps = std::max(fps, *max_fps);
  }

  if (*min_fps > *max_fps)
    *min_fps = *max_fps;
}

double FrameRateCounter::GetAverageFPS() const {
  int frame_count = 0;
  double frame_times_total = 0.0;
  double average_fps = 0.0;

  // Walk backwards through the samples looking for a run of good frame
  // timings from which to compute the mean.
  //
  // 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.
  //
  // IsBadFrameInterval encapsulates the frame too slow/frame too fast logic.

  for (RingBufferType::Iterator it = --ring_buffer_.End();
       it && frame_times_total < 1.0;
       --it) {
    base::TimeDelta delta = RecentFrameInterval(it.index() + 1);

    if (!IsBadFrameInterval(delta)) {
      frame_count++;
      frame_times_total += delta.InSecondsF();
    } else if (frame_count) {
      break;
    }
  }

  if (frame_count) {
    DCHECK_GT(frame_times_total, 0.0);
    average_fps = frame_count / frame_times_total;
  }

  return average_fps;
}

}  // namespace cc