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