summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc
blob: 508a92c1f19a2ec679578ea764717a4e688f2984 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h"

#include <utility>

#include "base/barrier_closure.h"
#include "base/callback_helpers.h"
#include "base/synchronization/waitable_event.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"

namespace blink {

namespace {
class AutoSignal {
 public:
  explicit AutoSignal(base::WaitableEvent* event) : event_(event) {
    DCHECK(event);
  }
  ~AutoSignal() { event_->Signal(); }

 private:
  base::WaitableEvent* event_;

  DISALLOW_COPY_AND_ASSIGN(AutoSignal);
};
}  // namespace

// static
std::unique_ptr<PlatformPaintWorkletLayerPainter>
PaintWorkletPaintDispatcher::CreateCompositorThreadPainter(
    base::WeakPtr<PaintWorkletPaintDispatcher>* paint_dispatcher) {
  DCHECK(IsMainThread());
  auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
  *paint_dispatcher = dispatcher->GetWeakPtr();

  return std::make_unique<PlatformPaintWorkletLayerPainter>(
      std::move(dispatcher));
}

PaintWorkletPaintDispatcher::PaintWorkletPaintDispatcher() {
  // PaintWorkletPaintDispatcher is created on the main thread but used on the
  // compositor, so detach the sequence checker until a call is received.
  DCHECK(IsMainThread());
  DETACH_FROM_SEQUENCE(sequence_checker_);
}

void PaintWorkletPaintDispatcher::RegisterPaintWorkletPainter(
    PaintWorkletPainter* painter,
    scoped_refptr<base::SingleThreadTaskRunner> painter_runner) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  TRACE_EVENT0("cc",
               "PaintWorkletPaintDispatcher::RegisterPaintWorkletPainter");

  int worklet_id = painter->GetWorkletId();
  DCHECK(painter_map_.find(worklet_id) == painter_map_.end());
  painter_map_.insert(worklet_id, std::make_pair(painter, painter_runner));
}

void PaintWorkletPaintDispatcher::UnregisterPaintWorkletPainter(
    int worklet_id) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  TRACE_EVENT0("cc",
               "PaintWorkletPaintDispatcher::"
               "UnregisterPaintWorkletPainter");
  DCHECK(painter_map_.find(worklet_id) != painter_map_.end());
  painter_map_.erase(worklet_id);
}

void PaintWorkletPaintDispatcher::DispatchWorklets(
    cc::PaintWorkletJobMap worklet_job_map,
    PlatformPaintWorkletLayerPainter::DoneCallback done_callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::DispatchWorklets");

  // We must be called with a valid callback to guarantee our internal state.
  DCHECK(!done_callback.is_null());

  // Dispatching to the worklets is an asynchronous process, but there should
  // only be one dispatch going on at once. We store the completion callback and
  // the PaintWorklet job map in the class during the dispatch, then clear them
  // when we get results (see AsyncPaintDone).
  DCHECK(on_async_paint_complete_.is_null());
  on_async_paint_complete_ = std::move(done_callback);
  ongoing_jobs_ = std::move(worklet_job_map);

  scoped_refptr<base::SingleThreadTaskRunner> runner =
      Thread::Current()->GetTaskRunner();
  WTF::CrossThreadClosure on_done = CrossThreadBindRepeating(
      [](base::WeakPtr<PaintWorkletPaintDispatcher> dispatcher,
         scoped_refptr<base::SingleThreadTaskRunner> runner) {
        PostCrossThreadTask(
            *runner, FROM_HERE,
            CrossThreadBindOnce(&PaintWorkletPaintDispatcher::AsyncPaintDone,
                                dispatcher));
      },
      weak_factory_.GetWeakPtr(), WTF::Passed(std::move(runner)));

  // Use a base::RepeatingClosure to make sure that AsyncPaintDone is only
  // called once, once all the worklets are done. If there are no inputs
  // specified, base::RepeatingClosure will trigger immediately and so the
  // callback will still happen.
  base::RepeatingClosure repeating_on_done = base::BarrierClosure(
      ongoing_jobs_.size(), ConvertToBaseCallback(std::move(on_done)));

  // Now dispatch the calls to the registered painters. For each input, we match
  // the id to a registered worklet and dispatch a cross-thread call to it,
  // using the above-created base::RepeatingClosure.
  for (auto& job : ongoing_jobs_) {
    int worklet_id = job.first;
    scoped_refptr<cc::PaintWorkletJobVector> jobs = job.second;

    // Wrap the barrier closure in a ScopedClosureRunner to guarantee it runs
    // even if there is no matching worklet or the posted task does not run.
    auto on_done_runner =
        std::make_unique<base::ScopedClosureRunner>(repeating_on_done);

    auto it = painter_map_.find(worklet_id);
    if (it == painter_map_.end())
      continue;

    PaintWorkletPainter* painter = it->value.first;
    scoped_refptr<base::SingleThreadTaskRunner> task_runner = it->value.second;
    DCHECK(!task_runner->BelongsToCurrentThread());

    PostCrossThreadTask(
        *task_runner, FROM_HERE,
        CrossThreadBindOnce(
            [](PaintWorkletPainter* painter,
               scoped_refptr<cc::PaintWorkletJobVector> jobs,
               std::unique_ptr<base::ScopedClosureRunner> on_done_runner) {
              for (cc::PaintWorkletJob& job : jobs->data) {
                job.SetOutput(painter->Paint(job.input().get(),
                                             job.GetAnimatedPropertyValues()));
              }
              on_done_runner->RunAndReset();
            },
            WrapCrossThreadPersistent(painter), WTF::Passed(std::move(jobs)),
            WTF::Passed(std::move(on_done_runner))));
  }
}

bool PaintWorkletPaintDispatcher::HasOngoingDispatch() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return !on_async_paint_complete_.is_null();
}

void PaintWorkletPaintDispatcher::AsyncPaintDone() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::AsyncPaintDone");
  std::move(on_async_paint_complete_).Run(std::move(ongoing_jobs_));
}

}  // namespace blink