Index: cc/scheduler/scheduler_unittest.cc |
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc |
index 2f878fdcdbfa9a22a3d101934c3200fb3c30b96d..16c0cf34b51cd6b456dac1dcf373c506de138f47 100644 |
--- a/cc/scheduler/scheduler_unittest.cc |
+++ b/cc/scheduler/scheduler_unittest.cc |
@@ -1378,7 +1378,14 @@ TEST_F(SchedulerTest, NotSkipMainFrameInPreferImplLatencyMode) { |
EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 1, true, true)); |
} |
-TEST_F(SchedulerTest, PollForCommitCompletion) { |
+TEST_F(SchedulerTest, |
+ Deadlock_NotifyReadyToCommitMakesProgressWhileSwapTrottled) { |
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main |
+ // thread. This prevents the scheduler from receiving any pending swap acks. |
+ // This test makes sure that we keep updating the TextureUploader with |
+ // DidAnticipatedDrawTimeChange's so that it can make forward progress and |
+ // upload all the textures needed for the commit to complete. |
+ |
// Since we are simulating a long commit, set up a client with draw duration |
// estimates that prevent skipping main frames to get to low latency mode. |
SchedulerClientWithFixedEstimates* client = |
@@ -1439,6 +1446,247 @@ TEST_F(SchedulerTest, PollForCommitCompletion) { |
} |
} |
+TEST_F( |
+ SchedulerTest, |
+ Deadlock_CommitMakesProgressWhileSwapTrottledAndActiveTreeNeedsFirstDraw) { |
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main |
+ // thread. This prevents the scheduler from receiving any pending swap acks. |
+ |
+ // Since we are simulating a long commit, set up a client with draw duration |
+ // estimates that prevent skipping main frames to get to low latency mode. |
+ SchedulerClientWithFixedEstimates* client = |
+ new SchedulerClientWithFixedEstimates( |
+ base::TimeDelta::FromMilliseconds(1), |
+ base::TimeDelta::FromMilliseconds(32), |
+ base::TimeDelta::FromMilliseconds(32)); |
+ scheduler_settings_.use_external_begin_frame_source = true; |
+ scheduler_settings_.main_frame_while_swap_throttled_enabled = true; |
+ scheduler_settings_.impl_side_painting = true; |
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true); |
+ |
+ // Disables automatic swap acks so this test can force swap ack throttling |
+ // to simulate a blocked Browser ui thread. |
+ scheduler_->SetMaxSwapsPending(1); |
+ client_->SetAutomaticSwapAck(false); |
+ |
+ // Get a new active tree in main-thread high latency mode and put us |
+ // in a swap throttled state. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ scheduler_->SetNeedsRedraw(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_TRUE(scheduler_->CommitPending()); |
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); |
+ task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); |
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); |
+ scheduler_->NotifyBeginMainFrameStarted(); |
+ scheduler_->NotifyReadyToCommit(); |
+ scheduler_->NotifyReadyToActivate(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 7); |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 1, 7); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 7); |
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 3, 7); |
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 7); |
+ EXPECT_ACTION("ScheduledActionCommit", client_, 5, 7); |
+ EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 6, 7); |
+ |
+ // Make sure that we can finish the next commit even while swap throttled. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ scheduler_->NotifyBeginMainFrameStarted(); |
+ scheduler_->NotifyReadyToCommit(); |
+ scheduler_->NotifyReadyToActivate(); |
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); |
+ task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); |
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 5); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 5); |
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 5); |
+ EXPECT_ACTION("ScheduledActionCommit", client_, 3, 5); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 4, 5); |
+ |
+ // Make sure we do not send a BeginMainFrame while swap throttled and |
+ // we have both a pending tree and an active tree. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ task_runner().RunPendingTasks(); // Run posted deadline. |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); |
+} |
+ |
+TEST_F(SchedulerTest, |
+ Deadlock_NoBeginMainFrameWhileSwapTrottledAndPipelineFull) { |
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main |
+ // thread. This prevents the scheduler from receiving any pending swap acks. |
+ |
+ // This particular test makes sure we do not send a BeginMainFrame while |
+ // swap trottled and we have a pending tree and active tree that |
+ // still needs to be drawn for the first time. |
+ |
+ // Since we are simulating a long commit, set up a client with draw duration |
+ // estimates that prevent skipping main frames to get to low latency mode. |
+ SchedulerClientWithFixedEstimates* client = |
+ new SchedulerClientWithFixedEstimates( |
+ base::TimeDelta::FromMilliseconds(1), |
+ base::TimeDelta::FromMilliseconds(32), |
+ base::TimeDelta::FromMilliseconds(32)); |
+ scheduler_settings_.use_external_begin_frame_source = true; |
+ scheduler_settings_.main_frame_while_swap_throttled_enabled = true; |
+ scheduler_settings_.main_frame_before_activation_enabled = true; |
+ scheduler_settings_.impl_side_painting = true; |
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true); |
+ |
+ // Disables automatic swap acks so this test can force swap ack throttling |
+ // to simulate a blocked Browser ui thread. |
+ scheduler_->SetMaxSwapsPending(1); |
+ client_->SetAutomaticSwapAck(false); |
+ |
+ // Start a new commit in main-thread high latency mode and hold off on |
+ // activation. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ scheduler_->SetNeedsRedraw(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_TRUE(scheduler_->CommitPending()); |
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); |
+ task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); |
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); |
+ scheduler_->DidSwapBuffersComplete(); |
+ scheduler_->NotifyBeginMainFrameStarted(); |
+ scheduler_->NotifyReadyToCommit(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 6); |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 1, 6); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 6); |
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 3, 6); |
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 6); |
+ EXPECT_ACTION("ScheduledActionCommit", client_, 5, 6); |
+ |
+ // Start another commit while we still have an active tree. |
+ // Enter a swap throttled state. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ scheduler_->SetNeedsRedraw(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_TRUE(scheduler_->CommitPending()); |
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); |
+ task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); |
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); |
+ scheduler_->NotifyBeginMainFrameStarted(); |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 4); |
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 4); |
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 3, 4); |
+ |
+ // Can't commit yet because there's still a pending tree. |
+ client_->Reset(); |
+ scheduler_->NotifyReadyToCommit(); |
+ EXPECT_NO_ACTION(client_); |
+ |
+ // Activate the pending tree, which also unblocks the commit immediately. |
+ client_->Reset(); |
+ scheduler_->NotifyReadyToActivate(); |
+ EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 2); |
+ EXPECT_ACTION("ScheduledActionCommit", client_, 1, 2); |
+ |
+ // Make sure we do not send a BeginMainFrame while swap throttled and |
+ // we have both a pending tree and an active tree that still needs |
+ // it's first draw. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ task_runner().RunPendingTasks(); // Run posted deadline. |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); |
+} |
+ |
+TEST_F( |
+ SchedulerTest, |
+ CommitMakesProgressWhenIdleAndHasPendingTreeAndActiveTreeNeedsFirstDraw) { |
+ // This verifies we don't block commits longer than we need to |
+ // for performance reasons - not deadlock reasons. |
+ |
+ // Since we are simulating a long commit, set up a client with draw duration |
+ // estimates that prevent skipping main frames to get to low latency mode. |
+ SchedulerClientWithFixedEstimates* client = |
+ new SchedulerClientWithFixedEstimates( |
+ base::TimeDelta::FromMilliseconds(1), |
+ base::TimeDelta::FromMilliseconds(32), |
+ base::TimeDelta::FromMilliseconds(32)); |
+ scheduler_settings_.use_external_begin_frame_source = true; |
+ scheduler_settings_.main_frame_while_swap_throttled_enabled = true; |
+ scheduler_settings_.main_frame_before_activation_enabled = true; |
+ scheduler_settings_.impl_side_painting = true; |
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true); |
+ |
+ // Disables automatic swap acks so this test can force swap ack throttling |
+ // to simulate a blocked Browser ui thread. |
+ scheduler_->SetMaxSwapsPending(1); |
+ client_->SetAutomaticSwapAck(false); |
+ |
+ // Start a new commit in main-thread high latency mode and hold off on |
+ // activation. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ scheduler_->SetNeedsRedraw(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_TRUE(scheduler_->CommitPending()); |
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); |
+ task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); |
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); |
+ scheduler_->DidSwapBuffersComplete(); |
+ scheduler_->NotifyBeginMainFrameStarted(); |
+ scheduler_->NotifyReadyToCommit(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 6); |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 1, 6); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 6); |
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 3, 6); |
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 6); |
+ EXPECT_ACTION("ScheduledActionCommit", client_, 5, 6); |
+ |
+ // Start another commit while we still have an active tree. |
+ client_->Reset(); |
+ EXPECT_FALSE(scheduler_->CommitPending()); |
+ scheduler_->SetNeedsCommit(); |
+ scheduler_->SetNeedsRedraw(); |
+ EXPECT_SCOPED(AdvanceFrame()); |
+ EXPECT_TRUE(scheduler_->CommitPending()); |
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); |
+ task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); |
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); |
+ scheduler_->NotifyBeginMainFrameStarted(); |
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); |
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 4); |
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 4); |
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 3, 4); |
+ scheduler_->DidSwapBuffersComplete(); |
+ |
+ // Can't commit yet because there's still a pending tree. |
+ client_->Reset(); |
+ scheduler_->NotifyReadyToCommit(); |
+ EXPECT_NO_ACTION(client_); |
+ |
+ // Activate the pending tree, which also unblocks the commit immediately |
+ // while we are in an idle state. |
+ client_->Reset(); |
+ scheduler_->NotifyReadyToActivate(); |
+ EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 2); |
+ EXPECT_ACTION("ScheduledActionCommit", client_, 1, 2); |
+} |
+ |
TEST_F(SchedulerTest, BeginRetroFrame) { |
scheduler_settings_.use_external_begin_frame_source = true; |
SetUpScheduler(true); |