0

[cc] Add a way to request an "urgent" main frame

When main frame updates are throttled to a lower framerate than impl
frames, some use cases may regress. This is partly intentionnal, for
instance requestAnimationFrame() should run at a lower
framerate. However, some updates (e.g. reacting to an input event) would
benefit from being processed at the next opportunity.

This CL adds the CC-side mechanism for it. It is split from a larger CL,
which adds the blink-side plumbing, as well as use cases.

Bug: 379678455
Change-Id: Idc662418829fb33e8d534063cc888a9572070526
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6163465
Commit-Queue: Benoit Lize <lizeb@chromium.org>
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1407060}
This commit is contained in:
Benoît Lizé
2025-01-15 17:31:43 -08:00
committed by Chromium LUCI CQ
parent 10fe5d4663
commit eab48df497
18 changed files with 161 additions and 81 deletions

@@ -164,8 +164,8 @@ void Scheduler::NotifyPaintWorkletStateChange(PaintWorkletState state) {
ProcessScheduledActions(); ProcessScheduledActions();
} }
void Scheduler::SetNeedsBeginMainFrame() { void Scheduler::SetNeedsBeginMainFrame(bool now) {
state_machine_.SetNeedsBeginMainFrame(); state_machine_.SetNeedsBeginMainFrame(now);
ProcessScheduledActions(); ProcessScheduledActions();
} }

@@ -154,7 +154,12 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
// 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
// timing. // timing.
void SetNeedsBeginMainFrame(); //
// Set `now` to true if the BeginMainFrame() should not be throttled, but
// happen as the next opportunity. This is useful when main frame updates are
// running at a lower rate than compositor frames, but we don't want to wait
// (e.g. there is an input event).
void SetNeedsBeginMainFrame(bool now = false);
// Requests a single impl frame (after the current frame if there is one // Requests a single impl frame (after the current frame if there is one
// active). // active).

