blob: 57cce02562177704fbb2a31c2e8ec5d091a8bb54 [file] [log] [blame]
// 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(), 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(), ConvertToBaseRepeatingCallback(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), std::move(jobs),
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