| // 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(const AutoSignal&) = delete; |
| AutoSignal& operator=(const AutoSignal&) = delete; |
| ~AutoSignal() { event_->Signal(); } |
| |
| private: |
| base::WaitableEvent* event_; |
| }; |
| } // 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 |