| // Copyright 2015 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "cc/trees/proxy_main.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <string> | 
 |  | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "base/trace_event/trace_event_argument.h" | 
 | #include "base/trace_event/trace_event_synthetic_delay.h" | 
 | #include "cc/animation/animation_events.h" | 
 | #include "cc/debug/benchmark_instrumentation.h" | 
 | #include "cc/debug/devtools_instrumentation.h" | 
 | #include "cc/output/output_surface.h" | 
 | #include "cc/output/swap_promise.h" | 
 | #include "cc/trees/blocking_task_runner.h" | 
 | #include "cc/trees/layer_tree_host.h" | 
 | #include "cc/trees/scoped_abort_remaining_swap_promises.h" | 
 | #include "cc/trees/threaded_channel.h" | 
 |  | 
 | namespace cc { | 
 |  | 
 | scoped_ptr<ProxyMain> ProxyMain::CreateThreaded( | 
 |     LayerTreeHost* layer_tree_host, | 
 |     TaskRunnerProvider* task_runner_provider) { | 
 |   scoped_ptr<ProxyMain> proxy_main( | 
 |       new ProxyMain(layer_tree_host, task_runner_provider)); | 
 |   proxy_main->SetChannel( | 
 |       ThreadedChannel::Create(proxy_main.get(), task_runner_provider)); | 
 |   return proxy_main; | 
 | } | 
 |  | 
 | ProxyMain::ProxyMain(LayerTreeHost* layer_tree_host, | 
 |                      TaskRunnerProvider* task_runner_provider) | 
 |     : layer_tree_host_(layer_tree_host), | 
 |       task_runner_provider_(task_runner_provider), | 
 |       layer_tree_host_id_(layer_tree_host->id()), | 
 |       max_requested_pipeline_stage_(NO_PIPELINE_STAGE), | 
 |       current_pipeline_stage_(NO_PIPELINE_STAGE), | 
 |       final_pipeline_stage_(NO_PIPELINE_STAGE), | 
 |       commit_waits_for_activation_(false), | 
 |       started_(false), | 
 |       defer_commits_(false) { | 
 |   TRACE_EVENT0("cc", "ProxyMain::ProxyMain"); | 
 |   DCHECK(task_runner_provider_); | 
 |   DCHECK(IsMainThread()); | 
 | } | 
 |  | 
 | ProxyMain::~ProxyMain() { | 
 |   TRACE_EVENT0("cc", "ProxyMain::~ProxyMain"); | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK(!started_); | 
 | } | 
 |  | 
 | void ProxyMain::SetChannel(scoped_ptr<ChannelMain> channel_main) { | 
 |   DCHECK(!channel_main_); | 
 |   channel_main_ = std::move(channel_main); | 
 | } | 
 |  | 
 | void ProxyMain::DidCompleteSwapBuffers() { | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->DidCompleteSwapBuffers(); | 
 | } | 
 |  | 
 | void ProxyMain::SetRendererCapabilities( | 
 |     const RendererCapabilities& capabilities) { | 
 |   DCHECK(IsMainThread()); | 
 |   renderer_capabilities_ = capabilities; | 
 | } | 
 |  | 
 | void ProxyMain::BeginMainFrameNotExpectedSoon() { | 
 |   TRACE_EVENT0("cc", "ProxyMain::BeginMainFrameNotExpectedSoon"); | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->BeginMainFrameNotExpectedSoon(); | 
 | } | 
 |  | 
 | void ProxyMain::DidCommitAndDrawFrame() { | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->DidCommitAndDrawFrame(); | 
 | } | 
 |  | 
 | void ProxyMain::SetAnimationEvents(scoped_ptr<AnimationEvents> events) { | 
 |   TRACE_EVENT0("cc", "ProxyMain::SetAnimationEvents"); | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->SetAnimationEvents(std::move(events)); | 
 | } | 
 |  | 
 | void ProxyMain::DidLoseOutputSurface() { | 
 |   TRACE_EVENT0("cc", "ProxyMain::DidLoseOutputSurface"); | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->DidLoseOutputSurface(); | 
 | } | 
 |  | 
 | void ProxyMain::RequestNewOutputSurface() { | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->RequestNewOutputSurface(); | 
 | } | 
 |  | 
 | void ProxyMain::DidInitializeOutputSurface( | 
 |     bool success, | 
 |     const RendererCapabilities& capabilities) { | 
 |   TRACE_EVENT0("cc", "ProxyMain::DidInitializeOutputSurface"); | 
 |   DCHECK(IsMainThread()); | 
 |  | 
 |   if (!success) { | 
 |     layer_tree_host_->DidFailToInitializeOutputSurface(); | 
 |     return; | 
 |   } | 
 |   renderer_capabilities_ = capabilities; | 
 |   layer_tree_host_->DidInitializeOutputSurface(); | 
 | } | 
 |  | 
 | void ProxyMain::DidCompletePageScaleAnimation() { | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->DidCompletePageScaleAnimation(); | 
 | } | 
 |  | 
 | void ProxyMain::PostFrameTimingEventsOnMain( | 
 |     scoped_ptr<FrameTimingTracker::CompositeTimingSet> composite_events, | 
 |     scoped_ptr<FrameTimingTracker::MainFrameTimingSet> main_frame_events) { | 
 |   DCHECK(IsMainThread()); | 
 |   layer_tree_host_->RecordFrameTimingEvents(std::move(composite_events), | 
 |                                             std::move(main_frame_events)); | 
 | } | 
 |  | 
 | void ProxyMain::BeginMainFrame( | 
 |     scoped_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) { | 
 |   benchmark_instrumentation::ScopedBeginFrameTask begin_frame_task( | 
 |       benchmark_instrumentation::kDoBeginFrame, | 
 |       begin_main_frame_state->begin_frame_id); | 
 |  | 
 |   base::TimeTicks begin_main_frame_start_time = base::TimeTicks::Now(); | 
 |  | 
 |   TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("cc.BeginMainFrame"); | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK_EQ(NO_PIPELINE_STAGE, current_pipeline_stage_); | 
 |  | 
 |   if (defer_commits_) { | 
 |     TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |     channel_main_->BeginMainFrameAbortedOnImpl( | 
 |         CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT, | 
 |         begin_main_frame_start_time); | 
 |     return; | 
 |   } | 
 |  | 
 |   // If the commit finishes, LayerTreeHost will transfer its swap promises to | 
 |   // LayerTreeImpl. The destructor of ScopedSwapPromiseChecker aborts the | 
 |   // remaining swap promises. | 
 |   ScopedAbortRemainingSwapPromises swap_promise_checker(layer_tree_host_); | 
 |  | 
 |   final_pipeline_stage_ = max_requested_pipeline_stage_; | 
 |   max_requested_pipeline_stage_ = NO_PIPELINE_STAGE; | 
 |  | 
 |   if (!layer_tree_host_->visible()) { | 
 |     TRACE_EVENT_INSTANT0("cc", "EarlyOut_NotVisible", TRACE_EVENT_SCOPE_THREAD); | 
 |     channel_main_->BeginMainFrameAbortedOnImpl( | 
 |         CommitEarlyOutReason::ABORTED_NOT_VISIBLE, begin_main_frame_start_time); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (layer_tree_host_->output_surface_lost()) { | 
 |     TRACE_EVENT_INSTANT0("cc", "EarlyOut_OutputSurfaceLost", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |     channel_main_->BeginMainFrameAbortedOnImpl( | 
 |         CommitEarlyOutReason::ABORTED_OUTPUT_SURFACE_LOST, | 
 |         begin_main_frame_start_time); | 
 |     return; | 
 |   } | 
 |  | 
 |   current_pipeline_stage_ = ANIMATE_PIPELINE_STAGE; | 
 |  | 
 |   layer_tree_host_->ApplyScrollAndScale( | 
 |       begin_main_frame_state->scroll_info.get()); | 
 |  | 
 |   layer_tree_host_->WillBeginMainFrame(); | 
 |  | 
 |   layer_tree_host_->BeginMainFrame(begin_main_frame_state->begin_frame_args); | 
 |   layer_tree_host_->AnimateLayers( | 
 |       begin_main_frame_state->begin_frame_args.frame_time); | 
 |  | 
 |   // Recreate all UI resources if there were evicted UI resources when the impl | 
 |   // thread initiated the commit. | 
 |   if (begin_main_frame_state->evicted_ui_resources) | 
 |     layer_tree_host_->RecreateUIResources(); | 
 |  | 
 |   layer_tree_host_->RequestMainFrameUpdate(); | 
 |   TRACE_EVENT_SYNTHETIC_DELAY_END("cc.BeginMainFrame"); | 
 |  | 
 |   bool can_cancel_this_commit = final_pipeline_stage_ < COMMIT_PIPELINE_STAGE && | 
 |                                 !begin_main_frame_state->evicted_ui_resources; | 
 |  | 
 |   current_pipeline_stage_ = UPDATE_LAYERS_PIPELINE_STAGE; | 
 |   bool should_update_layers = | 
 |       final_pipeline_stage_ >= UPDATE_LAYERS_PIPELINE_STAGE; | 
 |   bool updated = should_update_layers && layer_tree_host_->UpdateLayers(); | 
 |  | 
 |   layer_tree_host_->WillCommit(); | 
 |   devtools_instrumentation::ScopedCommitTrace commit_task( | 
 |       layer_tree_host_->id()); | 
 |  | 
 |   current_pipeline_stage_ = COMMIT_PIPELINE_STAGE; | 
 |   if (!updated && can_cancel_this_commit) { | 
 |     TRACE_EVENT_INSTANT0("cc", "EarlyOut_NoUpdates", TRACE_EVENT_SCOPE_THREAD); | 
 |     channel_main_->BeginMainFrameAbortedOnImpl( | 
 |         CommitEarlyOutReason::FINISHED_NO_UPDATES, begin_main_frame_start_time); | 
 |  | 
 |     // 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. | 
 |     current_pipeline_stage_ = NO_PIPELINE_STAGE; | 
 |     layer_tree_host_->CommitComplete(); | 
 |     layer_tree_host_->DidBeginMainFrame(); | 
 |     layer_tree_host_->BreakSwapPromises(SwapPromise::COMMIT_NO_UPDATE); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Notify the impl thread that the main thread is ready to commit. This will | 
 |   // begin the commit process, which is blocking from the main thread's | 
 |   // point of view, but asynchronously performed on the impl thread, | 
 |   // coordinated by the Scheduler. | 
 |   { | 
 |     TRACE_EVENT0("cc", "ProxyMain::BeginMainFrame::commit"); | 
 |  | 
 |     DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_); | 
 |  | 
 |     // This CapturePostTasks should be destroyed before CommitComplete() is | 
 |     // called since that goes out to the embedder, and we want the embedder | 
 |     // to receive its callbacks before that. | 
 |     BlockingTaskRunner::CapturePostTasks blocked( | 
 |         task_runner_provider_->blocking_main_thread_task_runner()); | 
 |  | 
 |     bool hold_commit_for_activation = commit_waits_for_activation_; | 
 |     commit_waits_for_activation_ = false; | 
 |     CompletionEvent completion; | 
 |     channel_main_->StartCommitOnImpl(&completion, layer_tree_host_, | 
 |                                      begin_main_frame_start_time, | 
 |                                      hold_commit_for_activation); | 
 |     completion.Wait(); | 
 |   } | 
 |  | 
 |   current_pipeline_stage_ = NO_PIPELINE_STAGE; | 
 |   layer_tree_host_->CommitComplete(); | 
 |   layer_tree_host_->DidBeginMainFrame(); | 
 | } | 
 |  | 
 | void ProxyMain::FinishAllRendering() { | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK(!defer_commits_); | 
 |  | 
 |   // Make sure all GL drawing is finished on the impl thread. | 
 |   DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_); | 
 |   CompletionEvent completion; | 
 |   channel_main_->FinishAllRenderingOnImpl(&completion); | 
 |   completion.Wait(); | 
 | } | 
 |  | 
 | bool ProxyMain::IsStarted() const { | 
 |   DCHECK(IsMainThread()); | 
 |   return started_; | 
 | } | 
 |  | 
 | bool ProxyMain::CommitToActiveTree() const { | 
 |   // With ProxyMain, we use a pending tree and activate it once it's ready to | 
 |   // draw to allow input to modify the active tree and draw during raster. | 
 |   return false; | 
 | } | 
 |  | 
 | void ProxyMain::SetOutputSurface(OutputSurface* output_surface) { | 
 |   channel_main_->InitializeOutputSurfaceOnImpl(output_surface); | 
 | } | 
 |  | 
 | void ProxyMain::SetVisible(bool visible) { | 
 |   TRACE_EVENT1("cc", "ProxyMain::SetVisible", "visible", visible); | 
 |   channel_main_->SetVisibleOnImpl(visible); | 
 | } | 
 |  | 
 | void ProxyMain::SetThrottleFrameProduction(bool throttle) { | 
 |   TRACE_EVENT1("cc", "ProxyMain::SetThrottleFrameProduction", "throttle", | 
 |                throttle); | 
 |   channel_main_->SetThrottleFrameProductionOnImpl(throttle); | 
 | } | 
 |  | 
 | const RendererCapabilities& ProxyMain::GetRendererCapabilities() const { | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK(!layer_tree_host_->output_surface_lost()); | 
 |   return renderer_capabilities_; | 
 | } | 
 |  | 
 | void ProxyMain::SetNeedsAnimate() { | 
 |   DCHECK(IsMainThread()); | 
 |   if (SendCommitRequestToImplThreadIfNeeded(ANIMATE_PIPELINE_STAGE)) { | 
 |     TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsAnimate", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |   } | 
 | } | 
 |  | 
 | void ProxyMain::SetNeedsUpdateLayers() { | 
 |   DCHECK(IsMainThread()); | 
 |   // If we are currently animating, make sure we also update the layers. | 
 |   if (current_pipeline_stage_ == ANIMATE_PIPELINE_STAGE) { | 
 |     final_pipeline_stage_ = | 
 |         std::max(final_pipeline_stage_, UPDATE_LAYERS_PIPELINE_STAGE); | 
 |     return; | 
 |   } | 
 |   if (SendCommitRequestToImplThreadIfNeeded(UPDATE_LAYERS_PIPELINE_STAGE)) { | 
 |     TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsUpdateLayers", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |   } | 
 | } | 
 |  | 
 | void ProxyMain::SetNeedsCommit() { | 
 |   DCHECK(IsMainThread()); | 
 |   // If we are currently animating, make sure we don't skip the commit. Note | 
 |   // that requesting a commit during the layer update stage means we need to | 
 |   // schedule another full commit. | 
 |   if (current_pipeline_stage_ == ANIMATE_PIPELINE_STAGE) { | 
 |     final_pipeline_stage_ = | 
 |         std::max(final_pipeline_stage_, COMMIT_PIPELINE_STAGE); | 
 |     return; | 
 |   } | 
 |   if (SendCommitRequestToImplThreadIfNeeded(COMMIT_PIPELINE_STAGE)) { | 
 |     TRACE_EVENT_INSTANT0("cc", "ProxyMain::SetNeedsCommit", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |   } | 
 | } | 
 |  | 
 | void ProxyMain::SetNeedsRedraw(const gfx::Rect& damage_rect) { | 
 |   TRACE_EVENT0("cc", "ProxyMain::SetNeedsRedraw"); | 
 |   DCHECK(IsMainThread()); | 
 |   channel_main_->SetNeedsRedrawOnImpl(damage_rect); | 
 | } | 
 |  | 
 | void ProxyMain::SetNextCommitWaitsForActivation() { | 
 |   DCHECK(IsMainThread()); | 
 |   commit_waits_for_activation_ = true; | 
 | } | 
 |  | 
 | void ProxyMain::NotifyInputThrottledUntilCommit() { | 
 |   DCHECK(IsMainThread()); | 
 |   channel_main_->SetInputThrottledUntilCommitOnImpl(true); | 
 | } | 
 |  | 
 | void ProxyMain::SetDeferCommits(bool defer_commits) { | 
 |   DCHECK(IsMainThread()); | 
 |   if (defer_commits_ == defer_commits) | 
 |     return; | 
 |  | 
 |   defer_commits_ = defer_commits; | 
 |   if (defer_commits_) | 
 |     TRACE_EVENT_ASYNC_BEGIN0("cc", "ProxyMain::SetDeferCommits", this); | 
 |   else | 
 |     TRACE_EVENT_ASYNC_END0("cc", "ProxyMain::SetDeferCommits", this); | 
 |  | 
 |   channel_main_->SetDeferCommitsOnImpl(defer_commits); | 
 | } | 
 |  | 
 | bool ProxyMain::CommitRequested() const { | 
 |   DCHECK(IsMainThread()); | 
 |   // TODO(skyostil): Split this into something like CommitRequested() and | 
 |   // CommitInProgress(). | 
 |   return current_pipeline_stage_ != NO_PIPELINE_STAGE || | 
 |          max_requested_pipeline_stage_ >= COMMIT_PIPELINE_STAGE; | 
 | } | 
 |  | 
 | bool ProxyMain::BeginMainFrameRequested() const { | 
 |   DCHECK(IsMainThread()); | 
 |   return max_requested_pipeline_stage_ != NO_PIPELINE_STAGE; | 
 | } | 
 |  | 
 | void ProxyMain::MainThreadHasStoppedFlinging() { | 
 |   DCHECK(IsMainThread()); | 
 |   channel_main_->MainThreadHasStoppedFlingingOnImpl(); | 
 | } | 
 |  | 
 | void ProxyMain::Start( | 
 |     scoped_ptr<BeginFrameSource> external_begin_frame_source) { | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK(task_runner_provider_->HasImplThread()); | 
 |   DCHECK(channel_main_); | 
 |   DCHECK(!layer_tree_host_->settings().use_external_begin_frame_source || | 
 |          external_begin_frame_source); | 
 |  | 
 |   // Create LayerTreeHostImpl. | 
 |   channel_main_->SynchronouslyInitializeImpl( | 
 |       layer_tree_host_, std::move(external_begin_frame_source)); | 
 |  | 
 |   started_ = true; | 
 | } | 
 |  | 
 | void ProxyMain::Stop() { | 
 |   TRACE_EVENT0("cc", "ProxyMain::Stop"); | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK(started_); | 
 |  | 
 |   channel_main_->SynchronouslyCloseImpl(); | 
 |  | 
 |   layer_tree_host_ = nullptr; | 
 |   started_ = false; | 
 | } | 
 |  | 
 | bool ProxyMain::SupportsImplScrolling() const { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ProxyMain::MainFrameWillHappenForTesting() { | 
 |   DCHECK(IsMainThread()); | 
 |   bool main_frame_will_happen = false; | 
 |   { | 
 |     DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_); | 
 |     CompletionEvent completion; | 
 |     channel_main_->MainFrameWillHappenOnImplForTesting(&completion, | 
 |                                                        &main_frame_will_happen); | 
 |     completion.Wait(); | 
 |   } | 
 |   return main_frame_will_happen; | 
 | } | 
 |  | 
 | void ProxyMain::SetChildrenNeedBeginFrames(bool children_need_begin_frames) { | 
 |   NOTREACHED() << "Only used by SingleThreadProxy"; | 
 | } | 
 |  | 
 | void ProxyMain::SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) { | 
 |   NOTREACHED() << "Only used by SingleProxyMain"; | 
 | } | 
 |  | 
 | void ProxyMain::ReleaseOutputSurface() { | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK(layer_tree_host_->output_surface_lost()); | 
 |  | 
 |   DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_); | 
 |   CompletionEvent completion; | 
 |   channel_main_->ReleaseOutputSurfaceOnImpl(&completion); | 
 |   completion.Wait(); | 
 | } | 
 |  | 
 | void ProxyMain::UpdateTopControlsState(TopControlsState constraints, | 
 |                                        TopControlsState current, | 
 |                                        bool animate) { | 
 |   DCHECK(IsMainThread()); | 
 |   channel_main_->UpdateTopControlsStateOnImpl(constraints, current, animate); | 
 | } | 
 |  | 
 | bool ProxyMain::SendCommitRequestToImplThreadIfNeeded( | 
 |     CommitPipelineStage required_stage) { | 
 |   DCHECK(IsMainThread()); | 
 |   DCHECK_NE(NO_PIPELINE_STAGE, required_stage); | 
 |   bool already_posted = max_requested_pipeline_stage_ != NO_PIPELINE_STAGE; | 
 |   max_requested_pipeline_stage_ = | 
 |       std::max(max_requested_pipeline_stage_, required_stage); | 
 |   if (already_posted) | 
 |     return false; | 
 |   channel_main_->SetNeedsCommitOnImpl(); | 
 |   return true; | 
 | } | 
 |  | 
 | bool ProxyMain::IsMainThread() const { | 
 |   return task_runner_provider_->IsMainThread(); | 
 | } | 
 |  | 
 | }  // namespace cc |