@@ -1612,8 +1612,12 @@ bool SchedulerStateMachine::ImplLatencyTakesPriority() const {
return false; return false;
} }
void SchedulerStateMachine::SetNeedsBeginMainFrame() { void SchedulerStateMachine::SetNeedsBeginMainFrame(bool now) {
needs_begin_main_frame_ = true; needs_begin_main_frame_ = true;
if (now) {
last_sent_begin_main_frame_time_ = base::TimeTicks();
}
} }
void SchedulerStateMachine::SetNeedsOneBeginImplFrame() { void SchedulerStateMachine::SetNeedsOneBeginImplFrame() {
@@ -1647,7 +1651,7 @@ void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) {
// the next BeginMainFrame after the deferred commit timeout will cause // the next BeginMainFrame after the deferred commit timeout will cause
// a commit, but it might come later than optimal. // a commit, but it might come later than optimal.
begin_main_frame_state_ = BeginMainFrameState::IDLE; begin_main_frame_state_ = BeginMainFrameState::IDLE;
SetNeedsBeginMainFrame(); SetNeedsBeginMainFrame(/* now = */ false);
break; break;
case CommitEarlyOutReason::kFinishedNoUpdates: case CommitEarlyOutReason::kFinishedNoUpdates:
WillCommit(/*commit_had_no_updates=*/true); WillCommit(/*commit_had_no_updates=*/true);
@@ -1662,7 +1666,7 @@ void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) {
case CommitEarlyOutReason::kAbortedNotVisible: case CommitEarlyOutReason::kAbortedNotVisible:
case CommitEarlyOutReason::kAbortedDeferredMainFrameUpdate: case CommitEarlyOutReason::kAbortedDeferredMainFrameUpdate:
case CommitEarlyOutReason::kAbortedDeferredCommit: case CommitEarlyOutReason::kAbortedDeferredCommit:
SetNeedsBeginMainFrame(); SetNeedsBeginMainFrame(/* now = */ false);
break; break;
case CommitEarlyOutReason::kFinishedNoUpdates: case CommitEarlyOutReason::kFinishedNoUpdates:
commit_count_++; commit_count_++;

@@ -271,7 +271,13 @@ class CC_EXPORT SchedulerStateMachine {
// Indicates that a new begin main frame flow needs to be performed, either // Indicates that a new begin main frame flow needs to be performed, either
// to pull updates from the main thread to the impl, or to push deltas from // to pull updates from the main thread to the impl, or to push deltas from
// the impl thread to main. // the impl thread to main.
void SetNeedsBeginMainFrame(); //
// If `now` is true, then the BeginMainFrame() update is not throttled, and
// the next BeginMainFrame() will be sent at the next opportunity, regardless
// of the interval since the last one. This is to be used in cases where
// `SetThrottleMainFrames()` has been called, and we have an "urgent" update
// that should not wait more than necessary.
void SetNeedsBeginMainFrame(bool now = false);
bool needs_begin_main_frame() const { return needs_begin_main_frame_; } bool needs_begin_main_frame() const { return needs_begin_main_frame_; }
void SetMainThreadWantsBeginMainFrameNotExpectedMessages(bool new_state); void SetMainThreadWantsBeginMainFrameNotExpectedMessages(bool new_state);

@@ -1,22 +1,24 @@
// Copyright 2011 The Chromium Authors // Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "cc/scheduler/scheduler_state_machine.h" #include "cc/scheduler/scheduler_state_machine.h"
#include <stddef.h> #include <stddef.h>
#include <array> #include <array>
#include "base/time/time.h"
#include "cc/metrics/begin_main_frame_metrics.h"
#ifdef UNSAFE_BUFFERS_BUILD #ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. // TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers #pragma allow_unsafe_buffers
#endif #endif
#include "base/test/gtest_util.h" #include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "cc/base/features.h" #include "cc/base/features.h"
#include "cc/metrics/begin_main_frame_metrics.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"
#include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/begin_frame_args_test.h"
@@ -1153,6 +1155,51 @@ TEST(SchedulerStateMachineTest, TestFullCycle) {
EXPECT_FALSE(state.needs_redraw()); EXPECT_FALSE(state.needs_redraw());
} }
namespace {
bool RunOneFrameAndReturnWhetherMainFrameIsIssued(StateMachine& state) {
state.IssueNextBeginImplFrame();
// If we send a BeginMainFrame(), simulate the fast path, where main is fast
// enough to catch the next deadline.
bool send_begin_main_frame = state.ShouldSendBeginMainFrame();
if (state.ShouldSendBeginMainFrame()) {
send_begin_main_frame = true;
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME);
EXPECT_MAIN_FRAME_STATE(SchedulerStateMachine::BeginMainFrameState::SENT);
EXPECT_FALSE(state.NeedsCommit());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
state.NotifyReadyToCommit();
EXPECT_MAIN_FRAME_STATE(
SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::POST_COMMIT);
state.NotifyReadyToActivate();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
EXPECT_TRUE(state.active_tree_needs_first_draw());
EXPECT_TRUE(state.needs_redraw());
} else {
// Still need to require a draw, otherwise nothing will happen below.
state.SetNeedsRedraw(true);
}
// Expect to do nothing until BeginImplFrame deadline
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::DRAW_IF_POSSIBLE);
state.DidSubmitCompositorFrame();
state.DidReceiveCompositorFrameAck();
// Should be synchronized, no draw needed, no action needed.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
EXPECT_MAIN_FRAME_STATE(SchedulerStateMachine::BeginMainFrameState::IDLE);
EXPECT_FALSE(state.needs_redraw());
return send_begin_main_frame;
}
} // namespace
TEST(SchedulerStateMachineTest, TestMainFrameThrottling) { TEST(SchedulerStateMachineTest, TestMainFrameThrottling) {
SchedulerSettings default_scheduler_settings; SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings); StateMachine state(default_scheduler_settings);
@@ -1164,50 +1211,55 @@ TEST(SchedulerStateMachineTest, TestMainFrameThrottling) {
int begin_main_frame_count = 0; int begin_main_frame_count = 0;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
state.SetNeedsBeginMainFrame(); state.SetNeedsBeginMainFrame();
state.IssueNextBeginImplFrame(); begin_main_frame_count +=
RunOneFrameAndReturnWhetherMainFrameIsIssued(state) ? 1 : 0;
// If we send a BeginMainFrame(), simulate the fast path, where main is fast
// enough to catch the next deadline.
if (state.ShouldSendBeginMainFrame()) {
begin_main_frame_count += 1;
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME);
EXPECT_MAIN_FRAME_STATE(SchedulerStateMachine::BeginMainFrameState::SENT);
EXPECT_FALSE(state.NeedsCommit());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
state.NotifyReadyToCommit();
EXPECT_MAIN_FRAME_STATE(
SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::POST_COMMIT);
state.NotifyReadyToActivate();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
EXPECT_TRUE(state.active_tree_needs_first_draw());
EXPECT_TRUE(state.needs_redraw());
} else {
// Still need to require a draw, otherwise nothing will happen below.
state.SetNeedsRedraw(true);
}
// Expect to do nothing until BeginImplFrame deadline
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::DRAW_IF_POSSIBLE);
state.DidSubmitCompositorFrame();
state.DidReceiveCompositorFrameAck();
// Should be synchronized, no draw needed, no action needed.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
EXPECT_MAIN_FRAME_STATE(SchedulerStateMachine::BeginMainFrameState::IDLE);
EXPECT_FALSE(state.needs_redraw());
state.AdvanceTimeBy(base::Hertz(120)); state.AdvanceTimeBy(base::Hertz(120));
} }
EXPECT_EQ(begin_main_frame_count, 5); EXPECT_EQ(begin_main_frame_count, 5);
} }
TEST(SchedulerStateMachineTest, TestMainFrameThrottlingWithUrgentUpdates) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
SET_UP_STATE(state);
state.SetThrottleMainFrames(base::Hertz(60));
state.AdvanceTimeBy(base::Seconds(1280)); // Start at an arbitrary point.
int begin_main_frame_count = 0;
for (int i = 0; i < 10; i++) {
state.SetNeedsBeginMainFrame(true);
begin_main_frame_count +=
RunOneFrameAndReturnWhetherMainFrameIsIssued(state) ? 1 : 0;
state.AdvanceTimeBy(base::Hertz(120));
}
// All the urgent updates result in main frames.
EXPECT_EQ(begin_main_frame_count, 10);
begin_main_frame_count = 0;
for (int i = 0; i < 10; i++) {
state.SetNeedsBeginMainFrame(false);
state.SetNeedsBeginMainFrame(true);
begin_main_frame_count +=
RunOneFrameAndReturnWhetherMainFrameIsIssued(state) ? 1 : 0;
state.AdvanceTimeBy(base::Hertz(120));
}
// Same when we get multiple requests in a frame, one urgent.
EXPECT_EQ(begin_main_frame_count, 10);
begin_main_frame_count = 0;
for (int i = 0; i < 10; i++) {
bool urgent = i < 3;
state.SetNeedsBeginMainFrame(urgent);
begin_main_frame_count +=
RunOneFrameAndReturnWhetherMainFrameIsIssued(state) ? 1 : 0;
state.AdvanceTimeBy(base::Hertz(120));
}
// One extra main frame, doesn't make all the subsequent ones urgent.
EXPECT_EQ(begin_main_frame_count, 5 + 1);
}
TEST(SchedulerStateMachineTest, CommitWithoutDrawWithPendingTree) { TEST(SchedulerStateMachineTest, CommitWithoutDrawWithPendingTree) {
SchedulerSettings default_scheduler_settings; SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings); StateMachine state(default_scheduler_settings);

@@ -25,7 +25,7 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient {
void NotifyReadyToDraw() override; void NotifyReadyToDraw() override;
void SetNeedsRedrawOnImplThread() override {} void SetNeedsRedrawOnImplThread() override {}
void SetNeedsOneBeginImplFrameOnImplThread() override {} void SetNeedsOneBeginImplFrameOnImplThread() override {}
void SetNeedsCommitOnImplThread() override {} void SetNeedsCommitOnImplThread(bool urgent) override {}
void SetNeedsPrepareTilesOnImplThread() override {} void SetNeedsPrepareTilesOnImplThread() override {}
void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {} void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {}
void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override {} void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override {}

@@ -31,7 +31,7 @@ class FakeProxy : public Proxy {
void ReleaseLayerTreeFrameSink() override {} void ReleaseLayerTreeFrameSink() override {}
void SetShouldWarmUp() override {} void SetShouldWarmUp() override {}
void SetVisible(bool visible) override {} void SetVisible(bool visible) override {}
void SetNeedsAnimate() override {} void SetNeedsAnimate(bool urgent) override {}
void SetNeedsUpdateLayers() override {} void SetNeedsUpdateLayers() override {}
void SetNeedsCommit() override {} void SetNeedsCommit() override {}
void SetNeedsRedraw(const gfx::Rect& damage_rect) override {} void SetNeedsRedraw(const gfx::Rect& damage_rect) override {}

@@ -130,7 +130,7 @@ class LayerTreeHostImplClient {
// LayerTreeHostImpl's SetNeedsRedraw() and SetNeedsOneBeginImplFrame(). // LayerTreeHostImpl's SetNeedsRedraw() and SetNeedsOneBeginImplFrame().
virtual void SetNeedsRedrawOnImplThread() = 0; virtual void SetNeedsRedrawOnImplThread() = 0;
virtual void SetNeedsOneBeginImplFrameOnImplThread() = 0; virtual void SetNeedsOneBeginImplFrameOnImplThread() = 0;
virtual void SetNeedsCommitOnImplThread() = 0; virtual void SetNeedsCommitOnImplThread(bool urgent = false) = 0;
virtual void SetNeedsPrepareTilesOnImplThread() = 0; virtual void SetNeedsPrepareTilesOnImplThread() = 0;
virtual void SetVideoNeedsBeginFrames(bool needs_begin_frames) = 0; virtual void SetVideoNeedsBeginFrames(bool needs_begin_frames) = 0;
virtual void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) = 0; virtual void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) = 0;

@@ -246,7 +246,9 @@ class LayerTreeHostImplTestBase : public testing::Test,
void SetNeedsPrepareTilesOnImplThread() override { void SetNeedsPrepareTilesOnImplThread() override {
did_request_prepare_tiles_ = true; did_request_prepare_tiles_ = true;
} }
void SetNeedsCommitOnImplThread() override { did_request_commit_ = true; } void SetNeedsCommitOnImplThread(bool urgent) override {
did_request_commit_ = true;
}
void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {} void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {}
void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override {} void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override {}
bool IsInsideDraw() override { return false; } bool IsInsideDraw() override { return false; }

@@ -47,7 +47,7 @@ class CC_EXPORT Proxy {
virtual void SetVisible(bool visible) = 0; virtual void SetVisible(bool visible) = 0;
virtual void SetShouldWarmUp() = 0; virtual void SetShouldWarmUp() = 0;
virtual void SetNeedsAnimate() = 0; virtual void SetNeedsAnimate(bool urgent = false) = 0;
virtual void SetNeedsUpdateLayers() = 0; virtual void SetNeedsUpdateLayers() = 0;
virtual void SetNeedsCommit() = 0; virtual void SetNeedsCommit() = 0;
virtual void SetNeedsRedraw(const gfx::Rect& damage_rect) = 0; virtual void SetNeedsRedraw(const gfx::Rect& damage_rect) = 0;

@@ -239,8 +239,8 @@ void ProxyImpl::SetNeedsRedrawOnImpl(const gfx::Rect& damage_rect) {
SetNeedsRedrawOnImplThread(); SetNeedsRedrawOnImplThread();
} }
void ProxyImpl::SetNeedsCommitOnImpl() { void ProxyImpl::SetNeedsCommitOnImpl(bool urgent) {
SetNeedsCommitOnImplThread(); SetNeedsCommitOnImplThread(urgent);
} }
void ProxyImpl::SetTargetLocalSurfaceIdOnImpl( void ProxyImpl::SetTargetLocalSurfaceIdOnImpl(
@@ -413,7 +413,7 @@ void ProxyImpl::NotifyReadyToCommitOnImpl(
// ApplyCompositorChanges. This means we are guaranteed to run another main // ApplyCompositorChanges. This means we are guaranteed to run another main
// frame after FCP, which is good because there may be hover effects to apply. // frame after FCP, which is good because there may be hover effects to apply.
if (!scroll_and_viewport_changes_synced) if (!scroll_and_viewport_changes_synced)
scheduler_->SetNeedsBeginMainFrame(); scheduler_->SetNeedsBeginMainFrame(false);
} }
void ProxyImpl::DidLoseLayerTreeFrameSinkOnImplThread() { void ProxyImpl::DidLoseLayerTreeFrameSinkOnImplThread() {
@@ -488,10 +488,10 @@ void ProxyImpl::SetNeedsPrepareTilesOnImplThread() {
scheduler_->SetNeedsPrepareTiles(); scheduler_->SetNeedsPrepareTiles();
} }
void ProxyImpl::SetNeedsCommitOnImplThread() { void ProxyImpl::SetNeedsCommitOnImplThread(bool urgent) {
TRACE_EVENT0("cc", "ProxyImpl::SetNeedsCommitOnImplThread"); TRACE_EVENT0("cc", "ProxyImpl::SetNeedsCommitOnImplThread");
DCHECK(IsImplThread()); DCHECK(IsImplThread());
scheduler_->SetNeedsBeginMainFrame(); scheduler_->SetNeedsBeginMainFrame(urgent);
} }
void ProxyImpl::SetVideoNeedsBeginFrames(bool needs_begin_frames) { void ProxyImpl::SetVideoNeedsBeginFrames(bool needs_begin_frames) {
@@ -622,7 +622,7 @@ void ProxyImpl::NotifyImageDecodeRequestFinished(int request_id,
base::BindOnce(&ProxyMain::NotifyImageDecodeRequestFinished, base::BindOnce(&ProxyMain::NotifyImageDecodeRequestFinished,
proxy_main_weak_ptr_, request_id, decode_succeeded)); proxy_main_weak_ptr_, request_id, decode_succeeded));
} else { } else {
SetNeedsCommitOnImplThread(); SetNeedsCommitOnImplThread(/* urgent= */ false);
} }
} }

@@ -61,7 +61,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void SetDeferBeginMainFrameFromMain(bool defer_begin_main_frame); void SetDeferBeginMainFrameFromMain(bool defer_begin_main_frame);
void SetPauseRendering(bool pause_rendering); void SetPauseRendering(bool pause_rendering);
void SetNeedsRedrawOnImpl(const gfx::Rect& damage_rect); void SetNeedsRedrawOnImpl(const gfx::Rect& damage_rect);
void SetNeedsCommitOnImpl(); void SetNeedsCommitOnImpl(bool urgent);
void SetTargetLocalSurfaceIdOnImpl( void SetTargetLocalSurfaceIdOnImpl(
const viz::LocalSurfaceId& target_local_surface_id); const viz::LocalSurfaceId& target_local_surface_id);
void BeginMainFrameAbortedOnImpl( void BeginMainFrameAbortedOnImpl(
@@ -115,7 +115,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void SetNeedsRedrawOnImplThread() override; void SetNeedsRedrawOnImplThread() override;
void SetNeedsOneBeginImplFrameOnImplThread() override; void SetNeedsOneBeginImplFrameOnImplThread() override;
void SetNeedsPrepareTilesOnImplThread() override; void SetNeedsPrepareTilesOnImplThread() override;
void SetNeedsCommitOnImplThread() override; void SetNeedsCommitOnImplThread(bool urgent) override;
void SetVideoNeedsBeginFrames(bool needs_begin_frames) override; void SetVideoNeedsBeginFrames(bool needs_begin_frames) override;
void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override; void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override;
bool IsInsideDraw() override; bool IsInsideDraw() override;

@@ -195,6 +195,7 @@ void ProxyMain::BeginMainFrame(
final_pipeline_stage_ = max_requested_pipeline_stage_; final_pipeline_stage_ = max_requested_pipeline_stage_;
max_requested_pipeline_stage_ = NO_PIPELINE_STAGE; max_requested_pipeline_stage_ = NO_PIPELINE_STAGE;
has_sent_urgent_commit_request_ = false;
// If main frame updates and commits are deferred, skip the entire pipeline. // If main frame updates and commits are deferred, skip the entire pipeline.
if (defer_main_frame_update_ || pause_rendering_) { if (defer_main_frame_update_ || pause_rendering_) {
@@ -563,9 +564,9 @@ void ProxyMain::SetShouldWarmUp() {
base::Unretained(proxy_impl_.get()))); base::Unretained(proxy_impl_.get())));
} }
void ProxyMain::SetNeedsAnimate() { void ProxyMain::SetNeedsAnimate(bool urgent) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
if (SendCommitRequestToImplThreadIfNeeded(ANIMATE_PIPELINE_STAGE)) { if (SendCommitRequestToImplThreadIfNeeded(ANIMATE_PIPELINE_STAGE, urgent)) {
TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsAnimate", TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsAnimate",
TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_SCOPE_THREAD);
} }
@@ -579,7 +580,8 @@ void ProxyMain::SetNeedsUpdateLayers() {
std::max(final_pipeline_stage_, UPDATE_LAYERS_PIPELINE_STAGE); std::max(final_pipeline_stage_, UPDATE_LAYERS_PIPELINE_STAGE);
return; return;
} }
if (SendCommitRequestToImplThreadIfNeeded(UPDATE_LAYERS_PIPELINE_STAGE)) { if (SendCommitRequestToImplThreadIfNeeded(UPDATE_LAYERS_PIPELINE_STAGE,
/* urgent = */ false)) {
TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsUpdateLayers", TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsUpdateLayers",
TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_SCOPE_THREAD);
} }
@@ -595,7 +597,8 @@ void ProxyMain::SetNeedsCommit() {
std::max(final_pipeline_stage_, COMMIT_PIPELINE_STAGE); std::max(final_pipeline_stage_, COMMIT_PIPELINE_STAGE);
return; return;
} }
if (SendCommitRequestToImplThreadIfNeeded(COMMIT_PIPELINE_STAGE)) { if (SendCommitRequestToImplThreadIfNeeded(COMMIT_PIPELINE_STAGE,
/* urgent = */ false)) {
TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsCommit", TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsCommit",
TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_SCOPE_THREAD);
} }
@@ -864,18 +867,21 @@ void ProxyMain::RequestBeginMainFrameNotExpected(bool new_state) {
} }
bool ProxyMain::SendCommitRequestToImplThreadIfNeeded( bool ProxyMain::SendCommitRequestToImplThreadIfNeeded(
CommitPipelineStage required_stage) { CommitPipelineStage required_stage,
bool urgent) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
DCHECK_NE(NO_PIPELINE_STAGE, required_stage); DCHECK_NE(NO_PIPELINE_STAGE, required_stage);
bool already_posted = max_requested_pipeline_stage_ != NO_PIPELINE_STAGE; bool already_posted = max_requested_pipeline_stage_ != NO_PIPELINE_STAGE;
max_requested_pipeline_stage_ = max_requested_pipeline_stage_ =
std::max(max_requested_pipeline_stage_, required_stage); std::max(max_requested_pipeline_stage_, required_stage);
if (already_posted) if (already_posted && (!urgent || has_sent_urgent_commit_request_)) {
return false; return false;
}
ImplThreadTaskRunner()->PostTask( ImplThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyImpl::SetNeedsCommitOnImpl, FROM_HERE, base::BindOnce(&ProxyImpl::SetNeedsCommitOnImpl,
base::Unretained(proxy_impl_.get()))); base::Unretained(proxy_impl_.get()), urgent));
layer_tree_host_->OnCommitRequested(); layer_tree_host_->OnCommitRequested();
has_sent_urgent_commit_request_ |= urgent;
return true; return true;
} }

