diff options
35 files changed, 659 insertions, 116 deletions
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index 5e7bd77..9d1cde5 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc @@ -121,6 +121,11 @@ void Layer::SetLayerTreeHost(LayerTreeHost* host) { layer_tree_host_->set_needs_filter_context(); } +void Layer::SetNeedsUpdate() { + if (layer_tree_host_ && !ignore_set_needs_commit_) + layer_tree_host_->SetNeedsUpdateLayers(); +} + void Layer::SetNeedsCommit() { if (!layer_tree_host_) return; @@ -563,14 +568,17 @@ void Layer::SetScrollOffset(gfx::Vector2d scroll_offset) { void Layer::SetScrollOffsetFromImplSide(gfx::Vector2d scroll_offset) { DCHECK(IsPropertyChangeAllowed()); + // This function only gets called during a begin frame, so there + // is no need to call SetNeedsUpdate here. DCHECK(layer_tree_host_ && layer_tree_host_->CommitRequested()); if (scroll_offset_ == scroll_offset) return; scroll_offset_ = scroll_offset; + SetNeedsPushProperties(); if (!did_scroll_callback_.is_null()) did_scroll_callback_.Run(); - // Note: didScroll() could potentially change the layer structure. - // "this" may have been destroyed during the process. + // The callback could potentially change the layer structure: + // "this" may have been destroyed during the process. } void Layer::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) { @@ -663,14 +671,16 @@ void Layer::SetHideLayerAndSubtree(bool hide) { } void Layer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) { + if (!update_rect_.Contains(dirty_rect)) { + SetNeedsPushProperties(); + } + update_rect_.Union(dirty_rect); needs_display_ = true; - // Simply mark the contents as dirty. For non-root layers, the call to - // SetNeedsCommit will schedule a fresh compositing pass. - // For the root layer, SetNeedsCommit has no effect. - if (DrawsContent() && !update_rect_.IsEmpty()) - SetNeedsCommit(); + if (DrawsContent() && !update_rect_.IsEmpty()) { + SetNeedsUpdate(); + } } bool Layer::DescendantIsFixedToContainerLayer() const { diff --git a/cc/layers/layer.h b/cc/layers/layer.h index fb63c4c0..09f7f71 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h @@ -394,7 +394,18 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, Layer(); + // These SetNeeds functions are in order of severity of update: + // + // Called when this layer has been modified in some way, but isn't sure + // that it needs a commit yet. It needs CalcDrawProperties and UpdateLayers + // before it knows whether or not a commit is required. + void SetNeedsUpdate(); + // Called when a property has been modified in a way that the layer + // knows immediately that a commit is required. This implies SetNeedsUpdate + // as well as SetNeedsPushProperties to push that property. void SetNeedsCommit(); + // Called when there's been a change in layer structure. Implies both + // SetNeedsUpdate and SetNeedsCommit, but not SetNeedsPushProperties. void SetNeedsFullTreeSync(); bool IsPropertyChangeAllowed() const; @@ -435,7 +446,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, int layer_id_; // When true, the layer is about to perform an update. Any commit requests - // will be handled implcitly after the update completes. + // will be handled implicitly after the update completes. bool ignore_set_needs_commit_; private: diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 4865fe2..008c689 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -265,11 +265,30 @@ gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) { new_delta.SetToMax(min_delta); new_delta.SetToMin(max_delta); gfx::Vector2dF unscrolled = ScrollDelta() + scroll - new_delta; - SetScrollDelta(new_delta); return unscrolled; } +void LayerImpl::ApplySentScrollDeltas() { + // Pending tree never has sent scroll deltas + DCHECK(layer_tree_impl()->IsActiveTree()); + + // Apply sent scroll deltas to scroll position / scroll delta as if the + // main thread had applied them and then committed those values. + // + // This function should not change the total scroll offset; it just shifts + // some of the scroll delta to the scroll offset. Therefore, adjust these + // variables directly rather than calling the scroll offset delegate to + // avoid sending it multiple spurious calls. + // + // Because of the way scroll delta is calculated with a delegate, this will + // leave the total scroll offset unchanged on this layer regardless of + // whether a delegate is being used. + scroll_offset_ += sent_scroll_delta_; + scroll_delta_ -= sent_scroll_delta_; + sent_scroll_delta_ = gfx::Vector2d(); +} + InputHandler::ScrollStatus LayerImpl::TryScroll( gfx::PointF screen_space_point, InputHandler::ScrollInputType type) const { @@ -893,8 +912,8 @@ void LayerImpl::SetScrollDelta(gfx::Vector2dF scroll_delta) { } else { scroll_delta_ = scroll_delta; } - NoteLayerPropertyChangedForSubtree(); + NoteLayerPropertyChangedForSubtree(); UpdateScrollbarPositions(); } diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index ff7e956..90da41a 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h @@ -330,6 +330,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { void SetScrollable(bool scrollable) { scrollable_ = scrollable; } bool scrollable() const { return scrollable_; } + void ApplySentScrollDeltas(); + void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) { should_scroll_on_main_thread_ = should_scroll_on_main_thread; } diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc index 45ba641..a0235ae 100644 --- a/cc/layers/layer_impl_unittest.cc +++ b/cc/layers/layer_impl_unittest.cc @@ -482,5 +482,77 @@ TEST_F(LayerImplScrollTest, ScrollByWithAcceptingDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); } +TEST_F(LayerImplScrollTest, ApplySentScrollsNoDelegate) { + gfx::Vector2d max_scroll_offset(50, 80); + gfx::Vector2d scroll_offset(10, 5); + gfx::Vector2dF scroll_delta(20.5f, 8.5f); + gfx::Vector2d sent_scroll_delta(12, -3); + + layer()->SetMaxScrollOffset(max_scroll_offset); + layer()->SetScrollOffset(scroll_offset); + layer()->ScrollBy(scroll_delta); + layer()->SetSentScrollDelta(sent_scroll_delta); + + EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); + EXPECT_VECTOR_EQ(scroll_delta, layer()->ScrollDelta()); + EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); + EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); + + layer()->ApplySentScrollDeltas(); + + EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); + EXPECT_VECTOR_EQ(scroll_delta - sent_scroll_delta, layer()->ScrollDelta()); + EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); + EXPECT_VECTOR_EQ(gfx::Vector2d(), layer()->sent_scroll_delta()); +} + +TEST_F(LayerImplScrollTest, ApplySentScrollsWithIgnoringDelegate) { + gfx::Vector2d max_scroll_offset(50, 80); + gfx::Vector2d scroll_offset(10, 5); + gfx::Vector2d sent_scroll_delta(12, -3); + gfx::Vector2dF fixed_offset(32, 12); + + layer()->SetMaxScrollOffset(max_scroll_offset); + layer()->SetScrollOffset(scroll_offset); + ScrollDelegateIgnore delegate; + delegate.set_fixed_offset(fixed_offset); + layer()->SetScrollOffsetDelegate(&delegate); + layer()->SetSentScrollDelta(sent_scroll_delta); + + EXPECT_VECTOR_EQ(fixed_offset, layer()->TotalScrollOffset()); + EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); + EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); + + layer()->ApplySentScrollDeltas(); + + EXPECT_VECTOR_EQ(fixed_offset, layer()->TotalScrollOffset()); + EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); + EXPECT_VECTOR_EQ(gfx::Vector2d(), layer()->sent_scroll_delta()); +} + +TEST_F(LayerImplScrollTest, ApplySentScrollsWithAcceptingDelegate) { + gfx::Vector2d max_scroll_offset(50, 80); + gfx::Vector2d scroll_offset(10, 5); + gfx::Vector2d sent_scroll_delta(12, -3); + gfx::Vector2dF scroll_delta(20.5f, 8.5f); + + layer()->SetMaxScrollOffset(max_scroll_offset); + layer()->SetScrollOffset(scroll_offset); + ScrollDelegateAccept delegate; + layer()->SetScrollOffsetDelegate(&delegate); + layer()->ScrollBy(scroll_delta); + layer()->SetSentScrollDelta(sent_scroll_delta); + + EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); + EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); + EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); + + layer()->ApplySentScrollDeltas(); + + EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); + EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); + EXPECT_VECTOR_EQ(gfx::Vector2d(), layer()->sent_scroll_delta()); +} + } // namespace } // namespace cc diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index f841e73..0c2fca6 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc @@ -44,6 +44,7 @@ class MockLayerTreeHost : public LayerTreeHost { } MOCK_METHOD0(SetNeedsCommit, void()); + MOCK_METHOD0(SetNeedsUpdateLayers, void()); MOCK_METHOD0(SetNeedsFullTreeSync, void()); }; @@ -467,7 +468,7 @@ TEST_F(LayerTest, GetRootLayerAfterTreeManipulations) { TEST_F(LayerTest, CheckSetNeedsDisplayCausesCorrectBehavior) { // The semantics for SetNeedsDisplay which are tested here: // 1. sets NeedsDisplay flag appropriately. - // 2. indirectly calls SetNeedsCommit, exactly once for each call to + // 2. indirectly calls SetNeedsUpdate, exactly once for each call to // SetNeedsDisplay. scoped_refptr<Layer> test_layer = Layer::Create(); @@ -497,7 +498,7 @@ TEST_F(LayerTest, CheckSetNeedsDisplayCausesCorrectBehavior) { // Case 1: Layer should accept dirty rects that go beyond its bounds. test_layer->ResetNeedsDisplayForTesting(); EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); - EXPECT_SET_NEEDS_COMMIT( + EXPECT_SET_NEEDS_UPDATE( 1, test_layer->SetNeedsDisplayRect(out_of_bounds_dirty_rect)); EXPECT_TRUE(test_layer->NeedsDisplayForTesting()); test_layer->ResetNeedsDisplayForTesting(); @@ -505,7 +506,7 @@ TEST_F(LayerTest, CheckSetNeedsDisplayCausesCorrectBehavior) { // Case 2: SetNeedsDisplay() without the dirty rect arg. test_layer->ResetNeedsDisplayForTesting(); EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); - EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetNeedsDisplay()); + EXPECT_SET_NEEDS_UPDATE(1, test_layer->SetNeedsDisplay()); EXPECT_TRUE(test_layer->NeedsDisplayForTesting()); test_layer->ResetNeedsDisplayForTesting(); @@ -513,7 +514,7 @@ TEST_F(LayerTest, CheckSetNeedsDisplayCausesCorrectBehavior) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsDrawable(false)); test_layer->ResetNeedsDisplayForTesting(); EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); - EXPECT_SET_NEEDS_COMMIT(0, test_layer->SetNeedsDisplayRect(dirty1)); + EXPECT_SET_NEEDS_UPDATE(0, test_layer->SetNeedsDisplayRect(dirty1)); EXPECT_TRUE(test_layer->NeedsDisplayForTesting()); } diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc index ccbd0a7..cbb66db 100644 --- a/cc/layers/texture_layer_unittest.cc +++ b/cc/layers/texture_layer_unittest.cc @@ -38,6 +38,7 @@ class MockLayerTreeHost : public LayerTreeHost { MOCK_METHOD0(AcquireLayerTextures, void()); MOCK_METHOD0(SetNeedsCommit, void()); + MOCK_METHOD0(SetNeedsUpdateLayers, void()); MOCK_METHOD1(StartRateLimiter, void(WebKit::WebGraphicsContext3D* context)); MOCK_METHOD1(StopRateLimiter, void(WebKit::WebGraphicsContext3D* context)); }; @@ -118,7 +119,7 @@ TEST_F(TextureLayerTest, SyncImplWhenDrawing) { Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); + EXPECT_CALL(*layer_tree_host_, SetNeedsUpdateLayers()).Times(1); test_layer->SetNeedsDisplayRect(dirty_rect); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 5e397bd..010cc99 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -85,9 +85,9 @@ void Scheduler::FinishCommit() { ProcessScheduledActions(); } -void Scheduler::BeginFrameAbortedByMainThread() { +void Scheduler::BeginFrameAbortedByMainThread(bool did_handle) { TRACE_EVENT0("cc", "Scheduler::BeginFrameAbortedByMainThread"); - state_machine_.BeginFrameAbortedByMainThread(); + state_machine_.BeginFrameAbortedByMainThread(did_handle); ProcessScheduledActions(); } diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index 48a3bb2..230f20d 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -86,7 +86,7 @@ class CC_EXPORT Scheduler { void DidSwapUseIncompleteTile(); void FinishCommit(); - void BeginFrameAbortedByMainThread(); + void BeginFrameAbortedByMainThread(bool did_handle); void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 3747c0b..ed9c15f 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -15,6 +15,7 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) commit_state_(COMMIT_STATE_IDLE), commit_count_(0), current_frame_number_(0), + last_frame_number_where_begin_frame_sent_to_main_thread_(-1), last_frame_number_where_draw_was_called_(-1), last_frame_number_where_tree_activation_attempted_(-1), last_frame_number_where_update_visible_tiles_was_called_(-1), @@ -24,6 +25,7 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) swap_used_incomplete_tile_(false), needs_forced_redraw_(false), needs_forced_redraw_after_next_commit_(false), + needs_redraw_after_next_commit_(false), needs_commit_(false), needs_forced_commit_(false), expect_immediate_begin_frame_for_main_thread_(false), @@ -116,6 +118,19 @@ bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const { last_frame_number_where_update_visible_tiles_was_called_; } +void SchedulerStateMachine::SetPostCommitFlags() { + // This post-commit work is common to both completed and aborted commits. + if (needs_forced_redraw_after_next_commit_) { + needs_forced_redraw_after_next_commit_ = false; + needs_forced_redraw_ = true; + } + if (needs_redraw_after_next_commit_) { + needs_redraw_after_next_commit_ = false; + needs_redraw_ = true; + } + texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; +} + bool SchedulerStateMachine::DrawSuspendedUntilCommit() const { if (!can_draw_) return true; @@ -184,7 +199,7 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD; switch (commit_state_) { - case COMMIT_STATE_IDLE: + case COMMIT_STATE_IDLE: { if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && needs_forced_redraw_) return ACTION_DRAW_FORCED; @@ -205,14 +220,18 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { return needs_forced_redraw_ ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE; } - if (needs_commit_ && - ((visible_ && output_surface_state_ == OUTPUT_SURFACE_ACTIVE) - || needs_forced_commit_)) + bool can_commit_this_frame = + visible_ && + current_frame_number_ > + last_frame_number_where_begin_frame_sent_to_main_thread_; + if (needs_commit_ && ((can_commit_this_frame && + output_surface_state_ == OUTPUT_SURFACE_ACTIVE) || + needs_forced_commit_)) // TODO(enne): Should probably drop the active tree on force commit. return has_pending_tree_ ? ACTION_NONE : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; return ACTION_NONE; - + } case COMMIT_STATE_FRAME_IN_PROGRESS: if (ShouldUpdateVisibleTiles()) return ACTION_UPDATE_VISIBLE_TILES; @@ -239,7 +258,11 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If // can_draw_ is false or textures are not available, proceed to the next // step (similar as in COMMIT_STATE_IDLE). - bool can_commit = visible_ || needs_forced_commit_; + bool can_commit = + needs_forced_commit_ || + (visible_ && + current_frame_number_ > + last_frame_number_where_begin_frame_sent_to_main_thread_); if (needs_commit_ && can_commit && DrawSuspendedUntilCommit()) return has_pending_tree_ ? ACTION_NONE : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; @@ -276,10 +299,16 @@ void SchedulerStateMachine::UpdateState(Action action) { case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD: DCHECK(!has_pending_tree_); - DCHECK(visible_ || needs_forced_commit_); + if (!needs_forced_commit_) { + DCHECK(visible_); + DCHECK_GT(current_frame_number_, + last_frame_number_where_begin_frame_sent_to_main_thread_); + } commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; needs_commit_ = false; needs_forced_commit_ = false; + last_frame_number_where_begin_frame_sent_to_main_thread_ = + current_frame_number_; return; case ACTION_COMMIT: @@ -293,13 +322,7 @@ void SchedulerStateMachine::UpdateState(Action action) { needs_redraw_ = true; if (draw_if_possible_failed_) last_frame_number_where_draw_was_called_ = -1; - - if (needs_forced_redraw_after_next_commit_) { - needs_forced_redraw_after_next_commit_ = false; - needs_forced_redraw_ = true; - } - - texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; + SetPostCommitFlags(); return; case ACTION_DRAW_FORCED: @@ -367,12 +390,12 @@ bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const { } void SchedulerStateMachine::DidEnterBeginFrame(const BeginFrameArgs& args) { + current_frame_number_++; inside_begin_frame_ = true; last_begin_frame_args_ = args; } void SchedulerStateMachine::DidLeaveBeginFrame() { - current_frame_number_++; inside_begin_frame_ = false; } @@ -422,10 +445,13 @@ void SchedulerStateMachine::FinishCommit() { commit_state_ = COMMIT_STATE_READY_TO_COMMIT; } -void SchedulerStateMachine::BeginFrameAbortedByMainThread() { +void SchedulerStateMachine::BeginFrameAbortedByMainThread(bool did_handle) { DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS); if (expect_immediate_begin_frame_for_main_thread_) { expect_immediate_begin_frame_for_main_thread_ = false; + } else if (did_handle) { + commit_state_ = COMMIT_STATE_IDLE; + SetPostCommitFlags(); } else { commit_state_ = COMMIT_STATE_IDLE; SetNeedsCommit(); @@ -458,6 +484,7 @@ void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() { // sort themselves out with a commit. needs_redraw_ = false; } + needs_redraw_after_next_commit_ = true; did_create_and_initialize_first_output_surface_ = true; } diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index 5a29830d..5c7d5b5 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -79,7 +79,7 @@ class CC_EXPORT SchedulerStateMachine { // Indicates that the system has entered and left a BeginFrame callback. // The scheduler will not draw more than once in a given BeginFrame - // callback. + // callback nor send more than one BeginFrame message. void DidEnterBeginFrame(const BeginFrameArgs& args); void DidLeaveBeginFrame(); bool inside_begin_frame() const { return inside_begin_frame_; } @@ -120,8 +120,9 @@ class CC_EXPORT SchedulerStateMachine { // Call this only in response to receiving an // ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD from NextAction if the client - // rejects the begin frame message. - void BeginFrameAbortedByMainThread(); + // rejects the begin frame message. If did_handle is false, then + // another commit will be retried soon. + void BeginFrameAbortedByMainThread(bool did_handle); // Request exclusive access to the textures that back single buffered // layers on behalf of the main thread. Upon acquisition, @@ -168,6 +169,7 @@ class CC_EXPORT SchedulerStateMachine { bool HasDrawnThisFrame() const; bool HasAttemptedTreeActivationThisFrame() const; bool HasUpdatedVisibleTilesThisFrame() const; + void SetPostCommitFlags(); const SchedulerSettings settings_; @@ -175,6 +177,7 @@ class CC_EXPORT SchedulerStateMachine { int commit_count_; int current_frame_number_; + int last_frame_number_where_begin_frame_sent_to_main_thread_; int last_frame_number_where_draw_was_called_; int last_frame_number_where_tree_activation_attempted_; int last_frame_number_where_update_visible_tiles_was_called_; @@ -184,6 +187,7 @@ class CC_EXPORT SchedulerStateMachine { bool swap_used_incomplete_tile_; bool needs_forced_redraw_; bool needs_forced_redraw_after_next_commit_; + bool needs_redraw_after_next_commit_; bool needs_commit_; bool needs_forced_commit_; bool expect_immediate_begin_frame_for_main_thread_; diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index 7036459..c7210eb 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc @@ -718,7 +718,7 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { // Become invisible and abort the main thread's begin frame. state.SetVisible(false); - state.BeginFrameAbortedByMainThread(); + state.BeginFrameAbortedByMainThread(false); // We should now be back in the idle state as if we didn't start a frame at // all. @@ -728,8 +728,14 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { // Become visible again. state.SetVisible(true); - // We should be beginning a frame now. + // Although we have aborted on this frame and haven't cancelled the commit + // (i.e. need another), don't send another begin frame yet. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_TRUE(state.NeedsCommit()); + + // Start a new frame. + state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); @@ -741,6 +747,52 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { state.CommitState()); } +TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) { + SchedulerSettings default_scheduler_settings; + StateMachine state(default_scheduler_settings); + state.SetCanStart(); + state.UpdateState(state.NextAction()); + state.DidCreateAndInitializeOutputSurface(); + state.SetVisible(true); + state.SetCanDraw(true); + + // Get into a begin frame / commit state. + state.SetNeedsCommit(); + EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, + state.NextAction()); + state.UpdateState( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, + state.CommitState()); + EXPECT_FALSE(state.NeedsCommit()); + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + + // Abort the commit, cancelling future commits. + state.BeginFrameAbortedByMainThread(true); + + // Verify that another commit doesn't start on the same frame. + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_FALSE(state.NeedsCommit()); + + // Start a new frame; draw because this is the first frame since output + // surface init'd. + state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); + state.DidLeaveBeginFrame(); + + // Verify another commit doesn't start on another frame either. + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_FALSE(state.NeedsCommit()); + + // Verify another commit can start if requested, though. + state.SetNeedsCommit(); + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, + state.NextAction()); +} + TEST(SchedulerStateMachineTest, TestFirstContextCreation) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); @@ -816,15 +868,23 @@ TEST(SchedulerStateMachineTest, // Recreate the context state.DidCreateAndInitializeOutputSurface(); + EXPECT_FALSE(state.RedrawPending()); // When the context is recreated, we should begin a commit EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, + state.CommitState()); + state.FinishCommit(); + EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + state.UpdateState(state.NextAction()); + // Finishing the first commit after initializing an output surface should + // automatically cause a redraw. + EXPECT_TRUE(state.RedrawPending()); // Once the context is recreated, whether we draw should be based on // SetCanDraw. - state.SetNeedsRedraw(true); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.SetCanDraw(false); @@ -1192,7 +1252,7 @@ TEST(SchedulerStateMachineTest, // Become invisible and abort the main thread's begin frame. state.SetVisible(false); - state.BeginFrameAbortedByMainThread(); + state.BeginFrameAbortedByMainThread(false); // Should be back in the idle state, but needing a commit. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); @@ -1295,5 +1355,37 @@ TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) { EXPECT_FALSE(state.DrawSuspendedUntilCommit()); } +TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) { + SchedulerSettings default_scheduler_settings; + SchedulerStateMachine state(default_scheduler_settings); + state.SetCanStart(); + state.UpdateState(state.NextAction()); + state.DidCreateAndInitializeOutputSurface(); + state.SetCanDraw(true); + state.SetVisible(true); + + state.SetMainThreadNeedsLayerTextures(); + EXPECT_EQ( + SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD, + state.NextAction()); + state.UpdateState(state.NextAction()); + EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + + state.SetNeedsCommit(); + EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, + state.NextAction()); + state.UpdateState(state.NextAction()); + EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + + state.BeginFrameAbortedByMainThread(true); + + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_FALSE(state.DrawSuspendedUntilCommit()); +} + } // namespace } // namespace cc diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 0a3c250..930111c 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -378,12 +378,17 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { client); client.Reset(); + // Already sent a begin frame on this current frame, so wait. + scheduler->SetVisible(true); + EXPECT_EQ(0, client.num_actions_()); + client.Reset(); + // Regaining visibility with textures acquired by main thread while // compositor is waiting for first draw should result in a request // for a new frame in order to escape a deadlock. - scheduler->SetVisible(true); - EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); - client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); } class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { diff --git a/cc/test/fake_content_layer.cc b/cc/test/fake_content_layer.cc index 5dcc512..077831fd 100644 --- a/cc/test/fake_content_layer.cc +++ b/cc/test/fake_content_layer.cc @@ -12,7 +12,8 @@ namespace cc { FakeContentLayer::FakeContentLayer(ContentLayerClient* client) : ContentLayer(client), update_count_(0), - push_properties_count_(0) { + push_properties_count_(0), + always_update_resources_(false) { SetAnchorPoint(gfx::PointF(0.f, 0.f)); SetBounds(gfx::Size(1, 1)); SetIsDrawable(true); @@ -29,7 +30,7 @@ bool FakeContentLayer::Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) { bool updated = ContentLayer::Update(queue, occlusion); update_count_++; - return updated; + return updated || always_update_resources_; } void FakeContentLayer::PushPropertiesTo(LayerImpl* layer) { diff --git a/cc/test/fake_content_layer.h b/cc/test/fake_content_layer.h index 6b7a37d..191a634 100644 --- a/cc/test/fake_content_layer.h +++ b/cc/test/fake_content_layer.h @@ -29,6 +29,10 @@ class FakeContentLayer : public ContentLayer { ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; + void set_always_update_resources(bool always_update_resources) { + always_update_resources_ = always_update_resources; + } + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; bool HaveBackingAt(int i, int j); @@ -39,6 +43,7 @@ class FakeContentLayer : public ContentLayer { size_t update_count_; size_t push_properties_count_; + bool always_update_resources_; }; } // namespace cc diff --git a/cc/test/fake_picture_layer.cc b/cc/test/fake_picture_layer.cc index 997197c..69292c3 100644 --- a/cc/test/fake_picture_layer.cc +++ b/cc/test/fake_picture_layer.cc @@ -11,7 +11,8 @@ namespace cc { FakePictureLayer::FakePictureLayer(ContentLayerClient* client) : PictureLayer(client), update_count_(0), - push_properties_count_(0) { + push_properties_count_(0), + always_update_resources_(false) { SetAnchorPoint(gfx::PointF(0.f, 0.f)); SetBounds(gfx::Size(1, 1)); SetIsDrawable(true); @@ -28,7 +29,7 @@ bool FakePictureLayer::Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) { bool updated = PictureLayer::Update(queue, occlusion); update_count_++; - return updated; + return updated || always_update_resources_; } void FakePictureLayer::PushPropertiesTo(LayerImpl* layer) { diff --git a/cc/test/fake_picture_layer.h b/cc/test/fake_picture_layer.h index b5b81de..824ac6c 100644 --- a/cc/test/fake_picture_layer.h +++ b/cc/test/fake_picture_layer.h @@ -26,9 +26,12 @@ class FakePictureLayer : public PictureLayer { size_t push_properties_count() const { return push_properties_count_; } void reset_push_properties_count() { push_properties_count_ = 0; } - virtual bool Update( - ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) OVERRIDE; + void set_always_update_resources(bool always_update_resources) { + always_update_resources_ = always_update_resources; + } + + virtual bool Update(ResourceUpdateQueue* queue, + const OcclusionTracker* occlusion) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; @@ -38,6 +41,7 @@ class FakePictureLayer : public PictureLayer { size_t update_count_; size_t push_properties_count_; + bool always_update_resources_; }; } // namespace cc diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h index 76de4b0..8cc3311 100644 --- a/cc/test/fake_proxy.h +++ b/cc/test/fake_proxy.h @@ -29,6 +29,7 @@ class FakeProxy : public Proxy { virtual void CreateAndInitializeOutputSurface() OVERRIDE; virtual const RendererCapabilities& GetRendererCapabilities() const OVERRIDE; virtual void SetNeedsAnimate() OVERRIDE {} + virtual void SetNeedsUpdateLayers() OVERRIDE {} virtual void SetNeedsCommit() OVERRIDE {} virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE {} virtual void SetDeferCommits(bool defer_commits) OVERRIDE {} diff --git a/cc/test/layer_test_common.h b/cc/test/layer_test_common.h index 94e2f2a..0021398 100644 --- a/cc/test/layer_test_common.h +++ b/cc/test/layer_test_common.h @@ -5,10 +5,18 @@ #ifndef CC_TEST_LAYER_TEST_COMMON_H_ #define CC_TEST_LAYER_TEST_COMMON_H_ -#define EXPECT_SET_NEEDS_COMMIT(expect, code_to_test) do { \ - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times((expect)); \ - code_to_test; \ - Mock::VerifyAndClearExpectations(layer_tree_host_.get()); \ +#define EXPECT_SET_NEEDS_COMMIT(expect, code_to_test) \ + do { \ + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times((expect)); \ + code_to_test; \ + Mock::VerifyAndClearExpectations(layer_tree_host_.get()); \ + } while (false) + +#define EXPECT_SET_NEEDS_UPDATE(expect, code_to_test) \ + do { \ + EXPECT_CALL(*layer_tree_host_, SetNeedsUpdateLayers()).Times((expect)); \ + code_to_test; \ + Mock::VerifyAndClearExpectations(layer_tree_host_.get()); \ } while (false) namespace gfx { class Rect; } diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index ed97752..6193d84 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -233,9 +233,9 @@ class LayerTreeHostClientForTesting : public LayerTreeHostClient { } virtual ~LayerTreeHostClientForTesting() {} - virtual void WillBeginFrame() OVERRIDE {} + virtual void WillBeginFrame() OVERRIDE { test_hooks_->WillBeginFrame(); } - virtual void DidBeginFrame() OVERRIDE {} + virtual void DidBeginFrame() OVERRIDE { test_hooks_->DidBeginFrame(); } virtual void Animate(double monotonic_time) OVERRIDE { test_hooks_->Animate(base::TimeTicks::FromInternalValue( @@ -263,7 +263,7 @@ class LayerTreeHostClientForTesting : public LayerTreeHostClient { test_hooks_->DidFailToInitializeOutputSurface(); } - virtual void WillCommit() OVERRIDE {} + virtual void WillCommit() OVERRIDE { test_hooks_->WillCommit(); } virtual void DidCommit() OVERRIDE { test_hooks_->DidCommit(); diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index b92e3af..61226dd 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h @@ -52,10 +52,13 @@ class TestHooks : public AnimationDelegate { virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) {} virtual void Animate(base::TimeTicks monotonic_time) {} + virtual void WillBeginFrame() {} + virtual void DidBeginFrame() {} virtual void Layout() {} virtual void DidInitializeOutputSurface(bool succeeded) {} virtual void DidFailToInitializeOutputSurface() {} virtual void DidAddAnimation() {} + virtual void WillCommit() {} virtual void DidCommit() {} virtual void DidCommitAndDrawFrame() {} virtual void DidCompleteSwapBuffers() {} diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index cdb5f61..0c82db8 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc @@ -469,6 +469,8 @@ void LayerTreeHost::SetNeedsAnimate() { proxy_->SetNeedsAnimate(); } +void LayerTreeHost::SetNeedsUpdateLayers() { proxy_->SetNeedsUpdateLayers(); } + void LayerTreeHost::SetNeedsCommit() { if (!prepaint_callback_.IsCancelled()) { TRACE_EVENT_INSTANT0("cc", @@ -581,6 +583,11 @@ void LayerTreeHost::SetOverdrawBottomHeight(float overdraw_bottom_height) { SetNeedsCommit(); } +void LayerTreeHost::ApplyPageScaleDeltaFromImplSide(float page_scale_delta) { + DCHECK(CommitRequested()); + page_scale_factor_ *= page_scale_delta; +} + void LayerTreeHost::SetPageScaleFactorAndLimits(float page_scale_factor, float min_page_scale_factor, float max_page_scale_factor) { @@ -965,8 +972,8 @@ void LayerTreeHost::ApplyScrollAndScale(const ScrollAndScaleSet& info) { if (!root_layer_.get()) return; - Layer* root_scroll_layer = FindFirstScrollableLayer(root_layer_.get()); gfx::Vector2d root_scroll_delta; + Layer* root_scroll_layer = FindFirstScrollableLayer(root_layer_.get()); for (size_t i = 0; i < info.scrolls.size(); ++i) { Layer* layer = @@ -981,8 +988,22 @@ void LayerTreeHost::ApplyScrollAndScale(const ScrollAndScaleSet& info) { info.scrolls[i].scroll_delta); } } - if (!root_scroll_delta.IsZero() || info.page_scale_delta != 1.f) + + if (!root_scroll_delta.IsZero() || info.page_scale_delta != 1.f) { + // SetScrollOffsetFromImplSide above could have destroyed the tree, + // so re-get this layer before doing anything to it. + root_scroll_layer = FindFirstScrollableLayer(root_layer_.get()); + + // Preemptively apply the scroll offset and scale delta here before sending + // it to the client. If the client comes back and sets it to the same + // value, then the layer can early out without needing a full commit. + if (root_scroll_layer) { + root_scroll_layer->SetScrollOffsetFromImplSide( + root_scroll_layer->scroll_offset() + root_scroll_delta); + } + ApplyPageScaleDeltaFromImplSide(info.page_scale_delta); client_->ApplyScrollAndScale(root_scroll_delta, info.page_scale_delta); + } } void LayerTreeHost::StartRateLimiter(WebKit::WebGraphicsContext3D* context3d) { diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index f04871a..2ec3eac 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -165,6 +165,7 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { const RendererCapabilities& GetRendererCapabilities() const; void SetNeedsAnimate(); + virtual void SetNeedsUpdateLayers(); virtual void SetNeedsCommit(); virtual void SetNeedsFullTreeSync(); void SetNeedsRedraw(); @@ -189,6 +190,7 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { gfx::Size device_viewport_size() const { return device_viewport_size_; } float overdraw_bottom_height() const { return overdraw_bottom_height_; } + void ApplyPageScaleDeltaFromImplSide(float page_scale_delta); void SetPageScaleFactorAndLimits(float page_scale_factor, float min_page_scale_factor, float max_page_scale_factor); diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 629877f..f97eea0 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1180,8 +1180,10 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame, TRACE_EVENT0("cc", "LayerTreeHostImpl::DrawLayers"); DCHECK(CanDraw()); - if (frame->has_no_damage) + if (frame->has_no_damage) { + TRACE_EVENT0("cc", "EarlyOut_NoDamage"); return; + } DCHECK(!frame->render_passes.empty()); diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 4d768205..e9435f7 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -1812,7 +1812,7 @@ class LayerTreeHostTestContinuousCommit : public LayerTreeHostTest { virtual void DidCommit() OVERRIDE { if (num_draw_layers_ == 2) return; - layer_tree_host()->root_layer()->SetNeedsDisplay(); + layer_tree_host()->SetNeedsCommit(); } virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { @@ -3099,6 +3099,9 @@ class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { virtual void SetupTree() OVERRIDE { layer_ = FakePictureLayer::Create(&client_); + // Force commits to not be aborted so new frames get drawn, otherwise + // the renderer gets deferred initialized but nothing new needs drawing. + layer_->set_always_update_resources(true); layer_tree_host()->SetRootLayer(layer_); LayerTreeHostTest::SetupTree(); } @@ -3395,9 +3398,11 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { ++expected_push_properties_grandchild_; break; case 16: + // SetNeedsDisplay does not always set needs commit (so call it + // explicitly), but is a property change. child_->SetNeedsDisplay(); - // The modified layer needs commit ++expected_push_properties_child_; + layer_tree_host()->SetNeedsCommit(); break; case 17: EndTest(); diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc index ccb7afd..b014941 100644 --- a/cc/trees/layer_tree_host_unittest_context.cc +++ b/cc/trees/layer_tree_host_unittest_context.cc @@ -286,6 +286,7 @@ class LayerTreeHostContextTestLostContextSucceeds virtual void InvalidateAndSetNeedsCommit() { // Cause damage so we try to draw. layer_tree_host()->root_layer()->SetNeedsDisplay(); + layer_tree_host()->SetNeedsCommit(); } bool NextTestCase() { diff --git a/cc/trees/layer_tree_host_unittest_damage.cc b/cc/trees/layer_tree_host_unittest_damage.cc index 523eecd..e8ae7a2 100644 --- a/cc/trees/layer_tree_host_unittest_damage.cc +++ b/cc/trees/layer_tree_host_unittest_damage.cc @@ -87,6 +87,7 @@ class LayerTreeHostDamageTestNoDamageDoesNotSwap case 3: // Cause non-visible damage. content_->SetNeedsDisplayRect(gfx::Rect(1990, 1990, 10, 10)); + layer_tree_host()->SetNeedsCommit(); break; } } diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc index b07b386..f0e1754 100644 --- a/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/cc/trees/layer_tree_host_unittest_scroll.cc @@ -70,10 +70,8 @@ class LayerTreeHostScrollTestScrollSimple : public LayerTreeHostScrollTest { } } - virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) - OVERRIDE { - gfx::Vector2d offset = layer_tree_host()->root_layer()->scroll_offset(); - layer_tree_host()->root_layer()->SetScrollOffset(offset + scroll_delta); + virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, + float scale) OVERRIDE { num_scrolls_++; } @@ -150,10 +148,8 @@ class LayerTreeHostScrollTestScrollMultipleRedraw } } - virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) - OVERRIDE { - gfx::Vector2d offset = layer_tree_host()->root_layer()->scroll_offset(); - layer_tree_host()->root_layer()->SetScrollOffset(offset + scroll_delta); + virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, + float scale) OVERRIDE { num_scrolls_++; } @@ -167,6 +163,182 @@ class LayerTreeHostScrollTestScrollMultipleRedraw MULTI_THREAD_TEST_F(LayerTreeHostScrollTestScrollMultipleRedraw); +class LayerTreeHostScrollTestScrollAbortedCommit + : public LayerTreeHostScrollTest { + public: + LayerTreeHostScrollTestScrollAbortedCommit() + : initial_scroll_(50, 60), + impl_scroll_(-3, 2), + second_main_scroll_(14, -3), + impl_scale_(2.f), + num_will_begin_frames_(0), + num_did_begin_frames_(0), + num_will_commits_(0), + num_did_commits_(0), + num_impl_commits_(0), + num_impl_scrolls_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void SetupTree() OVERRIDE { + LayerTreeHostScrollTest::SetupTree(); + scoped_refptr<Layer> root_scroll_layer = Layer::Create(); + root_scroll_layer->SetScrollable(true); + root_scroll_layer->SetScrollOffset(initial_scroll_); + root_scroll_layer->SetBounds(gfx::Size(200, 200)); + root_scroll_layer->SetMaxScrollOffset(gfx::Vector2d(100, 100)); + root_scroll_layer->SetIsDrawable(true); + layer_tree_host()->root_layer()->AddChild(root_scroll_layer); + + layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.01f, 100.f); + } + + virtual void WillBeginFrame() OVERRIDE { + num_will_begin_frames_++; + Layer* root_scroll_layer = layer_tree_host()->root_layer()->children()[0]; + switch (num_will_begin_frames_) { + case 1: + // This will not be aborted because of the initial prop changes. + EXPECT_EQ(0, num_impl_scrolls_); + EXPECT_EQ(0, layer_tree_host()->source_frame_number()); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_); + EXPECT_EQ(1.f, layer_tree_host()->page_scale_factor()); + break; + case 2: + // This commit will be aborted, and another commit will be + // initiated from the redraw. + EXPECT_EQ(1, num_impl_scrolls_); + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), + initial_scroll_ + impl_scroll_); + EXPECT_EQ(impl_scale_, layer_tree_host()->page_scale_factor()); + PostSetNeedsRedrawToMainThread(); + break; + case 3: + // This commit will not be aborted because of the scroll change. + EXPECT_EQ(2, num_impl_scrolls_); + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), + initial_scroll_ + impl_scroll_ + impl_scroll_); + EXPECT_EQ(impl_scale_ * impl_scale_, + layer_tree_host()->page_scale_factor()); + root_scroll_layer->SetScrollOffset(root_scroll_layer->scroll_offset() + + second_main_scroll_); + break; + case 4: + // This commit will also be aborted. + EXPECT_EQ(3, num_impl_scrolls_); + EXPECT_EQ(2, layer_tree_host()->source_frame_number()); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), + initial_scroll_ + impl_scroll_ + impl_scroll_ + + impl_scroll_ + second_main_scroll_); + // End the test by drawing to verify this commit is also aborted. + PostSetNeedsRedrawToMainThread(); + break; + } + } + + virtual void DidBeginFrame() OVERRIDE { num_did_begin_frames_++; } + + virtual void WillCommit() OVERRIDE { num_will_commits_++; } + + virtual void DidCommit() OVERRIDE { num_did_commits_++; } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_impl_commits_++; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerImpl* root_scroll_layer = + impl->active_tree()->root_layer()->children()[0]; + + if (impl->active_tree()->source_frame_number() == 0 && + impl->SourceAnimationFrameNumber() == 1) { + // First draw + EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); + root_scroll_layer->ScrollBy(impl_scroll_); + EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), impl_scroll_); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_); + + EXPECT_EQ(1.f, impl->active_tree()->page_scale_delta()); + EXPECT_EQ(1.f, impl->active_tree()->total_page_scale_factor()); + impl->active_tree()->SetPageScaleDelta(impl_scale_); + EXPECT_EQ(impl_scale_, impl->active_tree()->page_scale_delta()); + EXPECT_EQ(impl_scale_, impl->active_tree()->total_page_scale_factor()); + + // To simplify the testing flow, don't redraw here, just commit. + impl->SetNeedsCommit(); + } else if (impl->active_tree()->source_frame_number() == 0 && + impl->SourceAnimationFrameNumber() == 2) { + // Test a second draw after an aborted commit. + // The scroll/scale values should be baked into the offset/scale factor + // since the main thread consumed but aborted the begin frame. + EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); + root_scroll_layer->ScrollBy(impl_scroll_); + EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), impl_scroll_); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), + initial_scroll_ + impl_scroll_); + + EXPECT_EQ(1.f, impl->active_tree()->page_scale_delta()); + EXPECT_EQ(impl_scale_, impl->active_tree()->total_page_scale_factor()); + impl->active_tree()->SetPageScaleDelta(impl_scale_); + EXPECT_EQ(impl_scale_, impl->active_tree()->page_scale_delta()); + EXPECT_EQ(impl_scale_ * impl_scale_, + impl->active_tree()->total_page_scale_factor()); + + impl->SetNeedsCommit(); + } else if (impl->active_tree()->source_frame_number() == 1 && + impl->SourceAnimationFrameNumber() == 3) { + // Third draw after the second full commit. + EXPECT_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); + root_scroll_layer->ScrollBy(impl_scroll_); + impl->SetNeedsCommit(); + EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), impl_scroll_); + EXPECT_VECTOR_EQ( + root_scroll_layer->scroll_offset(), + initial_scroll_ + impl_scroll_ + impl_scroll_ + second_main_scroll_); + } else if (impl->active_tree()->source_frame_number() == 1 && + impl->SourceAnimationFrameNumber() == 4) { + // Final draw after the second aborted commit. + EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); + EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), + initial_scroll_ + impl_scroll_ + impl_scroll_ + + impl_scroll_ + second_main_scroll_); + EndTest(); + } + } + + virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, + float scale) OVERRIDE { + num_impl_scrolls_++; + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(3, num_impl_scrolls_); + // Verify that the embedder sees aborted commits as real commits. + EXPECT_EQ(4, num_will_begin_frames_); + EXPECT_EQ(4, num_did_begin_frames_); + EXPECT_EQ(4, num_will_commits_); + EXPECT_EQ(4, num_did_commits_); + // ...but the compositor thread only sees two real ones. + EXPECT_EQ(2, num_impl_commits_); + } + + private: + gfx::Vector2d initial_scroll_; + gfx::Vector2d impl_scroll_; + gfx::Vector2d second_main_scroll_; + float impl_scale_; + int num_will_begin_frames_; + int num_did_begin_frames_; + int num_will_commits_; + int num_did_commits_; + int num_impl_commits_; + int num_impl_scrolls_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostScrollTestScrollAbortedCommit); + class LayerTreeHostScrollTestFractionalScroll : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestFractionalScroll() : scroll_amount_(1.75, 0) {} @@ -208,12 +380,6 @@ class LayerTreeHostScrollTestFractionalScroll : public LayerTreeHostScrollTest { root->ScrollBy(scroll_amount_); } - virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) - OVERRIDE { - gfx::Vector2d offset = layer_tree_host()->root_layer()->scroll_offset(); - layer_tree_host()->root_layer()->SetScrollOffset(offset + scroll_delta); - } - virtual void AfterTest() OVERRIDE {} private: @@ -285,14 +451,20 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + virtual void WillCommit() OVERRIDE { + // Keep the test committing (otherwise the early out for no update + // will stall the test). + if (layer_tree_host()->source_frame_number() < 2) { + layer_tree_host()->SetNeedsCommit(); + } + } + void DidScroll() { final_scroll_offset_ = expected_scroll_layer_->scroll_offset(); } - virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) - OVERRIDE { - gfx::Vector2d offset = root_scroll_layer_->scroll_offset(); - root_scroll_layer_->SetScrollOffset(offset + scroll_delta); + virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, + float scale) OVERRIDE { num_scrolls_++; } @@ -642,10 +814,8 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { } } - virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) - OVERRIDE { - gfx::Vector2d offset = layer_tree_host()->root_layer()->scroll_offset(); - layer_tree_host()->root_layer()->SetScrollOffset(offset + scroll_delta); + virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, + float scale) OVERRIDE { num_scrolls_++; } diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 860f067..c160d97 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -231,6 +231,24 @@ void LayerTreeImpl::UpdateMaxScrollOffset() { root_scroll_layer_->SetMaxScrollOffset(gfx::ToFlooredVector2d(max_scroll)); } +static void ApplySentScrollDeltasOn(LayerImpl* layer) { + layer->ApplySentScrollDeltas(); +} + +void LayerTreeImpl::ApplySentScrollAndScaleDeltas() { + DCHECK(IsActiveTree()); + + page_scale_factor_ *= sent_page_scale_delta_; + page_scale_delta_ /= sent_page_scale_delta_; + sent_page_scale_delta_ = 1.f; + + if (!root_layer()) + return; + + LayerTreeHostCommon::CallFunctionForSubtree( + root_layer(), base::Bind(&ApplySentScrollDeltasOn)); +} + void LayerTreeImpl::UpdateSolidColorScrollbars() { DCHECK(settings().solid_color_scrollbars); diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index aafaf2a..27aa6fa 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -109,6 +109,7 @@ class CC_EXPORT LayerTreeImpl { void FindRootScrollLayer(); void UpdateMaxScrollOffset(); + void ApplySentScrollAndScaleDeltas(); SkColor background_color() const { return background_color_; } void set_background_color(SkColor color) { background_color_ = color; } diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index e3b5f82..f4a1ef9 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -66,6 +66,7 @@ class CC_EXPORT Proxy { virtual const RendererCapabilities& GetRendererCapabilities() const = 0; virtual void SetNeedsAnimate() = 0; + virtual void SetNeedsUpdateLayers() = 0; virtual void SetNeedsCommit() = 0; virtual void SetNeedsRedraw(gfx::Rect damage_rect) = 0; diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 426b44f..2c4d3a8 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc @@ -171,6 +171,11 @@ void SingleThreadProxy::SetNeedsAnimate() { NOTREACHED(); } +void SingleThreadProxy::SetNeedsUpdateLayers() { + DCHECK(Proxy::IsMainThread()); + layer_tree_host_->ScheduleComposite(); +} + void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) { DCHECK(Proxy::IsMainThread()); // Commit immediately. @@ -203,11 +208,12 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) { layer_tree_host_impl_->CommitComplete(); #ifndef NDEBUG - // In the single-threaded case, the scroll deltas should never be + // In the single-threaded case, the scale and scroll deltas should never be // touched on the impl layer tree. scoped_ptr<ScrollAndScaleSet> scroll_info = layer_tree_host_impl_->ProcessScrollDeltas(); DCHECK(!scroll_info->scrolls.size()); + DCHECK_EQ(1.f, scroll_info->page_scale_delta); #endif base::TimeDelta duration = stats_instrumentation->EndRecording(start_time); diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index b344cc3..9f7eed4 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -32,6 +32,7 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { virtual void CreateAndInitializeOutputSurface() OVERRIDE; virtual const RendererCapabilities& GetRendererCapabilities() const OVERRIDE; virtual void SetNeedsAnimate() OVERRIDE; + virtual void SetNeedsUpdateLayers() OVERRIDE; virtual void SetNeedsCommit() OVERRIDE; virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE; virtual void SetDeferCommits(bool defer_commits) OVERRIDE; diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index 2bf8247..3d73b21 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -90,6 +90,7 @@ ThreadProxy::ThreadProxy( using_synchronous_renderer_compositor_( layer_tree_host->settings().using_synchronous_renderer_compositor), inside_draw_(false), + can_cancel_commit_(true), defer_commits_(false), renew_tree_priority_on_impl_thread_pending_(false), draw_duration_history_(kDurationHistorySize), @@ -128,10 +129,15 @@ bool ThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) { &begin_frame_sent_to_main_thread_completion)); begin_frame_sent_to_main_thread_completion.Wait(); } + in_composite_and_readback_ = true; BeginFrameOnMainThread(scoped_ptr<BeginFrameAndCommitState>()); in_composite_and_readback_ = false; + // Composite and readback requires a second commit to undo any changes + // that it made. + can_cancel_commit_ = false; + // Perform a synchronous readback. ReadbackRequest request; request.rect = rect; @@ -301,6 +307,17 @@ void ThreadProxy::OnOutputSurfaceInitializeAttempted( } } +void ThreadProxy::SendCommitRequestToImplThreadIfNeeded() { + DCHECK(IsMainThread()); + if (commit_request_sent_to_impl_thread_) + return; + commit_request_sent_to_impl_thread_ = true; + Proxy::ImplThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread, + impl_thread_weak_ptr_)); +} + const RendererCapabilities& ThreadProxy::GetRendererCapabilities() const { DCHECK(IsMainThread()); DCHECK(!layer_tree_host_->output_surface_lost()); @@ -314,30 +331,26 @@ void ThreadProxy::SetNeedsAnimate() { TRACE_EVENT0("cc", "ThreadProxy::SetNeedsAnimate"); animate_requested_ = true; + can_cancel_commit_ = false; + SendCommitRequestToImplThreadIfNeeded(); +} - if (commit_request_sent_to_impl_thread_) - return; - commit_request_sent_to_impl_thread_ = true; - Proxy::ImplThreadTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread, - impl_thread_weak_ptr_)); +void ThreadProxy::SetNeedsUpdateLayers() { + DCHECK(IsMainThread()); + SendCommitRequestToImplThreadIfNeeded(); } void ThreadProxy::SetNeedsCommit() { DCHECK(IsMainThread()); + // Unconditionally set here to handle SetNeedsCommit calls during a commit. + can_cancel_commit_ = false; + if (commit_requested_) return; TRACE_EVENT0("cc", "ThreadProxy::SetNeedsCommit"); commit_requested_ = true; - if (commit_request_sent_to_impl_thread_) - return; - commit_request_sent_to_impl_thread_ = true; - Proxy::ImplThreadTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread, - impl_thread_weak_ptr_)); + SendCommitRequestToImplThreadIfNeeded(); } void ThreadProxy::DidLoseOutputSurfaceOnImplThread() { @@ -680,21 +693,23 @@ void ThreadProxy::BeginFrameOnMainThread( // callbacks will trigger another frame. animate_requested_ = false; - if (begin_frame_state) - layer_tree_host_->ApplyScrollAndScale(*begin_frame_state->scroll_info); - if (!in_composite_and_readback_ && !layer_tree_host_->visible()) { commit_requested_ = false; commit_request_sent_to_impl_thread_ = false; TRACE_EVENT0("cc", "EarlyOut_NotVisible"); + bool did_handle = false; Proxy::ImplThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread, - impl_thread_weak_ptr_)); + impl_thread_weak_ptr_, + did_handle)); return; } + if (begin_frame_state) + layer_tree_host_->ApplyScrollAndScale(*begin_frame_state->scroll_info); + layer_tree_host_->WillBeginFrame(); if (begin_frame_state) { @@ -718,24 +733,44 @@ void ThreadProxy::BeginFrameOnMainThread( // UpdateLayers. commit_requested_ = false; commit_request_sent_to_impl_thread_ = false; + bool can_cancel_this_commit = + can_cancel_commit_ && !in_composite_and_readback_; + can_cancel_commit_ = true; scoped_ptr<ResourceUpdateQueue> queue = make_scoped_ptr(new ResourceUpdateQueue); - layer_tree_host_->UpdateLayers( + bool updated = layer_tree_host_->UpdateLayers( queue.get(), - begin_frame_state ? - begin_frame_state->memory_allocation_limit_bytes : 0u); + begin_frame_state ? begin_frame_state->memory_allocation_limit_bytes + : 0u); // Once single buffered layers are committed, they cannot be modified until // they are drawn by the impl thread. textures_acquired_ = false; layer_tree_host_->WillCommit(); - // Before applying scrolls and calling animate, we set animate_requested_ to - // false. If it is true now, it means SetNeedAnimate was called again, but - // during a state when commit_request_sent_to_impl_thread_ = true. We need to - // force that call to happen again now so that the commit request is sent to - // the impl thread. + + if (!updated && can_cancel_this_commit) { + TRACE_EVENT0("cc", "EarlyOut_NoUpdates"); + bool did_handle = true; + Proxy::ImplThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread, + impl_thread_weak_ptr_, + did_handle)); + + // Although the commit is internally aborted, this is because it has been + // detected to be a no-op. From the perspective of an embedder, this commit + // went through, and input should no longer be throttled, etc. + layer_tree_host_->CommitComplete(); + layer_tree_host_->DidBeginFrame(); + return; + } + + // Before calling animate, we set animate_requested_ to false. If it is true + // now, it means SetNeedAnimate was called again, but during a state when + // commit_request_sent_to_impl_thread_ = true. We need to force that call to + // happen again now so that the commit request is sent to the impl thread. if (animate_requested_) { // Forces SetNeedsAnimate to consider posting a commit task. animate_requested_ = false; @@ -831,13 +866,19 @@ void ThreadProxy::StartCommitOnImplThread( scheduler_on_impl_thread_->AnticipatedDrawTime()); } -void ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread() { +void ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread(bool did_handle) { TRACE_EVENT0("cc", "ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread"); DCHECK(IsImplThread()); DCHECK(scheduler_on_impl_thread_); DCHECK(scheduler_on_impl_thread_->CommitPending()); + DCHECK(!layer_tree_host_impl_->pending_tree()); - scheduler_on_impl_thread_->BeginFrameAbortedByMainThread(); + // If the begin frame data was handled, then scroll and scale set was applied + // by the main thread, so the active tree needs to be updated as if these sent + // values were applied and committed. + if (did_handle) + layer_tree_host_impl_->active_tree()->ApplySentScrollAndScaleDeltas(); + scheduler_on_impl_thread_->BeginFrameAbortedByMainThread(did_handle); } void ThreadProxy::ScheduledActionCommit() { @@ -905,7 +946,8 @@ void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() { ScheduledActionDrawAndSwapResult ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { - TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndSwap"); + TRACE_EVENT1( + "cc", "ThreadProxy::ScheduledActionDrawAndSwap", "forced", forced_draw); ScheduledActionDrawAndSwapResult result; result.did_draw = false; @@ -1066,6 +1108,7 @@ void ThreadProxy::AcquireLayerTextures() { completion.Wait(); textures_acquired_ = true; + can_cancel_commit_ = false; } void ThreadProxy::AcquireLayerTexturesForMainThreadOnImplThread( diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h index eb2ef7e..c9d5b47 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h @@ -49,6 +49,7 @@ class ThreadProxy : public Proxy, virtual void CreateAndInitializeOutputSurface() OVERRIDE; virtual const RendererCapabilities& GetRendererCapabilities() const OVERRIDE; virtual void SetNeedsAnimate() OVERRIDE; + virtual void SetNeedsUpdateLayers() OVERRIDE; virtual void SetNeedsCommit() OVERRIDE; virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE; virtual void SetDeferCommits(bool defer_commits) OVERRIDE; @@ -135,6 +136,7 @@ class ThreadProxy : public Proxy, void OnOutputSurfaceInitializeAttempted( bool success, const RendererCapabilities& capabilities); + void SendCommitRequestToImplThreadIfNeeded(); // Called on impl thread. struct ReadbackRequest; @@ -146,7 +148,7 @@ class ThreadProxy : public Proxy, CompletionEvent* completion, ResourceUpdateQueue* queue, scoped_refptr<cc::ContextProvider> offscreen_context_provider); - void BeginFrameAbortedByMainThreadOnImplThread(); + void BeginFrameAbortedByMainThreadOnImplThread(bool did_handle); void RequestReadbackOnImplThread(ReadbackRequest* request); void FinishAllRenderingOnImplThread(CompletionEvent* completion); void InitializeImplOnImplThread(CompletionEvent* completion); @@ -241,6 +243,8 @@ class ThreadProxy : public Proxy, bool inside_draw_; + bool can_cancel_commit_; + bool defer_commits_; scoped_ptr<BeginFrameAndCommitState> pending_deferred_commit_; |