summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorboliu <boliu@chromium.org>2016-01-13 11:27:37 -0800
committerCommit bot <commit-bot@chromium.org>2016-01-13 19:28:58 +0000
commit0c1f91d8ae18676091fbe783beded96f862ab9b0 (patch)
tree3cd8dae324858a033be538442b71e7532b043ebb
parent2a18021a2ac515497ae64bd9535d386fb6da890b (diff)
downloadchromium_src-0c1f91d8ae18676091fbe783beded96f862ab9b0.zip
chromium_src-0c1f91d8ae18676091fbe783beded96f862ab9b0.tar.gz
chromium_src-0c1f91d8ae18676091fbe783beded96f862ab9b0.tar.bz2
Add begin frame paused signal
This avoids a deadlock situation in android webview where begin frames have stopped, but the blink main thread is blocked indefinitely waiting for activation. See bug for details. Plumb a signal that the BeginFrameSource is paused directly through BeginFrameSourceObserver, and force activate any pending activations when paused. This is similar to when compositor becomes invisible, but BFS allows webview send the signal without a hop to blink main thread. BUG=539373 CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel Review URL: https://codereview.chromium.org/1536353003 Cr-Commit-Position: refs/heads/master@{#369238}
-rw-r--r--cc/scheduler/begin_frame_source.cc15
-rw-r--r--cc/scheduler/begin_frame_source.h5
-rw-r--r--cc/scheduler/begin_frame_source_unittest.cc93
-rw-r--r--cc/scheduler/scheduler.cc9
-rw-r--r--cc/scheduler/scheduler.h1
-rw-r--r--cc/scheduler/scheduler_state_machine.cc28
-rw-r--r--cc/scheduler/scheduler_state_machine.h4
-rw-r--r--cc/scheduler/scheduler_state_machine_unittest.cc77
-rw-r--r--cc/scheduler/scheduler_unittest.cc33
-rw-r--r--cc/surfaces/display_scheduler.cc6
-rw-r--r--cc/surfaces/display_scheduler.h1
-rw-r--r--cc/test/begin_frame_source_test.h6
-rw-r--r--cc/test/scheduler_test_common.h2
-rw-r--r--content/browser/android/in_process/synchronous_compositor_impl.cc6
-rw-r--r--content/browser/android/synchronous_compositor_host.cc6
-rw-r--r--content/common/android/sync_compositor_messages.cc4
-rw-r--r--content/common/android/sync_compositor_messages.h2
-rw-r--r--content/renderer/android/synchronous_compositor_external_begin_frame_source.cc3
-rw-r--r--content/renderer/android/synchronous_compositor_external_begin_frame_source.h4
-rw-r--r--content/renderer/android/synchronous_compositor_proxy.cc2
20 files changed, 299 insertions, 8 deletions
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc
index 17b4a26..d96ef4e 100644
--- a/cc/scheduler/begin_frame_source.cc
+++ b/cc/scheduler/begin_frame_source.cc
@@ -53,6 +53,7 @@ void BeginFrameObserverBase::AsValueInto(
BeginFrameSourceBase::BeginFrameSourceBase()
: observer_(NULL),
needs_begin_frames_(false),
+ paused_(false),
inside_as_value_into_(false) {
DCHECK(!observer_);
DCHECK_EQ(inside_as_value_into_, false);
@@ -82,6 +83,8 @@ void BeginFrameSourceBase::AddObserver(BeginFrameObserver* obs) {
obs);
DCHECK(!observer_);
observer_ = obs;
+ if (observer_)
+ return observer_->OnBeginFrameSourcePausedChanged(paused_);
}
void BeginFrameSourceBase::RemoveObserver(BeginFrameObserver* obs) {
@@ -105,6 +108,14 @@ void BeginFrameSourceBase::CallOnBeginFrame(const BeginFrameArgs& args) {
}
}
+void BeginFrameSourceBase::SetBeginFrameSourcePaused(bool paused) {
+ if (paused_ == paused)
+ return;
+ paused_ = paused;
+ if (observer_)
+ return observer_->OnBeginFrameSourcePausedChanged(paused_);
+}
+
// Tracing support
void BeginFrameSourceBase::AsValueInto(
base::trace_event::TracedValue* dict) const {
@@ -381,6 +392,10 @@ const BeginFrameArgs BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs()
return BeginFrameArgs();
}
+void BeginFrameSourceMultiplexer::OnBeginFrameSourcePausedChanged(bool paused) {
+ BeginFrameSourceBase::SetBeginFrameSourcePaused(paused);
+}
+
// BeginFrameSource support
void BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange(
bool needs_begin_frames) {
diff --git a/cc/scheduler/begin_frame_source.h b/cc/scheduler/begin_frame_source.h
index ec8aaa5..a77d546 100644
--- a/cc/scheduler/begin_frame_source.h
+++ b/cc/scheduler/begin_frame_source.h
@@ -60,6 +60,8 @@ class CC_EXPORT BeginFrameObserver {
// preventing "double dropping" and other bad side effects.
virtual const BeginFrameArgs LastUsedBeginFrameArgs() const = 0;
+ virtual void OnBeginFrameSourcePausedChanged(bool paused) = 0;
+
// Tracing support
virtual void AsValueInto(base::trace_event::TracedValue* dict) const = 0;
};
@@ -168,6 +170,7 @@ class CC_EXPORT BeginFrameSourceBase : public BeginFrameSource {
// These methods should be used by subclasses to make the call to the
// observers.
void CallOnBeginFrame(const BeginFrameArgs& args);
+ void SetBeginFrameSourcePaused(bool paused);
// This method should be overridden if you want to change some behaviour on
// needs_begin_frames change.
@@ -175,6 +178,7 @@ class CC_EXPORT BeginFrameSourceBase : public BeginFrameSource {
BeginFrameObserver* observer_;
bool needs_begin_frames_;
+ bool paused_;
private:
bool inside_as_value_into_;
@@ -273,6 +277,7 @@ class CC_EXPORT BeginFrameSourceMultiplexer : public BeginFrameSourceBase,
// sources.
void OnBeginFrame(const BeginFrameArgs& args) override;
const BeginFrameArgs LastUsedBeginFrameArgs() const override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
// BeginFrameSource
void DidFinishFrame(size_t remaining_frames) override;
diff --git a/cc/scheduler/begin_frame_source_unittest.cc b/cc/scheduler/begin_frame_source_unittest.cc
index 43ba4fa..4591559c 100644
--- a/cc/scheduler/begin_frame_source_unittest.cc
+++ b/cc/scheduler/begin_frame_source_unittest.cc
@@ -13,6 +13,8 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::Mock;
+
namespace cc {
namespace {
@@ -20,6 +22,7 @@ namespace {
class MockMinimalBeginFrameObserverBase : public BeginFrameObserverBase {
public:
MOCK_METHOD1(OnBeginFrameDerivedImpl, bool(const BeginFrameArgs&));
+ MOCK_METHOD1(OnBeginFrameSourcePausedChanged, void(bool));
int64_t dropped_begin_frame_args() const { return dropped_begin_frame_args_; }
};
@@ -73,6 +76,7 @@ TEST(BeginFrameSourceBaseTest, ObserverManipulation) {
MockBeginFrameObserver otherObs;
FakeBeginFrameSource source;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
source.AddObserver(&obs);
EXPECT_EQ(&obs, source.GetObserver());
@@ -92,6 +96,7 @@ TEST(BeginFrameSourceBaseTest, ObserverManipulation) {
#endif
source.RemoveObserver(&obs);
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(otherObs, false);
source.AddObserver(&otherObs);
EXPECT_EQ(&otherObs, source.GetObserver());
source.RemoveObserver(&otherObs);
@@ -100,6 +105,7 @@ TEST(BeginFrameSourceBaseTest, ObserverManipulation) {
TEST(BeginFrameSourceBaseTest, Observer) {
FakeBeginFrameSource source;
MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
source.AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
EXPECT_BEGIN_FRAME_DROP(obs, 400, 600, 300);
@@ -126,6 +132,18 @@ TEST(BeginFrameSourceBaseTest, NeedsBeginFrames) {
EXPECT_FALSE(source.NeedsBeginFrames());
}
+TEST(BeginFrameSourceBaseTest, SetBeginFrameSourcePaused) {
+ FakeBeginFrameSource source;
+ MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ source.AddObserver(&obs);
+
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+ source.SetBeginFrameSourcePaused(true);
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ source.SetBeginFrameSourcePaused(false);
+}
+
class LoopingBeginFrameObserver : public BeginFrameObserverBase {
public:
BeginFrameSource* source_;
@@ -142,6 +160,8 @@ class LoopingBeginFrameObserver : public BeginFrameObserverBase {
bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override {
return true;
}
+
+ void OnBeginFrameSourcePausedChanged(bool paused) override {}
};
TEST(BeginFrameSourceBaseTest, DetectAsValueIntoLoop) {
@@ -196,6 +216,7 @@ class BackToBackBeginFrameSourceTest : public ::testing::Test {
source_ = TestBackToBackBeginFrameSource::Create(now_src_.get(),
task_runner_.get());
obs_ = make_scoped_ptr(new ::testing::StrictMock<MockBeginFrameObserver>());
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
}
@@ -363,6 +384,7 @@ class SyntheticBeginFrameSourceTest : public ::testing::Test {
now_src_.get(), task_runner_.get(),
base::TimeDelta::FromMicroseconds(10000));
obs_ = make_scoped_ptr(new MockBeginFrameObserver());
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
}
@@ -524,6 +546,7 @@ TEST_F(BeginFrameSourceMultiplexerTest, BeginFramesSimple) {
mux_->SetActiveSource(source1_);
MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
mux_->AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
@@ -543,6 +566,7 @@ TEST_F(BeginFrameSourceMultiplexerTest, BeginFramesBackwardsProtection) {
mux_->AddSource(source2_);
MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
mux_->AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
@@ -574,6 +598,7 @@ TEST_F(BeginFrameSourceMultiplexerTest, MinimumIntervalZero) {
mux_->AddSource(source1_);
MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
mux_->AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
@@ -589,6 +614,7 @@ TEST_F(BeginFrameSourceMultiplexerTest, MinimumIntervalBasic) {
mux_->AddSource(source1_);
MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
mux_->AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
@@ -604,6 +630,7 @@ TEST_F(BeginFrameSourceMultiplexerTest, MinimumIntervalWithMultipleSources) {
mux_->AddSource(source2_);
MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
mux_->AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
@@ -621,5 +648,71 @@ TEST_F(BeginFrameSourceMultiplexerTest, MinimumIntervalWithMultipleSources) {
SEND_BEGIN_FRAME_DROP(*source2_, 1100, 1400, 300);
}
+TEST_F(BeginFrameSourceMultiplexerTest, BeginFrameSourcePaused) {
+ mux_->AddSource(source1_);
+ mux_->AddSource(source2_);
+ mux_->SetActiveSource(source1_);
+
+ MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ mux_->AddObserver(&obs);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+ source1_->SetBeginFrameSourcePaused(true);
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ source1_->SetBeginFrameSourcePaused(false);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ mux_->SetActiveSource(source2_);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+ source2_->SetBeginFrameSourcePaused(true);
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ source2_->SetBeginFrameSourcePaused(false);
+}
+
+TEST_F(BeginFrameSourceMultiplexerTest,
+ BeginFrameSourcePausedUpdateOnSourceTransition) {
+ mux_->AddSource(source1_);
+ mux_->AddSource(source2_);
+ source1_->SetBeginFrameSourcePaused(true);
+ source2_->SetBeginFrameSourcePaused(false);
+ mux_->SetActiveSource(source1_);
+
+ MockBeginFrameObserver obs;
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+ mux_->AddObserver(&obs);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Paused to not paused.
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ mux_->SetActiveSource(source2_);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Not paused to paused.
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+ mux_->SetActiveSource(source1_);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+ source1_->SetBeginFrameSourcePaused(false);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Not paused to not paused.
+ mux_->SetActiveSource(source2_);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+ source2_->SetBeginFrameSourcePaused(true);
+ Mock::VerifyAndClearExpectations(&obs);
+ source1_->SetBeginFrameSourcePaused(true);
+
+ // Paused to paused.
+ mux_->SetActiveSource(source1_);
+ Mock::VerifyAndClearExpectations(&obs);
+}
+
} // namespace
} // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 0bf8f25..f6dfccb 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -297,6 +297,15 @@ void Scheduler::SetupNextBeginFrameIfNeeded() {
PostBeginRetroFrameIfNeeded();
}
+void Scheduler::OnBeginFrameSourcePausedChanged(bool paused) {
+ if (state_machine_.begin_frame_source_paused() == paused)
+ return;
+ TRACE_EVENT_INSTANT1("cc", "Scheduler::SetBeginFrameSourcePaused",
+ TRACE_EVENT_SCOPE_THREAD, "paused", paused);
+ state_machine_.SetBeginFrameSourcePaused(paused);
+ ProcessScheduledActions();
+}
+
// BeginFrame is the mechanism that tells us that now is a good time to start
// making a frame. Usually this means that user input for the frame is complete.
// If the scheduler is busy, we queue the BeginFrame to be handled later as
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index e1a067c..61a0ea8 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -66,6 +66,7 @@ class CC_EXPORT Scheduler : public BeginFrameObserverBase {
~Scheduler() override;
// BeginFrameObserverBase
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override;
void OnDrawForOutputSurface(bool resourceless_software_draw);
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 333b8edf..9bd4e16 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -42,6 +42,7 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
needs_begin_main_frame_(false),
needs_one_begin_impl_frame_(false),
visible_(false),
+ begin_frame_source_paused_(false),
resourceless_draw_(false),
can_draw_(false),
has_pending_tree_(false),
@@ -235,6 +236,7 @@ void SchedulerStateMachine::AsValueInto(
state->SetBoolean("needs_begin_main_frame", needs_begin_main_frame_);
state->SetBoolean("needs_one_begin_impl_frame", needs_one_begin_impl_frame_);
state->SetBoolean("visible", visible_);
+ state->SetBoolean("begin_frame_source_paused", begin_frame_source_paused_);
state->SetBoolean("can_draw", can_draw_);
state->SetBoolean("resourceless_draw", resourceless_draw_);
state->SetBoolean("has_pending_tree", has_pending_tree_);
@@ -264,10 +266,10 @@ void SchedulerStateMachine::AsValueInto(
}
bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
- // Normally when |visible_| is false, pending activations will be forced and
- // draws will be aborted. However, when the embedder is Android WebView,
- // software draws could be scheduled by the Android OS at any time and draws
- // should not be aborted in this case.
+ // Normally when |visible_| is false or |begin_frame_source_paused_| is true,
+ // pending activations will be forced and draws will be aborted. However,
+ // when the embedder is Android WebView, software draws could be scheduled by
+ // the Android OS at any time and draws should not be aborted in this case.
bool is_output_surface_lost = (output_surface_state_ == OUTPUT_SURFACE_NONE);
if (resourceless_draw_)
return is_output_surface_lost || !can_draw_;
@@ -278,7 +280,8 @@ bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
// This should be a superset of PendingActivationsShouldBeForced() since
// activation of the pending tree is blocked by drawing of the active tree and
// the main thread might be blocked on activation of the most recent commit.
- return is_output_surface_lost || !can_draw_ || !visible_;
+ return is_output_surface_lost || !can_draw_ || !visible_ ||
+ begin_frame_source_paused_;
}
bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
@@ -298,6 +301,11 @@ bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
if (!visible_)
return true;
+ // Force pending activations when BeginFrameSource is paused to avoid
+ // deadlocking the main thread.
+ if (begin_frame_source_paused_)
+ return true;
+
return false;
}
@@ -389,6 +397,11 @@ bool SchedulerStateMachine::CouldSendBeginMainFrame() const {
if (!visible_)
return false;
+ // There are no BeginImplFrames while BeginFrameSource is paused,
+ // so should also stop BeginMainFrames.
+ if (begin_frame_source_paused_)
+ return false;
+
// Do not make a new commits when it is deferred.
if (defer_commits_)
return false;
@@ -554,6 +567,7 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
void SchedulerStateMachine::WillSendBeginMainFrame() {
DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled);
DCHECK(visible_);
+ DCHECK(!begin_frame_source_paused_);
DCHECK(!send_begin_main_frame_funnel_);
begin_main_frame_state_ = BEGIN_MAIN_FRAME_STATE_SENT;
needs_begin_main_frame_ = false;
@@ -961,6 +975,10 @@ void SchedulerStateMachine::SetVisible(bool visible) {
wait_for_ready_to_draw_ = false;
}
+void SchedulerStateMachine::SetBeginFrameSourcePaused(bool paused) {
+ begin_frame_source_paused_ = paused;
+}
+
void SchedulerStateMachine::SetResourcelessSoftareDraw(bool resourceless_draw) {
resourceless_draw_ = resourceless_draw;
}
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 22c58d0..7444432 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -174,6 +174,9 @@ class CC_EXPORT SchedulerStateMachine {
void SetVisible(bool visible);
bool visible() const { return visible_; }
+ void SetBeginFrameSourcePaused(bool paused);
+ bool begin_frame_source_paused() const { return begin_frame_source_paused_; }
+
// Indicates that a redraw is required, either due to the impl tree changing
// or the screen being damaged and simply needing redisplay.
void SetNeedsRedraw();
@@ -336,6 +339,7 @@ class CC_EXPORT SchedulerStateMachine {
bool needs_begin_main_frame_;
bool needs_one_begin_impl_frame_;
bool visible_;
+ bool begin_frame_source_paused_;
bool resourceless_draw_;
bool can_draw_;
bool has_pending_tree_;
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index 668f515..15e172e 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -1208,6 +1208,21 @@ TEST(SchedulerStateMachineTest, TestNoRequestCommitWhenInvisible) {
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
state.SetNeedsBeginMainFrame();
+ EXPECT_FALSE(state.CouldSendBeginMainFrame());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+}
+
+TEST(SchedulerStateMachineTest, TestNoRequestCommitWhenBeginFrameSourcePaused) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ state.SetVisible(true);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetBeginFrameSourcePaused(true);
+ state.SetNeedsBeginMainFrame();
+ EXPECT_FALSE(state.CouldSendBeginMainFrame());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
@@ -1754,6 +1769,32 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
+TEST(SchedulerStateMachineTest,
+ TestFinishCommitWhenCommitInProgressAndBeginFrameSourcePaused) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ state.SetVisible(true);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetBeginFrameSourcePaused(true);
+ state.SetBeginMainFrameState(
+ SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_SENT);
+ state.SetNeedsBeginMainFrame();
+
+ // After the commit completes, activation and draw happen immediately
+ // because we are not visible.
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE);
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+}
+
TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
@@ -1797,6 +1838,10 @@ TEST(SchedulerStateMachineTest, ReportIfNotDrawing) {
state.SetCanDraw(true);
state.SetVisible(true);
+ state.SetBeginFrameSourcePaused(true);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+
+ state.SetBeginFrameSourcePaused(false);
EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
}
@@ -1815,6 +1860,17 @@ TEST(SchedulerStateMachineTest, ForceDrawForResourcelessSoftwareDraw) {
state.SetResourcelessSoftareDraw(true);
EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+ state.SetVisible(true);
+
+ state.SetBeginFrameSourcePaused(true);
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+
+ state.SetResourcelessSoftareDraw(false);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+
+ state.SetResourcelessSoftareDraw(true);
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+ state.SetBeginFrameSourcePaused(false);
state.SetVisible(false);
state.DidLoseOutputSurface();
@@ -1980,6 +2036,27 @@ TEST(SchedulerStateMachineTest, TestTriggerDeadlineImmediatelyWhenInvisible) {
state.SetVisible(false);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
+}
+
+TEST(SchedulerStateMachineTest,
+ TestTriggerDeadlineImmediatelyWhenBeginFrameSourcePaused) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ SET_UP_STATE(state)
+
+ state.SetNeedsBeginMainFrame();
+
+ state.OnBeginImplFrame();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
+
+ state.SetBeginFrameSourcePaused(true);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
}
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 7f577a6..9a30c34 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -232,6 +232,11 @@ class FakeExternalBeginFrameSource : public BeginFrameSourceBase {
return CallOnBeginFrame(args);
}
+ void SetPaused(bool paused) {
+ DCHECK(observer_);
+ observer_->OnBeginFrameSourcePausedChanged(paused);
+ }
+
private:
FakeSchedulerClient* client_;
};
@@ -2924,6 +2929,34 @@ TEST_F(SchedulerTest, ScheduledActionActivateAfterBecomingInvisible) {
EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
}
+TEST_F(SchedulerTest, ScheduledActionActivateAfterBeginFrameSourcePaused) {
+ scheduler_settings_.use_external_begin_frame_source = true;
+ SetUpScheduler(true);
+
+ // SetNeedsBeginMainFrame should begin the frame.
+ scheduler_->SetNeedsBeginMainFrame();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+
+ client_->Reset();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2);
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+
+ client_->Reset();
+ scheduler_->NotifyBeginMainFrameStarted(base::TimeTicks());
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_);
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+
+ client_->Reset();
+ fake_external_begin_frame_source_->SetPaused(true);
+ task_runner().RunPendingTasks(); // Run posted deadline.
+
+ // Sync tree should be forced to activate.
+ EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_);
+}
+
// Tests to ensure frame sources can be successfully changed while drawing.
TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) {
scheduler_settings_.use_external_begin_frame_source = true;
diff --git a/cc/surfaces/display_scheduler.cc b/cc/surfaces/display_scheduler.cc
index feb84ea..f87d4fb 100644
--- a/cc/surfaces/display_scheduler.cc
+++ b/cc/surfaces/display_scheduler.cc
@@ -152,6 +152,12 @@ bool DisplayScheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs& args) {
return true;
}
+void DisplayScheduler::OnBeginFrameSourcePausedChanged(bool paused) {
+ // BeginFrameSources used with DisplayScheduler do not make use of this
+ // feature.
+ NOTIMPLEMENTED();
+}
+
base::TimeTicks DisplayScheduler::DesiredBeginFrameDeadlineTime() {
if (output_surface_lost_) {
TRACE_EVENT_INSTANT0("cc", "Lost output surface", TRACE_EVENT_SCOPE_THREAD);
diff --git a/cc/surfaces/display_scheduler.h b/cc/surfaces/display_scheduler.h
index 838af40..cb8a365 100644
--- a/cc/surfaces/display_scheduler.h
+++ b/cc/surfaces/display_scheduler.h
@@ -48,6 +48,7 @@ class CC_SURFACES_EXPORT DisplayScheduler : public BeginFrameObserverBase {
// BeginFrameObserverBase implementation
bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
BeginFrameSource* begin_frame_source_for_children() {
return begin_frame_source_for_children_.get();
diff --git a/cc/test/begin_frame_source_test.h b/cc/test/begin_frame_source_test.h
index 1de8b99..351e090 100644
--- a/cc/test/begin_frame_source_test.h
+++ b/cc/test/begin_frame_source_test.h
@@ -26,6 +26,11 @@
.InSequence((obs).sequence) \
.WillOnce(::testing::SaveArg<0>(&((obs).last_begin_frame_args)))
+#define EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, paused) \
+ EXPECT_CALL((obs), OnBeginFrameSourcePausedChanged(paused)) \
+ .Times(1) \
+ .InSequence((obs).sequence)
+
// Macros to send BeginFrameArgs on a FakeBeginFrameSink (and verify resulting
// observer behaviour).
#define SEND_BEGIN_FRAME(args_equal_to, source, frame_time, deadline, \
@@ -53,6 +58,7 @@ class MockBeginFrameObserver : public BeginFrameObserver {
public:
MOCK_METHOD1(OnBeginFrame, void(const BeginFrameArgs&));
MOCK_CONST_METHOD0(LastUsedBeginFrameArgs, const BeginFrameArgs());
+ MOCK_METHOD1(OnBeginFrameSourcePausedChanged, void(bool));
virtual void AsValueInto(base::trace_event::TracedValue* dict) const;
diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h
index e561d82..cf73a25 100644
--- a/cc/test/scheduler_test_common.h
+++ b/cc/test/scheduler_test_common.h
@@ -111,6 +111,8 @@ class FakeBeginFrameSource : public BeginFrameSourceBase {
void DidFinishFrame(size_t remaining_frames) override;
void AsValueInto(base::trace_event::TracedValue* dict) const override;
+ using BeginFrameSourceBase::SetBeginFrameSourcePaused;
+
private:
bool remaining_frames_;
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.cc b/content/browser/android/in_process/synchronous_compositor_impl.cc
index e5c789b..e179137 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -130,6 +130,7 @@ void SynchronousCompositorImpl::DidInitializeRendererObjects(
output_surface_->SetSyncClient(this);
begin_frame_source_->SetClient(this);
+ begin_frame_source_->SetBeginFrameSourcePaused(!is_active_);
}
void SynchronousCompositorImpl::DidDestroyRendererObjects() {
@@ -244,8 +245,13 @@ void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset(
void SynchronousCompositorImpl::SetIsActive(bool is_active) {
TRACE_EVENT1("cc", "SynchronousCompositorImpl::SetIsActive", "is_active",
is_active);
+ if (is_active_ == is_active)
+ return;
+
is_active_ = is_active;
UpdateNeedsBeginFrames();
+ if (begin_frame_source_)
+ begin_frame_source_->SetBeginFrameSourcePaused(!is_active_);
}
void SynchronousCompositorImpl::OnComputeScroll(
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc
index 8b6e798..83dd85f 100644
--- a/content/browser/android/synchronous_compositor_host.cc
+++ b/content/browser/android/synchronous_compositor_host.cc
@@ -48,6 +48,8 @@ SynchronousCompositorHost::SynchronousCompositorHost(
SynchronousCompositorHost::~SynchronousCompositorHost() {
client_->DidDestroyCompositor(this);
+ if (weak_ptr_factory_.HasWeakPtrs())
+ UpdateStateTask();
}
bool SynchronousCompositorHost::OnMessageReceived(const IPC::Message& message) {
@@ -260,8 +262,11 @@ void SynchronousCompositorHost::UpdateStateTask() {
}
void SynchronousCompositorHost::SetIsActive(bool is_active) {
+ if (is_active_ == is_active)
+ return;
is_active_ = is_active;
UpdateNeedsBeginFrames();
+ SendAsyncCompositorStateIfNeeded();
}
void SynchronousCompositorHost::OnComputeScroll(
@@ -331,6 +336,7 @@ void SynchronousCompositorHost::PopulateCommonParams(
params->update_root_scroll_offset = root_scroll_offset_updated_by_browser_;
root_scroll_offset_updated_by_browser_ = false;
}
+ params->begin_frame_source_paused = !is_active_;
weak_ptr_factory_.InvalidateWeakPtrs();
}
diff --git a/content/common/android/sync_compositor_messages.cc b/content/common/android/sync_compositor_messages.cc
index c736f7f..f44415f 100644
--- a/content/common/android/sync_compositor_messages.cc
+++ b/content/common/android/sync_compositor_messages.cc
@@ -7,7 +7,9 @@
namespace content {
SyncCompositorCommonBrowserParams::SyncCompositorCommonBrowserParams()
- : bytes_limit(0u), update_root_scroll_offset(false) {}
+ : bytes_limit(0u),
+ update_root_scroll_offset(false),
+ begin_frame_source_paused(false) {}
SyncCompositorCommonBrowserParams::~SyncCompositorCommonBrowserParams() {}
diff --git a/content/common/android/sync_compositor_messages.h b/content/common/android/sync_compositor_messages.h
index b2dccda..a914407 100644
--- a/content/common/android/sync_compositor_messages.h
+++ b/content/common/android/sync_compositor_messages.h
@@ -29,6 +29,7 @@ struct SyncCompositorCommonBrowserParams {
cc::CompositorFrameAck ack;
gfx::ScrollOffset root_scroll_offset;
bool update_root_scroll_offset;
+ bool begin_frame_source_paused;
};
struct SyncCompositorDemandDrawHwParams {
@@ -98,6 +99,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorCommonBrowserParams)
IPC_STRUCT_TRAITS_MEMBER(ack)
IPC_STRUCT_TRAITS_MEMBER(root_scroll_offset)
IPC_STRUCT_TRAITS_MEMBER(update_root_scroll_offset)
+ IPC_STRUCT_TRAITS_MEMBER(begin_frame_source_paused)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorDemandDrawHwParams)
diff --git a/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc b/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc
index 08ea729..c5337d0 100644
--- a/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc
+++ b/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc
@@ -49,6 +49,9 @@ void SynchronousCompositorExternalBeginFrameSource::SetClient(
if (client_)
client_->OnNeedsBeginFramesChange(needs_begin_frames_);
+
+ // State without client is paused, and default client state is not paused.
+ SetBeginFrameSourcePaused(!client_);
}
void SynchronousCompositorExternalBeginFrameSource::OnNeedsBeginFramesChange(
diff --git a/content/renderer/android/synchronous_compositor_external_begin_frame_source.h b/content/renderer/android/synchronous_compositor_external_begin_frame_source.h
index cc3e9e53..83fed03 100644
--- a/content/renderer/android/synchronous_compositor_external_begin_frame_source.h
+++ b/content/renderer/android/synchronous_compositor_external_begin_frame_source.h
@@ -33,8 +33,8 @@ class SynchronousCompositorExternalBeginFrameSource
~SynchronousCompositorExternalBeginFrameSource() override;
void BeginFrame(const cc::BeginFrameArgs& args);
- void SetClient(
- SynchronousCompositorExternalBeginFrameSourceClient* client);
+ void SetClient(SynchronousCompositorExternalBeginFrameSourceClient* client);
+ using cc::BeginFrameSourceBase::SetBeginFrameSourcePaused;
// cc::BeginFrameSourceBase implementation.
void OnNeedsBeginFramesChange(bool needs_begin_frames) override;
diff --git a/content/renderer/android/synchronous_compositor_proxy.cc b/content/renderer/android/synchronous_compositor_proxy.cc
index 6e4a899..e8fc5f3 100644
--- a/content/renderer/android/synchronous_compositor_proxy.cc
+++ b/content/renderer/android/synchronous_compositor_proxy.cc
@@ -390,6 +390,8 @@ void SynchronousCompositorProxy::ProcessCommonParams(
input_handler_proxy_->SynchronouslySetRootScrollOffset(
total_scroll_offset_);
}
+ begin_frame_source_->SetBeginFrameSourcePaused(
+ common_params.begin_frame_source_paused);
if (!common_params.ack.resources.empty()) {
output_surface_->ReturnResources(common_params.ack);
}