@@ -95,7 +95,7 @@ class CC_EXPORT ProxyMain : public Proxy {
LayerTreeFrameSink* layer_tree_frame_sink) override; LayerTreeFrameSink* layer_tree_frame_sink) override;
void SetVisible(bool visible) override; void SetVisible(bool visible) override;
void SetShouldWarmUp() override; void SetShouldWarmUp() override;
void SetNeedsAnimate() override; void SetNeedsAnimate(bool urgent) override;
void SetNeedsUpdateLayers() override; void SetNeedsUpdateLayers() override;
void SetNeedsCommit() override; void SetNeedsCommit() override;
void SetNeedsRedraw(const gfx::Rect& damage_rect) override; void SetNeedsRedraw(const gfx::Rect& damage_rect) override;
@@ -138,8 +138,8 @@ class CC_EXPORT ProxyMain : public Proxy {
// Returns |true| if the request was actually sent, |false| if one was // Returns |true| if the request was actually sent, |false| if one was
// already outstanding. // already outstanding.
bool SendCommitRequestToImplThreadIfNeeded( bool SendCommitRequestToImplThreadIfNeeded(CommitPipelineStage required_stage,
CommitPipelineStage required_stage); bool urgent);
bool IsMainThread() const; bool IsMainThread() const;
bool IsImplThread() const; bool IsImplThread() const;
base::SingleThreadTaskRunner* ImplThreadTaskRunner(); base::SingleThreadTaskRunner* ImplThreadTaskRunner();
@@ -170,6 +170,11 @@ class CC_EXPORT ProxyMain : public Proxy {
// deferred. // deferred.
CommitPipelineStage deferred_final_pipeline_stage_; CommitPipelineStage deferred_final_pipeline_stage_;
// Commit requests are deduplicated, however if we requested a regular commit
// request, then get an "urgent" request later, we should inform impl that the
// request became urgent.
bool has_sent_urgent_commit_request_ = false;
// Set when the Proxy is started using Proxy::Start() and reset when it is // Set when the Proxy is started using Proxy::Start() and reset when it is
// stopped using Proxy::Stop(). // stopped using Proxy::Stop().
bool started_; bool started_;

@@ -192,7 +192,7 @@ void SingleThreadProxy::SetLayerTreeFrameSink(
} }
} }
void SingleThreadProxy::SetNeedsAnimate() { void SingleThreadProxy::SetNeedsAnimate(bool urgent) {
TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsAnimate"); TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsAnimate");
DCHECK(task_runner_provider_->IsMainThread()); DCHECK(task_runner_provider_->IsMainThread());
if (animate_requested_) if (animate_requested_)
@@ -200,7 +200,7 @@ void SingleThreadProxy::SetNeedsAnimate() {
animate_requested_ = true; animate_requested_ = true;
DebugScopedSetImplThread impl(task_runner_provider_); DebugScopedSetImplThread impl(task_runner_provider_);
if (scheduler_on_impl_thread_) if (scheduler_on_impl_thread_)
scheduler_on_impl_thread_->SetNeedsBeginMainFrame(); scheduler_on_impl_thread_->SetNeedsBeginMainFrame(urgent);
layer_tree_host_->OnCommitRequested(); layer_tree_host_->OnCommitRequested();
} }
@@ -210,7 +210,7 @@ void SingleThreadProxy::SetNeedsUpdateLayers() {
if (!RequestedAnimatePending()) { if (!RequestedAnimatePending()) {
DebugScopedSetImplThread impl(task_runner_provider_); DebugScopedSetImplThread impl(task_runner_provider_);
if (scheduler_on_impl_thread_) if (scheduler_on_impl_thread_)
scheduler_on_impl_thread_->SetNeedsBeginMainFrame(); scheduler_on_impl_thread_->SetNeedsBeginMainFrame(/* urgent = */ false);
} }
update_layers_requested_ = true; update_layers_requested_ = true;
} }
@@ -305,7 +305,7 @@ void SingleThreadProxy::SetNeedsCommit() {
commit_requested_ = true; commit_requested_ = true;
DebugScopedSetImplThread impl(task_runner_provider_); DebugScopedSetImplThread impl(task_runner_provider_);
if (scheduler_on_impl_thread_) if (scheduler_on_impl_thread_)
scheduler_on_impl_thread_->SetNeedsBeginMainFrame(); scheduler_on_impl_thread_->SetNeedsBeginMainFrame(/* urgent = */ false);
} }
void SingleThreadProxy::SetNeedsRedraw(const gfx::Rect& damage_rect) { void SingleThreadProxy::SetNeedsRedraw(const gfx::Rect& damage_rect) {
@@ -537,12 +537,12 @@ void SingleThreadProxy::SetNeedsPrepareTilesOnImplThread() {
scheduler_on_impl_thread_->SetNeedsPrepareTiles(); scheduler_on_impl_thread_->SetNeedsPrepareTiles();
} }
void SingleThreadProxy::SetNeedsCommitOnImplThread() { void SingleThreadProxy::SetNeedsCommitOnImplThread(bool urgent) {
DCHECK(!task_runner_provider_->HasImplThread() || DCHECK(!task_runner_provider_->HasImplThread() ||
task_runner_provider_->IsImplThread()); task_runner_provider_->IsImplThread());
single_thread_client_->ScheduleAnimationForWebTests(); single_thread_client_->ScheduleAnimationForWebTests();
if (scheduler_on_impl_thread_) if (scheduler_on_impl_thread_)
scheduler_on_impl_thread_->SetNeedsBeginMainFrame(); scheduler_on_impl_thread_->SetNeedsBeginMainFrame(urgent);
commit_requested_ = true; commit_requested_ = true;
} }
@@ -667,7 +667,7 @@ void SingleThreadProxy::NotifyImageDecodeRequestFinished(
DebugScopedSetMainThread main_thread(task_runner_provider_); DebugScopedSetMainThread main_thread(task_runner_provider_);
IssueImageDecodeFinishedCallbacks(); IssueImageDecodeFinishedCallbacks();
} else { } else {
SetNeedsCommitOnImplThread(); SetNeedsCommitOnImplThread(/* urgent = */ false);
} }
} }
} }

