summaryrefslogtreecommitdiff
path: root/chromium/ui/latency/skipped_frame_tracker.cc
blob: fe5fc7a1737b7d9655eaa3d09059f7fb9f74eee0 (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
// Copyright 2018 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 "ui/latency/skipped_frame_tracker.h"

#include <cmath>
#include "ui/latency/frame_metrics.h"

namespace ui {

SkippedFrameTracker::SkippedFrameTracker(Client* client) : client_(client) {}

void SkippedFrameTracker::BeginFrame(base::TimeTicks frame_time,
                                     base::TimeDelta interval) {
  DCHECK(!inside_begin_frame_);
  inside_begin_frame_ = true;
  did_produce_this_frame_ = false;
  frame_time_ = frame_time;
  interval_ = interval;

  // On our first frame of activity, we may need to initialize
  // will_produce_frame_time_.
  if (active_state_ == ActiveState::WillProduceFirst &&
      will_produce_frame_time_.is_null()) {
    will_produce_frame_time_ = frame_time_;
  }
}

void SkippedFrameTracker::FinishFrame() {
  inside_begin_frame_ = false;

  // Assume the source is idle if it hasn't attempted to produce for an entire
  // BeginFrame.
  if (!did_produce_this_frame_ && active_state_ == ActiveState::WasActive) {
    will_produce_frame_time_ = base::TimeTicks();
    active_state_ = ActiveState::Idle;
  }
}

void SkippedFrameTracker::WillProduceFrame() {
  // Make sure we don't transition out of the WillProduceFirst state until
  // we've actually produced the first frame.
  if (active_state_ == ActiveState::WillProduceFirst)
    return;

  // This is our first frame of activity.
  if (active_state_ == ActiveState::Idle) {
    active_state_ = ActiveState::WillProduceFirst;
    // If we're already inside a BeginFrame when we first become active,
    // we can initialize will_produce_frame_time_.
    if (inside_begin_frame_)
      will_produce_frame_time_ = frame_time_;
    return;
  }

  active_state_ = ActiveState::WillProduce;
}

void SkippedFrameTracker::DidProduceFrame() {
  // Ignore duplicate calls to DidProduceFrame.
  if (did_produce_this_frame_)
    return;

  // Return early if frame was pulled by sink.
  bool frame_was_pushed_by_source =
      (active_state_ == ActiveState::WillProduceFirst &&
       !will_produce_frame_time_.is_null()) ||
      active_state_ == ActiveState::WillProduce;
  if (!frame_was_pushed_by_source)
    return;

  DCHECK(!will_produce_frame_time_.is_null());

  // Clamp the amount of time skipped to a positive value, since negative
  // values aren't meaningful.
  base::TimeDelta skipped_clamped =
      std::max(base::TimeDelta(), (frame_time_ - will_produce_frame_time_));

  // Snap the amount of time skipped to whole intervals in order to filter
  // out jitter in the timing received by the BeginFrame source.
  int skipped_intervals = (skipped_clamped + (interval_ / 2)) / interval_;
  base::TimeDelta skipped_snapped = skipped_intervals * interval_;

  DCHECK_GE(skipped_snapped, base::TimeDelta());
  client_->AddFrameProduced(frame_time_, interval_, skipped_snapped);

  // Predict the next BeginFrame's frame time, so we can detect if it gets
  // dropped.
  will_produce_frame_time_ = frame_time_ + interval_;
  active_state_ = ActiveState::WasActive;
  did_produce_this_frame_ = true;
}

void SkippedFrameTracker::WillNotProduceFrame() {
  if (active_state_ != ActiveState::Idle) {
    inside_begin_frame_ = false;
    did_produce_this_frame_ = false;
    will_produce_frame_time_ = base::TimeTicks();
    active_state_ = ActiveState::Idle;
  }
}

}  // namespace ui