Commit fe312c94 authored by Stephen McGruer's avatar Stephen McGruer Committed by Commit Bot

[OT-PW] Track PaintWorklet painting state in the cc scheduler

This CL teaches the cc scheduler about the asynchronous painting of
PaintWorklets. While PaintWorklets are being painted, the pending tree
cannot be activated (as it is not fully painted). As such, we must
delay activation (even via aborting the frame) until the painting has
finished.

Note that at this time the asynchronous painting has not been landed (as
it depends on this scheduling behavior). Support will be added in
upcoming patches; see the prototype:
https://chromium-review.googlesource.com/c/chromium/src/+/1577635

Bug: 907897
Change-Id: I7d9ec466c57fbe3f8088c5ff6bf6faa140bd4b83
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1649656
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: default avatarvmpstr <vmpstr@chromium.org>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#669114}
parent 1b4d8e8a
...@@ -115,6 +115,11 @@ void Scheduler::NotifyAnimationWorkletStateChange(AnimationWorkletState state, ...@@ -115,6 +115,11 @@ void Scheduler::NotifyAnimationWorkletStateChange(AnimationWorkletState state,
ProcessScheduledActions(); ProcessScheduledActions();
} }
void Scheduler::NotifyPaintWorkletStateChange(PaintWorkletState state) {
state_machine_.NotifyPaintWorkletStateChange(state);
ProcessScheduledActions();
}
void Scheduler::SetNeedsBeginMainFrame() { void Scheduler::SetNeedsBeginMainFrame() {
state_machine_.SetNeedsBeginMainFrame(); state_machine_.SetNeedsBeginMainFrame();
ProcessScheduledActions(); ProcessScheduledActions();
......
...@@ -108,6 +108,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -108,6 +108,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
void SetBeginFrameSource(viz::BeginFrameSource* source); void SetBeginFrameSource(viz::BeginFrameSource* source);
using AnimationWorkletState = SchedulerStateMachine::AnimationWorkletState; using AnimationWorkletState = SchedulerStateMachine::AnimationWorkletState;
using PaintWorkletState = SchedulerStateMachine::PaintWorkletState;
using TreeType = SchedulerStateMachine::TreeType; using TreeType = SchedulerStateMachine::TreeType;
// Sets whether asynchronous animation worklet mutations are running. // Sets whether asynchronous animation worklet mutations are running.
...@@ -116,6 +117,11 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -116,6 +117,11 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
void NotifyAnimationWorkletStateChange(AnimationWorkletState state, void NotifyAnimationWorkletStateChange(AnimationWorkletState state,
TreeType tree); TreeType tree);
// Sets whether asynchronous paint worklets are running. Paint worklets
// running should block activation of the pending tree, as it isn't fully
// painted until they are done.
void NotifyPaintWorkletStateChange(PaintWorkletState state);
// Set |needs_begin_main_frame_| to true, which will cause the BeginFrame // Set |needs_begin_main_frame_| to true, which will cause the BeginFrame
// source to be told to send BeginFrames to this client so that this client // source to be told to send BeginFrames to this client so that this client
// can send a CompositorFrame to the display compositor with appropriate // can send a CompositorFrame to the display compositor with appropriate
......
...@@ -241,6 +241,8 @@ void SchedulerStateMachine::AsValueInto( ...@@ -241,6 +241,8 @@ void SchedulerStateMachine::AsValueInto(
processing_animation_worklets_for_active_tree_); processing_animation_worklets_for_active_tree_);
state->SetBoolean("processing_animation_worklets_for_pending_tree", state->SetBoolean("processing_animation_worklets_for_pending_tree",
processing_animation_worklets_for_pending_tree_); processing_animation_worklets_for_pending_tree_);
state->SetBoolean("processing_paint_worklets_for_pending_tree",
processing_paint_worklets_for_pending_tree_);
state->EndDictionary(); state->EndDictionary();
} }
...@@ -379,6 +381,15 @@ bool SchedulerStateMachine::ShouldActivateSyncTree() const { ...@@ -379,6 +381,15 @@ bool SchedulerStateMachine::ShouldActivateSyncTree() const {
if (active_tree_needs_first_draw_) if (active_tree_needs_first_draw_)
return false; return false;
// Delay pending tree activation until paint worklets have completed painting
// the pending tree. This must occur before the |ShouldAbortCurrentFrame|
// check as we cannot have an unpainted active tree.
//
// Note that paint worklets continue to paint when the page is not visible, so
// any abort will eventually happen when they complete.
if (processing_paint_worklets_for_pending_tree_)
return false;
if (ShouldAbortCurrentFrame()) if (ShouldAbortCurrentFrame())
return true; return true;
...@@ -847,6 +858,11 @@ void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) { ...@@ -847,6 +858,11 @@ void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) {
} }
void SchedulerStateMachine::WillActivate() { void SchedulerStateMachine::WillActivate() {
// We cannot activate the pending tree while paint worklets are still being
// processed; the pending tree *must* be fully painted before it can ever be
// activated because we cannot paint the active tree.
DCHECK(!processing_paint_worklets_for_pending_tree_);
if (layer_tree_frame_sink_state_ == if (layer_tree_frame_sink_state_ ==
LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION) LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION)
layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::ACTIVE; layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::ACTIVE;
...@@ -1415,6 +1431,12 @@ void SchedulerStateMachine::DidLoseLayerTreeFrameSink() { ...@@ -1415,6 +1431,12 @@ void SchedulerStateMachine::DidLoseLayerTreeFrameSink() {
} }
bool SchedulerStateMachine::NotifyReadyToActivate() { bool SchedulerStateMachine::NotifyReadyToActivate() {
// It is not valid for clients to try and activate the pending tree whilst
// paint worklets are still being processed; the pending tree *must* be fully
// painted before it can ever be activated (even if e.g. it is not visible),
// because we cannot paint the active tree.
DCHECK(!processing_paint_worklets_for_pending_tree_);
if (!has_pending_tree_ || pending_tree_is_ready_for_activation_) if (!has_pending_tree_ || pending_tree_is_ready_for_activation_)
return false; return false;
...@@ -1448,6 +1470,16 @@ void SchedulerStateMachine::NotifyAnimationWorkletStateChange( ...@@ -1448,6 +1470,16 @@ void SchedulerStateMachine::NotifyAnimationWorkletStateChange(
} }
} }
void SchedulerStateMachine::NotifyPaintWorkletStateChange(
PaintWorkletState state) {
bool processing_paint_worklets_for_pending_tree =
(state == PaintWorkletState::PROCESSING);
DCHECK_NE(processing_paint_worklets_for_pending_tree,
processing_paint_worklets_for_pending_tree_);
processing_paint_worklets_for_pending_tree_ =
processing_paint_worklets_for_pending_tree;
}
void SchedulerStateMachine::DidCreateAndInitializeLayerTreeFrameSink() { void SchedulerStateMachine::DidCreateAndInitializeLayerTreeFrameSink() {
DCHECK_EQ(layer_tree_frame_sink_state_, LayerTreeFrameSinkState::CREATING); DCHECK_EQ(layer_tree_frame_sink_state_, LayerTreeFrameSinkState::CREATING);
layer_tree_frame_sink_state_ = layer_tree_frame_sink_state_ =
......
...@@ -283,6 +283,7 @@ class CC_EXPORT SchedulerStateMachine { ...@@ -283,6 +283,7 @@ class CC_EXPORT SchedulerStateMachine {
void NotifyReadyToDraw(); void NotifyReadyToDraw();
enum class AnimationWorkletState { PROCESSING, IDLE }; enum class AnimationWorkletState { PROCESSING, IDLE };
enum class PaintWorkletState { PROCESSING, IDLE };
enum class TreeType { ACTIVE, PENDING }; enum class TreeType { ACTIVE, PENDING };
// Indicates if currently processing animation worklets for the active or // Indicates if currently processing animation worklets for the active or
...@@ -291,6 +292,11 @@ class CC_EXPORT SchedulerStateMachine { ...@@ -291,6 +292,11 @@ class CC_EXPORT SchedulerStateMachine {
void NotifyAnimationWorkletStateChange(AnimationWorkletState state, void NotifyAnimationWorkletStateChange(AnimationWorkletState state,
TreeType tree); TreeType tree);
// Sets whether asynchronous paint worklets are running. Paint worklets
// running should block activation of the pending tree, as it isn't fully
// painted until they are done.
void NotifyPaintWorkletStateChange(PaintWorkletState state);
void SetNeedsImplSideInvalidation(bool needs_first_draw_on_activation); void SetNeedsImplSideInvalidation(bool needs_first_draw_on_activation);
bool has_pending_tree() const { return has_pending_tree_; } bool has_pending_tree() const { return has_pending_tree_; }
...@@ -451,6 +457,9 @@ class CC_EXPORT SchedulerStateMachine { ...@@ -451,6 +457,9 @@ class CC_EXPORT SchedulerStateMachine {
// Indicates if an aysnc mutation cycle is in-flight or queued for the pending // Indicates if an aysnc mutation cycle is in-flight or queued for the pending
// tree. Only one can be running or queued at any time. // tree. Only one can be running or queued at any time.
bool processing_animation_worklets_for_pending_tree_ = false; bool processing_animation_worklets_for_pending_tree_ = false;
// Indicates if asychronous paint worklet painting is ongoing for the pending
// tree. During this time we should not activate the pending tree.
bool processing_paint_worklets_for_pending_tree_ = false;
// Set to true if the main thread fails to respond with a commit or abort the // Set to true if the main thread fails to respond with a commit or abort the
// main frame before the draw deadline on the previous impl frame. // main frame before the draw deadline on the previous impl frame.
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <stddef.h> #include <stddef.h>
#include "base/test/gtest_util.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "cc/scheduler/scheduler.h" #include "cc/scheduler/scheduler.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_sinks/begin_frame_args.h"
...@@ -2815,5 +2816,58 @@ TEST(SchedulerStateMachineTest, BlockActivationIfAnimationWorkletsPending) { ...@@ -2815,5 +2816,58 @@ TEST(SchedulerStateMachineTest, BlockActivationIfAnimationWorkletsPending) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
} }
TEST(SchedulerStateMachineTest, BlockActivationIfPaintWorkletsPending) {
SchedulerSettings settings;
StateMachine state(settings);
SET_UP_STATE(state);
state.SetNeedsBeginMainFrame();
state.OnBeginImplFrame(0, 10, kAnimateOnly);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME);
state.NotifyBeginMainFrameStarted();
state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
// Post-commit, we start working on PaintWorklets. It is not valid to activate
// until they are done.
state.NotifyPaintWorkletStateChange(
SchedulerStateMachine::PaintWorkletState::PROCESSING);
EXPECT_DCHECK_DEATH(state.NotifyReadyToActivate());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
state.NotifyPaintWorkletStateChange(
SchedulerStateMachine::PaintWorkletState::IDLE);
state.NotifyReadyToActivate();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
}
TEST(SchedulerStateMachineTest,
BlockActivationIfPaintWorkletsPendingEvenWhenAbortingFrame) {
SchedulerSettings settings;
StateMachine state(settings);
SET_UP_STATE(state);
state.SetNeedsBeginMainFrame();
state.OnBeginImplFrame(0, 10, kAnimateOnly);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME);
state.NotifyBeginMainFrameStarted();
state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
// Even if we are aborting the frame, we must paint the pending tree before we
// activate it (because we cannot have an unpainted active tree).
state.NotifyPaintWorkletStateChange(
SchedulerStateMachine::PaintWorkletState::PROCESSING);
state.SetVisible(false);
ASSERT_TRUE(state.ShouldAbortCurrentFrame());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
state.NotifyPaintWorkletStateChange(
SchedulerStateMachine::PaintWorkletState::IDLE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
}
} // namespace } // namespace
} // namespace cc } // namespace cc
...@@ -46,6 +46,8 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient { ...@@ -46,6 +46,8 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient {
void NotifyAnimationWorkletStateChange(AnimationWorkletMutationState state, void NotifyAnimationWorkletStateChange(AnimationWorkletMutationState state,
ElementListType tree_type) override {} ElementListType tree_type) override {}
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override {}
void reset_did_request_impl_side_invalidation() { void reset_did_request_impl_side_invalidation() {
did_request_impl_side_invalidation_ = false; did_request_impl_side_invalidation_ = false;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "cc/scheduler/begin_frame_tracker.h" #include "cc/scheduler/begin_frame_tracker.h"
#include "cc/scheduler/commit_earlyout_reason.h" #include "cc/scheduler/commit_earlyout_reason.h"
#include "cc/scheduler/draw_result.h" #include "cc/scheduler/draw_result.h"
#include "cc/scheduler/scheduler.h"
#include "cc/scheduler/video_frame_controller.h" #include "cc/scheduler/video_frame_controller.h"
#include "cc/tiles/decoded_image_tracker.h" #include "cc/tiles/decoded_image_tracker.h"
#include "cc/tiles/image_decode_cache.h" #include "cc/tiles/image_decode_cache.h"
...@@ -160,6 +161,9 @@ class LayerTreeHostImplClient { ...@@ -160,6 +161,9 @@ class LayerTreeHostImplClient {
AnimationWorkletMutationState state, AnimationWorkletMutationState state,
ElementListType tree_type) = 0; ElementListType tree_type) = 0;
virtual void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) = 0;
protected: protected:
virtual ~LayerTreeHostImplClient() {} virtual ~LayerTreeHostImplClient() {}
}; };
......
...@@ -229,6 +229,8 @@ class LayerTreeHostImplTest : public testing::Test, ...@@ -229,6 +229,8 @@ class LayerTreeHostImplTest : public testing::Test,
const gfx::PresentationFeedback& feedback) override {} const gfx::PresentationFeedback& feedback) override {}
void NotifyAnimationWorkletStateChange(AnimationWorkletMutationState state, void NotifyAnimationWorkletStateChange(AnimationWorkletMutationState state,
ElementListType tree_type) override {} ElementListType tree_type) override {}
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override {}
void set_reduce_memory_result(bool reduce_memory_result) { void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result; reduce_memory_result_ = reduce_memory_result;
......
...@@ -512,6 +512,12 @@ void ProxyImpl::NotifyAnimationWorkletStateChange( ...@@ -512,6 +512,12 @@ void ProxyImpl::NotifyAnimationWorkletStateChange(
tree_type); tree_type);
} }
void ProxyImpl::NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) {
DCHECK(IsImplThread());
scheduler_->NotifyPaintWorkletStateChange(state);
}
bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
DCHECK(IsImplThread()); DCHECK(IsImplThread());
return host_impl_->WillBeginImplFrame(args); return host_impl_->WillBeginImplFrame(args);
......
...@@ -112,6 +112,8 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, ...@@ -112,6 +112,8 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void NotifyAnimationWorkletStateChange( void NotifyAnimationWorkletStateChange(
AnimationWorkletMutationState state, AnimationWorkletMutationState state,
ElementListType element_list_type) override; ElementListType element_list_type) override;
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override;
// SchedulerClient implementation // SchedulerClient implementation
bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override; bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
......
...@@ -533,6 +533,12 @@ void SingleThreadProxy::NotifyAnimationWorkletStateChange( ...@@ -533,6 +533,12 @@ void SingleThreadProxy::NotifyAnimationWorkletStateChange(
layer_tree_host_->NotifyAnimationWorkletStateChange(state, element_list_type); layer_tree_host_->NotifyAnimationWorkletStateChange(state, element_list_type);
} }
void SingleThreadProxy::NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) {
// Off-Thread PaintWorklet is only supported on the threaded compositor.
NOTREACHED();
}
void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) {
if (scheduler_on_impl_thread_) { if (scheduler_on_impl_thread_) {
scheduler_on_impl_thread_->SetMainThreadWantsBeginMainFrameNotExpected( scheduler_on_impl_thread_->SetMainThreadWantsBeginMainFrameNotExpected(
......
...@@ -134,6 +134,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy, ...@@ -134,6 +134,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void NotifyAnimationWorkletStateChange( void NotifyAnimationWorkletStateChange(
AnimationWorkletMutationState state, AnimationWorkletMutationState state,
ElementListType element_list_type) override; ElementListType element_list_type) override;
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override;
void RequestNewLayerTreeFrameSink(); void RequestNewLayerTreeFrameSink();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment