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