@@ -55,7 +55,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void ReleaseLayerTreeFrameSink() override; void ReleaseLayerTreeFrameSink() override;
void SetVisible(bool visible) override; void SetVisible(bool visible) override;
void SetShouldWarmUp() override; void SetShouldWarmUp() override;
void SetNeedsAnimate() override; void SetNeedsAnimate(bool urgent) override;
void SetNeedsUpdateLayers() override; void SetNeedsUpdateLayers() override;
void SetNeedsCommit() override; void SetNeedsCommit() override;
void SetNeedsRedraw(const gfx::Rect& damage_rect) override; void SetNeedsRedraw(const gfx::Rect& damage_rect) override;
@@ -131,7 +131,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void SetNeedsRedrawOnImplThread() override; void SetNeedsRedrawOnImplThread() override;
void SetNeedsOneBeginImplFrameOnImplThread() override; void SetNeedsOneBeginImplFrameOnImplThread() override;
void SetNeedsPrepareTilesOnImplThread() override; void SetNeedsPrepareTilesOnImplThread() override;
void SetNeedsCommitOnImplThread() override; void SetNeedsCommitOnImplThread(bool urgent) override;
void SetVideoNeedsBeginFrames(bool needs_begin_frames) override; void SetVideoNeedsBeginFrames(bool needs_begin_frames) override;
void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override {} void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override {}
bool IsInsideDraw() override; bool IsInsideDraw() override;

@@ -894,7 +894,7 @@ void LayerContextImpl::SetNeedsPrepareTilesOnImplThread() {
NOTREACHED(); NOTREACHED();
} }
void LayerContextImpl::SetNeedsCommitOnImplThread() { void LayerContextImpl::SetNeedsCommitOnImplThread(bool urgent) {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }

@@ -59,7 +59,7 @@ class LayerContextImpl : public cc::LayerTreeHostImplClient,
void SetNeedsRedrawOnImplThread() override; void SetNeedsRedrawOnImplThread() override;
void SetNeedsOneBeginImplFrameOnImplThread() override; void SetNeedsOneBeginImplFrameOnImplThread() override;
void SetNeedsPrepareTilesOnImplThread() override; void SetNeedsPrepareTilesOnImplThread() override;
void SetNeedsCommitOnImplThread() override; void SetNeedsCommitOnImplThread(bool urgent) override;
void SetVideoNeedsBeginFrames(bool needs_begin_frames) override; void SetVideoNeedsBeginFrames(bool needs_begin_frames) override;
void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override; void SetDeferBeginMainFrameFromImpl(bool defer_begin_main_frame) override;
bool IsInsideDraw() override; bool IsInsideDraw() override;