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