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