1 // Copyright 2016 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 "third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h"
6
7 #include "base/barrier_closure.h"
8 #include "base/callback_helpers.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/time/default_tick_clock.h"
12 #include "base/timer/elapsed_timer.h"
13 #include "third_party/blink/public/platform/platform.h"
14 #include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h"
15 #include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
16 #include "third_party/blink/renderer/platform/graphics/main_thread_mutator_client.h"
17 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
18 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
19 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
20 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
21 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
22
23 namespace blink {
24
25 namespace {
26
27 int g_next_async_mutation_id = 0;
GetNextAsyncMutationId()28 int GetNextAsyncMutationId() {
29 return g_next_async_mutation_id++;
30 }
31
32 } // end namespace
33
34 // Wrap output vector in a thread safe and ref-counted object since it is
35 // accessed from animation worklet threads and its lifetime must be guaranteed
36 // to outlive the mutation update cycle.
37 class AnimationWorkletMutatorDispatcherImpl::OutputVectorRef
38 : public ThreadSafeRefCounted<OutputVectorRef> {
39 public:
Create()40 static scoped_refptr<OutputVectorRef> Create() {
41 return base::AdoptRef(new OutputVectorRef());
42 }
get()43 Vector<std::unique_ptr<AnimationWorkletDispatcherOutput>>& get() {
44 return vector_;
45 }
46
47 private:
48 OutputVectorRef() = default;
49 Vector<std::unique_ptr<AnimationWorkletDispatcherOutput>> vector_;
50 };
51
52 struct AnimationWorkletMutatorDispatcherImpl::AsyncMutationRequest {
53 base::TimeTicks request_time;
54 std::unique_ptr<AnimationWorkletDispatcherInput> input_state;
55 AsyncMutationCompleteCallback done_callback;
56
AsyncMutationRequestblink::AnimationWorkletMutatorDispatcherImpl::AsyncMutationRequest57 AsyncMutationRequest(
58 base::TimeTicks request_time,
59 std::unique_ptr<AnimationWorkletDispatcherInput> input_state,
60 AsyncMutationCompleteCallback done_callback)
61 : request_time(request_time),
62 input_state(std::move(input_state)),
63 done_callback(std::move(done_callback)) {}
64
65 ~AsyncMutationRequest() = default;
66 };
67
AnimationWorkletMutatorDispatcherImpl(bool main_thread_task_runner)68 AnimationWorkletMutatorDispatcherImpl::AnimationWorkletMutatorDispatcherImpl(
69 bool main_thread_task_runner)
70 : client_(nullptr), outputs_(OutputVectorRef::Create()) {
71 // By default web tests run without threaded compositing. See
72 // https://crbug.com/770028. If threaded compositing is disabled or
73 // |main_thread_task_runner| is true we run on the main thread's compositor
74 // task runner otherwise we run tasks on the compositor thread's default
75 // task runner.
76 host_queue_ = main_thread_task_runner || !Thread::CompositorThread()
77 ? Thread::MainThread()->Scheduler()->CompositorTaskRunner()
78 : Thread::CompositorThread()->GetTaskRunner();
79 tick_clock_ = std::make_unique<base::DefaultTickClock>();
80 }
81
82 AnimationWorkletMutatorDispatcherImpl::
~AnimationWorkletMutatorDispatcherImpl()83 ~AnimationWorkletMutatorDispatcherImpl() {}
84
85 // static
86 template <typename ClientType>
CreateClient(base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> * weak_interface,scoped_refptr<base::SingleThreadTaskRunner> * queue,bool main_thread_client)87 std::unique_ptr<ClientType> AnimationWorkletMutatorDispatcherImpl::CreateClient(
88 base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>* weak_interface,
89 scoped_refptr<base::SingleThreadTaskRunner>* queue,
90 bool main_thread_client) {
91 DCHECK(IsMainThread());
92 auto mutator = std::make_unique<AnimationWorkletMutatorDispatcherImpl>(
93 main_thread_client);
94 // This is allowed since we own the class for the duration of creation.
95 *weak_interface = mutator->weak_factory_.GetWeakPtr();
96 *queue = mutator->GetTaskRunner();
97
98 return std::make_unique<ClientType>(std::move(mutator));
99 }
100
101 // static
102 std::unique_ptr<CompositorMutatorClient>
CreateCompositorThreadClient(base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> * weak_interface,scoped_refptr<base::SingleThreadTaskRunner> * queue)103 AnimationWorkletMutatorDispatcherImpl::CreateCompositorThreadClient(
104 base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>* weak_interface,
105 scoped_refptr<base::SingleThreadTaskRunner>* queue) {
106 return CreateClient<CompositorMutatorClient>(weak_interface, queue, false);
107 }
108
109 // static
110 std::unique_ptr<MainThreadMutatorClient>
CreateMainThreadClient(base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> * weak_interface,scoped_refptr<base::SingleThreadTaskRunner> * queue)111 AnimationWorkletMutatorDispatcherImpl::CreateMainThreadClient(
112 base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>* weak_interface,
113 scoped_refptr<base::SingleThreadTaskRunner>* queue) {
114 return CreateClient<MainThreadMutatorClient>(weak_interface, queue, true);
115 }
116
MutateSynchronously(std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input)117 void AnimationWorkletMutatorDispatcherImpl::MutateSynchronously(
118 std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input) {
119 TRACE_EVENT0("cc", "AnimationWorkletMutatorDispatcherImpl::mutate");
120 if (mutator_map_.IsEmpty() || !mutator_input)
121 return;
122 base::ElapsedTimer timer;
123 DCHECK(client_);
124 DCHECK(host_queue_->BelongsToCurrentThread());
125 DCHECK(mutator_input_map_.IsEmpty());
126 DCHECK(outputs_->get().IsEmpty());
127
128 mutator_input_map_ = CreateInputMap(*mutator_input);
129 if (mutator_input_map_.IsEmpty())
130 return;
131
132 base::WaitableEvent event;
133 CrossThreadOnceClosure on_done = CrossThreadBindOnce(
134 &base::WaitableEvent::Signal, WTF::CrossThreadUnretained(&event));
135 RequestMutations(std::move(on_done));
136 event.Wait();
137
138 ApplyMutationsOnHostThread();
139
140 UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
141 "Animation.AnimationWorklet.Dispatcher.SynchronousMutateDuration",
142 timer.Elapsed(), base::TimeDelta::FromMicroseconds(1),
143 base::TimeDelta::FromMilliseconds(100), 50);
144 }
145
NowTicks() const146 base::TimeTicks AnimationWorkletMutatorDispatcherImpl::NowTicks() const {
147 DCHECK(tick_clock_);
148 return tick_clock_->NowTicks();
149 }
150
MutateAsynchronously(std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input,MutateQueuingStrategy queuing_strategy,AsyncMutationCompleteCallback done_callback)151 bool AnimationWorkletMutatorDispatcherImpl::MutateAsynchronously(
152 std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input,
153 MutateQueuingStrategy queuing_strategy,
154 AsyncMutationCompleteCallback done_callback) {
155 DCHECK(client_);
156 DCHECK(host_queue_->BelongsToCurrentThread());
157 if (mutator_map_.IsEmpty() || !mutator_input)
158 return false;
159
160 base::TimeTicks request_time = NowTicks();
161 if (!mutator_input_map_.IsEmpty()) {
162 // Still running mutations from a previous frame.
163 switch (queuing_strategy) {
164 case MutateQueuingStrategy::kDrop:
165 // Skip this frame to avoid lagging behind.
166 return false;
167
168 case MutateQueuingStrategy::kQueueHighPriority:
169 // Can only have one priority request in-flight.
170 DCHECK(!queued_priority_request.get());
171 queued_priority_request = std::make_unique<AsyncMutationRequest>(
172 request_time, std::move(mutator_input), std::move(done_callback));
173 return true;
174
175 case MutateQueuingStrategy::kQueueAndReplaceNormalPriority:
176 if (queued_replaceable_request.get()) {
177 // Cancel previously queued request.
178 request_time = queued_replaceable_request->request_time;
179 std::move(queued_replaceable_request->done_callback)
180 .Run(MutateStatus::kCanceled);
181 }
182 queued_replaceable_request = std::make_unique<AsyncMutationRequest>(
183 request_time, std::move(mutator_input), std::move(done_callback));
184 return true;
185 }
186 }
187
188 mutator_input_map_ = CreateInputMap(*mutator_input);
189 if (mutator_input_map_.IsEmpty())
190 return false;
191
192 MutateAsynchronouslyInternal(request_time, std::move(done_callback));
193 return true;
194 }
195
MutateAsynchronouslyInternal(base::TimeTicks request_time,AsyncMutationCompleteCallback done_callback)196 void AnimationWorkletMutatorDispatcherImpl::MutateAsynchronouslyInternal(
197 base::TimeTicks request_time,
198 AsyncMutationCompleteCallback done_callback) {
199 DCHECK(host_queue_->BelongsToCurrentThread());
200 on_async_mutation_complete_ = std::move(done_callback);
201 int next_async_mutation_id = GetNextAsyncMutationId();
202 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
203 "cc", "AnimationWorkletMutatorDispatcherImpl::MutateAsync",
204 TRACE_ID_LOCAL(next_async_mutation_id));
205
206 CrossThreadOnceClosure on_done = CrossThreadBindOnce(
207 [](scoped_refptr<base::SingleThreadTaskRunner> host_queue,
208 base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> dispatcher,
209 int next_async_mutation_id, base::TimeTicks request_time) {
210 PostCrossThreadTask(
211 *host_queue, FROM_HERE,
212 CrossThreadBindOnce(
213 &AnimationWorkletMutatorDispatcherImpl::AsyncMutationsDone,
214 dispatcher, next_async_mutation_id, request_time));
215 },
216 host_queue_, weak_factory_.GetWeakPtr(), next_async_mutation_id,
217 request_time);
218
219 RequestMutations(std::move(on_done));
220 }
221
AsyncMutationsDone(int async_mutation_id,base::TimeTicks request_time)222 void AnimationWorkletMutatorDispatcherImpl::AsyncMutationsDone(
223 int async_mutation_id,
224 base::TimeTicks request_time) {
225 DCHECK(client_);
226 DCHECK(host_queue_->BelongsToCurrentThread());
227 bool update_applied = ApplyMutationsOnHostThread();
228 auto done_callback = std::move(on_async_mutation_complete_);
229 std::unique_ptr<AsyncMutationRequest> queued_request;
230 if (queued_priority_request.get()) {
231 queued_request.reset(queued_priority_request.release());
232 } else if (queued_replaceable_request.get()) {
233 queued_request.reset(queued_replaceable_request.release());
234 }
235 if (queued_request.get()) {
236 mutator_input_map_ = CreateInputMap(*queued_request->input_state);
237 MutateAsynchronouslyInternal(queued_request->request_time,
238 std::move(queued_request->done_callback));
239 }
240 // The trace event deos not include queuing time. It covers the interval
241 // between dispatching the request and retrieving the results.
242 TRACE_EVENT_NESTABLE_ASYNC_END0(
243 "cc", "AnimationWorkletMutatorDispatcherImpl::MutateAsync",
244 TRACE_ID_LOCAL(async_mutation_id));
245 // The Async mutation duration is the total time between request and
246 // completion, and thus includes queuing time.
247 UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
248 "Animation.AnimationWorklet.Dispatcher.AsynchronousMutateDuration",
249 NowTicks() - request_time, base::TimeDelta::FromMicroseconds(1),
250 base::TimeDelta::FromMilliseconds(100), 50);
251
252 std::move(done_callback)
253 .Run(update_applied ? MutateStatus::kCompletedWithUpdate
254 : MutateStatus::kCompletedNoUpdate);
255 }
256
RegisterAnimationWorkletMutator(CrossThreadPersistent<AnimationWorkletMutator> mutator,scoped_refptr<base::SingleThreadTaskRunner> mutator_runner)257 void AnimationWorkletMutatorDispatcherImpl::RegisterAnimationWorkletMutator(
258 CrossThreadPersistent<AnimationWorkletMutator> mutator,
259 scoped_refptr<base::SingleThreadTaskRunner> mutator_runner) {
260 TRACE_EVENT0(
261 "cc",
262 "AnimationWorkletMutatorDispatcherImpl::RegisterAnimationWorkletMutator");
263
264 DCHECK(mutator);
265 DCHECK(host_queue_->BelongsToCurrentThread());
266
267 mutator_map_.insert(mutator, mutator_runner);
268 }
269
UnregisterAnimationWorkletMutator(CrossThreadPersistent<AnimationWorkletMutator> mutator)270 void AnimationWorkletMutatorDispatcherImpl::UnregisterAnimationWorkletMutator(
271 CrossThreadPersistent<AnimationWorkletMutator> mutator) {
272 TRACE_EVENT0("cc",
273 "AnimationWorkletMutatorDispatcherImpl::"
274 "UnregisterAnimationWorkletMutator");
275 DCHECK(mutator);
276 DCHECK(host_queue_->BelongsToCurrentThread());
277
278 mutator_map_.erase(mutator);
279 }
280
SynchronizeAnimatorName(const String & animator_name)281 void AnimationWorkletMutatorDispatcherImpl::SynchronizeAnimatorName(
282 const String& animator_name) {
283 client_->SynchronizeAnimatorName(animator_name);
284 }
285
HasMutators()286 bool AnimationWorkletMutatorDispatcherImpl::HasMutators() {
287 return !mutator_map_.IsEmpty();
288 }
289
290 AnimationWorkletMutatorDispatcherImpl::InputMap
CreateInputMap(AnimationWorkletDispatcherInput & mutator_input) const291 AnimationWorkletMutatorDispatcherImpl::CreateInputMap(
292 AnimationWorkletDispatcherInput& mutator_input) const {
293 InputMap input_map;
294 for (const auto& pair : mutator_map_) {
295 AnimationWorkletMutator* mutator = pair.key;
296 const int worklet_id = mutator->GetWorkletId();
297 std::unique_ptr<AnimationWorkletInput> input =
298 mutator_input.TakeWorkletState(worklet_id);
299 if (input) {
300 input_map.insert(worklet_id, std::move(input));
301 }
302 }
303 return input_map;
304 }
305
RequestMutations(CrossThreadOnceClosure done_callback)306 void AnimationWorkletMutatorDispatcherImpl::RequestMutations(
307 CrossThreadOnceClosure done_callback) {
308 DCHECK(client_);
309 DCHECK(outputs_->get().IsEmpty());
310
311 int num_requests = mutator_map_.size();
312 if (num_requests == 0) {
313 std::move(done_callback).Run();
314 return;
315 }
316
317 int next_request_index = 0;
318 outputs_->get().Grow(num_requests);
319 base::RepeatingClosure on_mutator_done = base::BarrierClosure(
320 num_requests, ConvertToBaseOnceCallback(std::move(done_callback)));
321
322 for (const auto& pair : mutator_map_) {
323 AnimationWorkletMutator* mutator = pair.key;
324 scoped_refptr<base::SingleThreadTaskRunner> worklet_queue = pair.value;
325 int worklet_id = mutator->GetWorkletId();
326 DCHECK(!worklet_queue->BelongsToCurrentThread());
327
328 // Wrap the barrier closure in a ScopedClosureRunner to guarantee it runs
329 // even if the posted task does not run.
330 auto on_done_runner =
331 std::make_unique<base::ScopedClosureRunner>(on_mutator_done);
332
333 auto it = mutator_input_map_.find(worklet_id);
334 if (it == mutator_input_map_.end()) {
335 // Here the on_done_runner goes out of scope which causes the barrier
336 // closure to run.
337 continue;
338 }
339
340 PostCrossThreadTask(
341 *worklet_queue, FROM_HERE,
342 CrossThreadBindOnce(
343 [](AnimationWorkletMutator* mutator,
344 std::unique_ptr<AnimationWorkletInput> input,
345 scoped_refptr<OutputVectorRef> outputs, int index,
346 std::unique_ptr<base::ScopedClosureRunner> on_done_runner) {
347 std::unique_ptr<AnimationWorkletOutput> output =
348 mutator ? mutator->Mutate(std::move(input)) : nullptr;
349 outputs->get()[index] = std::move(output);
350 on_done_runner->RunAndReset();
351 },
352 // The mutator is created and destroyed on the worklet thread.
353 WrapCrossThreadWeakPersistent(mutator),
354 // The worklet input is not required after the Mutate call.
355 WTF::Passed(std::move(it->value)),
356 // The vector of outputs is wrapped in a scoped_refptr initialized
357 // on the host thread. It can outlive the dispatcher during shutdown
358 // of a process with a running animation.
359 outputs_, next_request_index++,
360 WTF::Passed(std::move(on_done_runner))));
361 }
362 }
363
ApplyMutationsOnHostThread()364 bool AnimationWorkletMutatorDispatcherImpl::ApplyMutationsOnHostThread() {
365 DCHECK(client_);
366 DCHECK(host_queue_->BelongsToCurrentThread());
367 bool update_applied = false;
368 for (auto& output : outputs_->get()) {
369 if (output) {
370 client_->SetMutationUpdate(std::move(output));
371 update_applied = true;
372 }
373 }
374 mutator_input_map_.clear();
375 outputs_->get().clear();
376 return update_applied;
377 }
378
379 } // namespace blink
380