1 // Copyright 2014 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 "components/viz/common/frame_sinks/begin_frame_source.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12 
13 #include "base/atomic_sequence_num.h"
14 #include "base/auto_reset.h"
15 #include "base/check_op.h"
16 #include "base/location.h"
17 #include "base/notreached.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/trace_event/trace_event.h"
22 #include "base/trace_event/traced_value.h"
23 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
24 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
25 
26 namespace viz {
27 
28 namespace {
29 // kDoubleTickDivisor prevents the SyntheticBFS from sending BeginFrames too
30 // often to an observer.
31 constexpr double kDoubleTickDivisor = 2.0;
32 
33 base::AtomicSequenceNumber g_next_source_id;
34 
35 // Generates a source_id with upper 32 bits from |restart_id| and lower 32 bits
36 // from an atomic sequence.
GenerateSourceId(uint32_t restart_id)37 uint64_t GenerateSourceId(uint32_t restart_id) {
38   return static_cast<uint64_t>(restart_id) << 32 | g_next_source_id.GetNext();
39 }
40 
41 // Notifies the observer of the BeginFrame. If the BeginFrame is a
42 // animate_only BeginFrame, the observer may not be notified of the
43 // BeginFrame.
FilterAndIssueBeginFrame(BeginFrameObserver * observer,const BeginFrameArgs & args)44 void FilterAndIssueBeginFrame(BeginFrameObserver* observer,
45                               const BeginFrameArgs& args) {
46   if (args.animate_only && !observer->WantsAnimateOnlyBeginFrames())
47     return;
48   observer->OnBeginFrame(args);
49 }
50 
51 // Checks |args| for continuity with our last args.  It is possible that the
52 // source in which |args| originate changes, or that our hookup to this source
53 // changes, so we have to check for continuity.  See also
54 // https://crbug.com/690127 for what may happen without this check.
CheckBeginFrameContinuity(BeginFrameObserver * observer,const BeginFrameArgs & args)55 bool CheckBeginFrameContinuity(BeginFrameObserver* observer,
56                                const BeginFrameArgs& args) {
57   const BeginFrameArgs& last_args = observer->LastUsedBeginFrameArgs();
58   if (!last_args.IsValid() || (args.frame_time > last_args.frame_time)) {
59     DCHECK(!last_args.frame_id.IsNextInSequenceTo(args.frame_id))
60         << "current " << args.ToString() << ", last " << last_args.ToString();
61     return true;
62   }
63   return false;
64 }
65 }  // namespace
66 
67 // BeginFrameObserver -----------------------------------------------------
IsRoot() const68 bool BeginFrameObserver::IsRoot() const {
69   return false;
70 }
71 
72 // BeginFrameObserverBase -------------------------------------------------
73 BeginFrameObserverBase::BeginFrameObserverBase() = default;
74 
75 BeginFrameObserverBase::~BeginFrameObserverBase() = default;
76 
LastUsedBeginFrameArgs() const77 const BeginFrameArgs& BeginFrameObserverBase::LastUsedBeginFrameArgs() const {
78   return last_begin_frame_args_;
79 }
80 
WantsAnimateOnlyBeginFrames() const81 bool BeginFrameObserverBase::WantsAnimateOnlyBeginFrames() const {
82   return wants_animate_only_begin_frames_;
83 }
84 
OnBeginFrame(const BeginFrameArgs & args)85 void BeginFrameObserverBase::OnBeginFrame(const BeginFrameArgs& args) {
86   DCHECK(args.IsValid());
87   DCHECK_GE(args.frame_time, last_begin_frame_args_.frame_time);
88   DCHECK(!last_begin_frame_args_.frame_id.IsNextInSequenceTo(args.frame_id))
89       << "current " << args.ToString() << ", last "
90       << last_begin_frame_args_.ToString();
91   bool used = OnBeginFrameDerivedImpl(args);
92   if (used) {
93     last_begin_frame_args_ = args;
94   } else {
95     ++dropped_begin_frame_args_;
96   }
97 }
98 
AsProtozeroInto(perfetto::protos::pbzero::BeginFrameObserverState * state) const99 void BeginFrameObserverBase::AsProtozeroInto(
100     perfetto::protos::pbzero::BeginFrameObserverState* state) const {
101   state->set_dropped_begin_frame_args(dropped_begin_frame_args_);
102 
103   last_begin_frame_args_.AsProtozeroInto(state->set_last_begin_frame_args());
104 }
105 
106 BeginFrameArgs
GenerateBeginFrameArgs(uint64_t source_id,base::TimeTicks frame_time,base::TimeTicks next_frame_time,base::TimeDelta vsync_interval)107 BeginFrameSource::BeginFrameArgsGenerator::GenerateBeginFrameArgs(
108     uint64_t source_id,
109     base::TimeTicks frame_time,
110     base::TimeTicks next_frame_time,
111     base::TimeDelta vsync_interval) {
112   uint64_t sequence_number =
113       next_sequence_number_ +
114       EstimateTickCountsBetween(frame_time, next_expected_frame_time_,
115                                 vsync_interval);
116   next_expected_frame_time_ = next_frame_time;
117   next_sequence_number_ = sequence_number + 1;
118   return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, source_id,
119                                 sequence_number, frame_time, next_frame_time,
120                                 vsync_interval, BeginFrameArgs::NORMAL);
121 }
122 
EstimateTickCountsBetween(base::TimeTicks frame_time,base::TimeTicks next_expected_frame_time,base::TimeDelta vsync_interval)123 uint64_t BeginFrameSource::BeginFrameArgsGenerator::EstimateTickCountsBetween(
124     base::TimeTicks frame_time,
125     base::TimeTicks next_expected_frame_time,
126     base::TimeDelta vsync_interval) {
127   if (next_expected_frame_time.is_null())
128     return 0;
129 
130   // kErrorMarginIntervalPct used to determine what percentage of the time tick
131   // interval should be used as a margin of error when comparing times to
132   // deadlines.
133   constexpr double kErrorMarginIntervalPct = 0.05;
134   base::TimeDelta error_margin = vsync_interval * kErrorMarginIntervalPct;
135   int ticks_since_estimated_frame_time = base::ClampFloor(
136       (frame_time + error_margin - next_expected_frame_time) / vsync_interval);
137   return std::max(0, ticks_since_estimated_frame_time);
138 }
139 
140 // BeginFrameSource -------------------------------------------------------
141 
142 // static
143 constexpr uint32_t BeginFrameSource::kNotRestartableId;
144 
BeginFrameSource(uint32_t restart_id)145 BeginFrameSource::BeginFrameSource(uint32_t restart_id)
146     : source_id_(GenerateSourceId(restart_id)) {}
147 
148 BeginFrameSource::~BeginFrameSource() = default;
149 
SetIsGpuBusy(bool busy)150 void BeginFrameSource::SetIsGpuBusy(bool busy) {
151   if (is_gpu_busy_ == busy)
152     return;
153   is_gpu_busy_ = busy;
154   if (is_gpu_busy_) {
155     DCHECK_EQ(gpu_busy_response_state_, GpuBusyThrottlingState::kIdle);
156     return;
157   }
158 
159   const bool was_throttled =
160       gpu_busy_response_state_ == GpuBusyThrottlingState::kThrottled;
161   gpu_busy_response_state_ = GpuBusyThrottlingState::kIdle;
162   if (was_throttled)
163     OnGpuNoLongerBusy();
164 }
165 
RequestCallbackOnGpuAvailable()166 bool BeginFrameSource::RequestCallbackOnGpuAvailable() {
167   if (!is_gpu_busy_) {
168     DCHECK_EQ(gpu_busy_response_state_, GpuBusyThrottlingState::kIdle);
169     return false;
170   }
171 
172   switch (gpu_busy_response_state_) {
173     case GpuBusyThrottlingState::kIdle:
174         gpu_busy_response_state_ =
175             GpuBusyThrottlingState::kOneBeginFrameAfterBusySent;
176         return false;
177     case GpuBusyThrottlingState::kOneBeginFrameAfterBusySent:
178       gpu_busy_response_state_ = GpuBusyThrottlingState::kThrottled;
179       return true;
180     case GpuBusyThrottlingState::kThrottled:
181       return true;
182   }
183 
184   NOTREACHED();
185   return false;
186 }
187 
AsProtozeroInto(perfetto::protos::pbzero::BeginFrameSourceState * state) const188 void BeginFrameSource::AsProtozeroInto(
189     perfetto::protos::pbzero::BeginFrameSourceState* state) const {
190   // The lower 32 bits of source_id are the interesting piece of |source_id_|.
191   state->set_source_id(static_cast<uint32_t>(source_id_));
192 }
193 
194 // StubBeginFrameSource ---------------------------------------------------
StubBeginFrameSource()195 StubBeginFrameSource::StubBeginFrameSource()
196     : BeginFrameSource(kNotRestartableId) {}
197 
IsThrottled() const198 bool StubBeginFrameSource::IsThrottled() const {
199   return true;
200 }
201 
202 // SyntheticBeginFrameSource ----------------------------------------------
SyntheticBeginFrameSource(uint32_t restart_id)203 SyntheticBeginFrameSource::SyntheticBeginFrameSource(uint32_t restart_id)
204     : BeginFrameSource(restart_id) {}
205 
206 SyntheticBeginFrameSource::~SyntheticBeginFrameSource() = default;
207 
208 // BackToBackBeginFrameSource ---------------------------------------------
BackToBackBeginFrameSource(std::unique_ptr<DelayBasedTimeSource> time_source)209 BackToBackBeginFrameSource::BackToBackBeginFrameSource(
210     std::unique_ptr<DelayBasedTimeSource> time_source)
211     : SyntheticBeginFrameSource(kNotRestartableId),
212       time_source_(std::move(time_source)),
213       next_sequence_number_(BeginFrameArgs::kStartingFrameNumber) {
214   time_source_->SetClient(this);
215   // The time_source_ ticks immediately, so we SetActive(true) for a single
216   // tick when we need it, and keep it as SetActive(false) otherwise.
217   time_source_->SetTimebaseAndInterval(base::TimeTicks(), base::TimeDelta());
218 }
219 
220 BackToBackBeginFrameSource::~BackToBackBeginFrameSource() = default;
221 
AddObserver(BeginFrameObserver * obs)222 void BackToBackBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
223   DCHECK(obs);
224   DCHECK(!base::Contains(observers_, obs));
225   observers_.insert(obs);
226   pending_begin_frame_observers_.insert(obs);
227   obs->OnBeginFrameSourcePausedChanged(false);
228   time_source_->SetActive(true);
229 }
230 
RemoveObserver(BeginFrameObserver * obs)231 void BackToBackBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
232   DCHECK(obs);
233   DCHECK(base::Contains(observers_, obs));
234   observers_.erase(obs);
235   pending_begin_frame_observers_.erase(obs);
236   if (pending_begin_frame_observers_.empty())
237     time_source_->SetActive(false);
238 }
239 
DidFinishFrame(BeginFrameObserver * obs)240 void BackToBackBeginFrameSource::DidFinishFrame(BeginFrameObserver* obs) {
241   if (base::Contains(observers_, obs)) {
242     pending_begin_frame_observers_.insert(obs);
243     time_source_->SetActive(true);
244   }
245 }
246 
IsThrottled() const247 bool BackToBackBeginFrameSource::IsThrottled() const {
248   return false;
249 }
250 
OnGpuNoLongerBusy()251 void BackToBackBeginFrameSource::OnGpuNoLongerBusy() {
252   OnTimerTick();
253 }
254 
OnTimerTick()255 void BackToBackBeginFrameSource::OnTimerTick() {
256   if (RequestCallbackOnGpuAvailable())
257     return;
258   base::TimeTicks frame_time = time_source_->LastTickTime();
259   base::TimeDelta default_interval = BeginFrameArgs::DefaultInterval();
260   BeginFrameArgs args = BeginFrameArgs::Create(
261       BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time,
262       frame_time + default_interval, default_interval, BeginFrameArgs::NORMAL);
263   next_sequence_number_++;
264 
265   // This must happen after getting the LastTickTime() from the time source.
266   time_source_->SetActive(false);
267 
268   base::flat_set<BeginFrameObserver*> pending_observers;
269   pending_observers.swap(pending_begin_frame_observers_);
270   DCHECK(!pending_observers.empty());
271   for (BeginFrameObserver* obs : pending_observers)
272     FilterAndIssueBeginFrame(obs, args);
273 }
274 
275 // DelayBasedBeginFrameSource ---------------------------------------------
DelayBasedBeginFrameSource(std::unique_ptr<DelayBasedTimeSource> time_source,uint32_t restart_id)276 DelayBasedBeginFrameSource::DelayBasedBeginFrameSource(
277     std::unique_ptr<DelayBasedTimeSource> time_source,
278     uint32_t restart_id)
279     : SyntheticBeginFrameSource(restart_id),
280       time_source_(std::move(time_source)) {
281   time_source_->SetClient(this);
282 }
283 
284 DelayBasedBeginFrameSource::~DelayBasedBeginFrameSource() = default;
285 
OnUpdateVSyncParameters(base::TimeTicks timebase,base::TimeDelta interval)286 void DelayBasedBeginFrameSource::OnUpdateVSyncParameters(
287     base::TimeTicks timebase,
288     base::TimeDelta interval) {
289   if (interval.is_zero()) {
290     // TODO(brianderson): We should not be receiving 0 intervals.
291     interval = BeginFrameArgs::DefaultInterval();
292   }
293 
294   last_timebase_ = timebase;
295   time_source_->SetTimebaseAndInterval(timebase, interval);
296 }
297 
CreateBeginFrameArgs(base::TimeTicks frame_time)298 BeginFrameArgs DelayBasedBeginFrameSource::CreateBeginFrameArgs(
299     base::TimeTicks frame_time) {
300   base::TimeDelta interval = time_source_->Interval();
301   return begin_frame_args_generator_.GenerateBeginFrameArgs(
302       source_id(), frame_time, time_source_->NextTickTime(), interval);
303 }
304 
AddObserver(BeginFrameObserver * obs)305 void DelayBasedBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
306   DCHECK(obs);
307   DCHECK(!base::Contains(observers_, obs));
308 
309   observers_.insert(obs);
310   obs->OnBeginFrameSourcePausedChanged(false);
311   time_source_->SetActive(true);
312 
313   // Missed args should correspond to |last_begin_frame_args_| (particularly,
314   // have the same sequence number) if |last_begin_frame_args_| still correspond
315   // to the last time the time source should have ticked. This may not be the
316   // case if the time source was inactive before AddObserver() was called. In
317   // such a case, we create new args with a new sequence number only if
318   // sufficient time has passed since the last tick.
319   base::TimeTicks last_or_missed_tick_time =
320       time_source_->NextTickTime() - time_source_->Interval();
321   if (!last_begin_frame_args_.IsValid() ||
322       last_or_missed_tick_time >
323           last_begin_frame_args_.frame_time +
324               last_begin_frame_args_.interval / kDoubleTickDivisor) {
325     last_begin_frame_args_ = CreateBeginFrameArgs(last_or_missed_tick_time);
326   }
327   BeginFrameArgs missed_args = last_begin_frame_args_;
328   missed_args.type = BeginFrameArgs::MISSED;
329   IssueBeginFrameToObserver(obs, missed_args);
330 }
331 
RemoveObserver(BeginFrameObserver * obs)332 void DelayBasedBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
333   DCHECK(obs);
334   DCHECK(base::Contains(observers_, obs));
335 
336   observers_.erase(obs);
337   if (observers_.empty())
338     time_source_->SetActive(false);
339 }
340 
IsThrottled() const341 bool DelayBasedBeginFrameSource::IsThrottled() const {
342   return true;
343 }
344 
OnGpuNoLongerBusy()345 void DelayBasedBeginFrameSource::OnGpuNoLongerBusy() {
346   OnTimerTick();
347 }
348 
OnTimerTick()349 void DelayBasedBeginFrameSource::OnTimerTick() {
350   if (RequestCallbackOnGpuAvailable())
351     return;
352   last_begin_frame_args_ = CreateBeginFrameArgs(time_source_->LastTickTime());
353   TRACE_EVENT2(
354       "viz", "DelayBasedBeginFrameSource::OnTimerTick", "frame_time",
355       last_begin_frame_args_.frame_time.since_origin().InMicroseconds(),
356       "interval", last_begin_frame_args_.interval.InMicroseconds());
357   base::flat_set<BeginFrameObserver*> observers(observers_);
358   for (auto* obs : observers)
359     IssueBeginFrameToObserver(obs, last_begin_frame_args_);
360 }
361 
IssueBeginFrameToObserver(BeginFrameObserver * obs,const BeginFrameArgs & args)362 void DelayBasedBeginFrameSource::IssueBeginFrameToObserver(
363     BeginFrameObserver* obs,
364     const BeginFrameArgs& args) {
365   BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
366   if (!last_args.IsValid() ||
367       (args.frame_time >
368        last_args.frame_time + args.interval / kDoubleTickDivisor)) {
369     if (args.type == BeginFrameArgs::MISSED) {
370       DCHECK(!last_args.frame_id.IsNextInSequenceTo(args.frame_id))
371           << "missed " << args.ToString() << ", last " << last_args.ToString();
372     }
373     FilterAndIssueBeginFrame(obs, args);
374   }
375 }
376 
377 // ExternalBeginFrameSource -----------------------------------------------
ExternalBeginFrameSource(ExternalBeginFrameSourceClient * client,uint32_t restart_id)378 ExternalBeginFrameSource::ExternalBeginFrameSource(
379     ExternalBeginFrameSourceClient* client,
380     uint32_t restart_id)
381     : BeginFrameSource(restart_id), client_(client) {
382   DCHECK(client_);
383 }
384 
~ExternalBeginFrameSource()385 ExternalBeginFrameSource::~ExternalBeginFrameSource() {
386   DCHECK(observers_.empty());
387 }
388 
AsProtozeroInto(perfetto::protos::pbzero::BeginFrameSourceState * state) const389 void ExternalBeginFrameSource::AsProtozeroInto(
390     perfetto::protos::pbzero::BeginFrameSourceState* state) const {
391   BeginFrameSource::AsProtozeroInto(state);
392 
393   state->set_paused(paused_);
394   state->set_num_observers(observers_.size());
395   last_begin_frame_args_.AsProtozeroInto(state->set_last_begin_frame_args());
396 }
397 
AddObserver(BeginFrameObserver * obs)398 void ExternalBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
399   DCHECK(obs);
400   DCHECK(!base::Contains(observers_, obs));
401 
402   if (observers_.empty())
403     client_->OnNeedsBeginFrames(true);
404 
405   observers_.insert(obs);
406   obs->OnBeginFrameSourcePausedChanged(paused_);
407 
408   // Send a MISSED begin frame if necessary.
409   BeginFrameArgs missed_args = GetMissedBeginFrameArgs(obs);
410   if (missed_args.IsValid()) {
411     DCHECK_EQ(BeginFrameArgs::MISSED, missed_args.type);
412     FilterAndIssueBeginFrame(obs, missed_args);
413   }
414 }
415 
RemoveObserver(BeginFrameObserver * obs)416 void ExternalBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
417   DCHECK(obs);
418   DCHECK(base::Contains(observers_, obs));
419 
420   observers_.erase(obs);
421   if (observers_.empty())
422     client_->OnNeedsBeginFrames(false);
423 }
424 
IsThrottled() const425 bool ExternalBeginFrameSource::IsThrottled() const {
426   return true;
427 }
428 
OnGpuNoLongerBusy()429 void ExternalBeginFrameSource::OnGpuNoLongerBusy() {
430   OnBeginFrame(pending_begin_frame_args_);
431   pending_begin_frame_args_ = BeginFrameArgs();
432 }
433 
OnSetBeginFrameSourcePaused(bool paused)434 void ExternalBeginFrameSource::OnSetBeginFrameSourcePaused(bool paused) {
435   if (paused_ == paused)
436     return;
437   paused_ = paused;
438   base::flat_set<BeginFrameObserver*> observers(observers_);
439   for (auto* obs : observers)
440     obs->OnBeginFrameSourcePausedChanged(paused_);
441 }
442 
OnBeginFrame(const BeginFrameArgs & args)443 void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) {
444   // Ignore out of order begin frames because of layer tree frame sink being
445   // recreated.
446   if (last_begin_frame_args_.IsValid() &&
447       (args.frame_time <= last_begin_frame_args_.frame_time ||
448        (args.frame_id.source_id == last_begin_frame_args_.frame_id.source_id &&
449         args.frame_id.sequence_number <=
450             last_begin_frame_args_.frame_id.sequence_number)))
451     return;
452 
453   if (RequestCallbackOnGpuAvailable()) {
454     pending_begin_frame_args_ = args;
455     return;
456   }
457 
458   TRACE_EVENT2(
459       "viz", "ExternalBeginFrameSource::OnBeginFrame", "frame_time",
460       last_begin_frame_args_.frame_time.since_origin().InMicroseconds(),
461       "interval", last_begin_frame_args_.interval.InMicroseconds());
462 
463   last_begin_frame_args_ = args;
464   base::flat_set<BeginFrameObserver*> observers(observers_);
465 
466   // Process non-root observers.
467   // TODO(ericrk): Remove root/non-root handling once a better workaround
468   // exists. https://crbug.com/947717
469   for (auto* obs : observers) {
470     if (obs->IsRoot())
471       continue;
472     if (!CheckBeginFrameContinuity(obs, args))
473       continue;
474     FilterAndIssueBeginFrame(obs, args);
475   }
476   // Process root observers.
477   for (auto* obs : observers) {
478     if (!obs->IsRoot())
479       continue;
480     if (!CheckBeginFrameContinuity(obs, args))
481       continue;
482     FilterAndIssueBeginFrame(obs, args);
483   }
484 }
485 
GetMissedBeginFrameArgs(BeginFrameObserver * obs)486 BeginFrameArgs ExternalBeginFrameSource::GetMissedBeginFrameArgs(
487     BeginFrameObserver* obs) {
488   if (!last_begin_frame_args_.IsValid())
489     return BeginFrameArgs();
490   if (!CheckBeginFrameContinuity(obs, last_begin_frame_args_))
491     return BeginFrameArgs();
492 
493   BeginFrameArgs missed_args = last_begin_frame_args_;
494   missed_args.type = BeginFrameArgs::MISSED;
495   return missed_args;
496 }
497 
498 }  // namespace viz
499