1 // Copyright 2019 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/paint_worklet_paint_dispatcher.h"
6 
7 #include <utility>
8 
9 #include "base/barrier_closure.h"
10 #include "base/callback_helpers.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "third_party/blink/public/platform/platform.h"
13 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
14 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
15 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
16 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
17 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
18 
19 namespace blink {
20 
21 namespace {
22 class AutoSignal {
23  public:
AutoSignal(base::WaitableEvent * event)24   explicit AutoSignal(base::WaitableEvent* event) : event_(event) {
25     DCHECK(event);
26   }
~AutoSignal()27   ~AutoSignal() { event_->Signal(); }
28 
29  private:
30   base::WaitableEvent* event_;
31 
32   DISALLOW_COPY_AND_ASSIGN(AutoSignal);
33 };
34 }  // namespace
35 
36 // static
37 std::unique_ptr<PlatformPaintWorkletLayerPainter>
CreateCompositorThreadPainter(base::WeakPtr<PaintWorkletPaintDispatcher> * paint_dispatcher)38 PaintWorkletPaintDispatcher::CreateCompositorThreadPainter(
39     base::WeakPtr<PaintWorkletPaintDispatcher>* paint_dispatcher) {
40   DCHECK(IsMainThread());
41   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
42   *paint_dispatcher = dispatcher->GetWeakPtr();
43 
44   return std::make_unique<PlatformPaintWorkletLayerPainter>(
45       std::move(dispatcher));
46 }
47 
PaintWorkletPaintDispatcher()48 PaintWorkletPaintDispatcher::PaintWorkletPaintDispatcher() {
49   // PaintWorkletPaintDispatcher is created on the main thread but used on the
50   // compositor, so detach the sequence checker until a call is received.
51   DCHECK(IsMainThread());
52   DETACH_FROM_SEQUENCE(sequence_checker_);
53 }
54 
RegisterPaintWorkletPainter(PaintWorkletPainter * painter,scoped_refptr<base::SingleThreadTaskRunner> painter_runner)55 void PaintWorkletPaintDispatcher::RegisterPaintWorkletPainter(
56     PaintWorkletPainter* painter,
57     scoped_refptr<base::SingleThreadTaskRunner> painter_runner) {
58   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
59   TRACE_EVENT0("cc",
60                "PaintWorkletPaintDispatcher::RegisterPaintWorkletPainter");
61 
62   int worklet_id = painter->GetWorkletId();
63   DCHECK(painter_map_.find(worklet_id) == painter_map_.end());
64   painter_map_.insert(worklet_id, std::make_pair(painter, painter_runner));
65 }
66 
UnregisterPaintWorkletPainter(int worklet_id)67 void PaintWorkletPaintDispatcher::UnregisterPaintWorkletPainter(
68     int worklet_id) {
69   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
70   TRACE_EVENT0("cc",
71                "PaintWorkletPaintDispatcher::"
72                "UnregisterPaintWorkletPainter");
73   DCHECK(painter_map_.find(worklet_id) != painter_map_.end());
74   painter_map_.erase(worklet_id);
75 }
76 
DispatchWorklets(cc::PaintWorkletJobMap worklet_job_map,PlatformPaintWorkletLayerPainter::DoneCallback done_callback)77 void PaintWorkletPaintDispatcher::DispatchWorklets(
78     cc::PaintWorkletJobMap worklet_job_map,
79     PlatformPaintWorkletLayerPainter::DoneCallback done_callback) {
80   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
81   TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::DispatchWorklets");
82 
83   // We must be called with a valid callback to guarantee our internal state.
84   DCHECK(!done_callback.is_null());
85 
86   // Dispatching to the worklets is an asynchronous process, but there should
87   // only be one dispatch going on at once. We store the completion callback and
88   // the PaintWorklet job map in the class during the dispatch, then clear them
89   // when we get results (see AsyncPaintDone).
90   DCHECK(on_async_paint_complete_.is_null());
91   on_async_paint_complete_ = std::move(done_callback);
92   ongoing_jobs_ = std::move(worklet_job_map);
93 
94   scoped_refptr<base::SingleThreadTaskRunner> runner =
95       Thread::Current()->GetTaskRunner();
96   WTF::CrossThreadClosure on_done = CrossThreadBindRepeating(
97       [](base::WeakPtr<PaintWorkletPaintDispatcher> dispatcher,
98          scoped_refptr<base::SingleThreadTaskRunner> runner) {
99         PostCrossThreadTask(
100             *runner, FROM_HERE,
101             CrossThreadBindOnce(&PaintWorkletPaintDispatcher::AsyncPaintDone,
102                                 dispatcher));
103       },
104       weak_factory_.GetWeakPtr(), WTF::Passed(std::move(runner)));
105 
106   // Use a base::RepeatingClosure to make sure that AsyncPaintDone is only
107   // called once, once all the worklets are done. If there are no inputs
108   // specified, base::RepeatingClosure will trigger immediately and so the
109   // callback will still happen.
110   base::RepeatingClosure repeating_on_done = base::BarrierClosure(
111       ongoing_jobs_.size(), ConvertToBaseRepeatingCallback(std::move(on_done)));
112 
113   // Now dispatch the calls to the registered painters. For each input, we match
114   // the id to a registered worklet and dispatch a cross-thread call to it,
115   // using the above-created base::RepeatingClosure.
116   for (auto& job : ongoing_jobs_) {
117     int worklet_id = job.first;
118     scoped_refptr<cc::PaintWorkletJobVector> jobs = job.second;
119 
120     // Wrap the barrier closure in a ScopedClosureRunner to guarantee it runs
121     // even if there is no matching worklet or the posted task does not run.
122     auto on_done_runner =
123         std::make_unique<base::ScopedClosureRunner>(repeating_on_done);
124 
125     auto it = painter_map_.find(worklet_id);
126     if (it == painter_map_.end())
127       continue;
128 
129     PaintWorkletPainter* painter = it->value.first;
130     scoped_refptr<base::SingleThreadTaskRunner> task_runner = it->value.second;
131     DCHECK(!task_runner->BelongsToCurrentThread());
132 
133     PostCrossThreadTask(
134         *task_runner, FROM_HERE,
135         CrossThreadBindOnce(
136             [](PaintWorkletPainter* painter,
137                scoped_refptr<cc::PaintWorkletJobVector> jobs,
138                std::unique_ptr<base::ScopedClosureRunner> on_done_runner) {
139               for (cc::PaintWorkletJob& job : jobs->data) {
140                 job.SetOutput(painter->Paint(job.input().get(),
141                                              job.GetAnimatedPropertyValues()));
142               }
143               on_done_runner->RunAndReset();
144             },
145             WrapCrossThreadPersistent(painter), WTF::Passed(std::move(jobs)),
146             WTF::Passed(std::move(on_done_runner))));
147   }
148 }
149 
HasOngoingDispatch() const150 bool PaintWorkletPaintDispatcher::HasOngoingDispatch() const {
151   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
152   return !on_async_paint_complete_.is_null();
153 }
154 
AsyncPaintDone()155 void PaintWorkletPaintDispatcher::AsyncPaintDone() {
156   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
157   TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::AsyncPaintDone");
158   std::move(on_async_paint_complete_).Run(std::move(ongoing_jobs_));
159 }
160 
161 }  // namespace blink
162