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