blob: 513f84dc9ffc67328ed04ea5f703d9ce29eb8649 [file] [log] [blame]
// 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/debug/benchmark_instrumentation.h"
#include "cc/debug/devtools_instrumentation.h"
#include "cc/output/compositor_frame_sink.h"
#include "cc/output/swap_promise.h"
#include "cc/resources/ui_resource_manager.h"
#include "cc/trees/blocking_task_runner.h"
#include "cc/trees/layer_tree_host_in_process.h"
#include "cc/trees/mutator_host.h"
#include "cc/trees/scoped_abort_remaining_swap_promises.h"
#include "cc/trees/threaded_channel.h"
namespace cc {
std::unique_ptr<ProxyMain> ProxyMain::CreateThreaded(
LayerTreeHostInProcess* layer_tree_host,
TaskRunnerProvider* task_runner_provider) {
std::unique_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(LayerTreeHostInProcess* 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->GetId()),
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(std::unique_ptr<ChannelMain> channel_main) {
DCHECK(!channel_main_);
channel_main_ = std::move(channel_main);
}
void ProxyMain::DidReceiveCompositorFrameAck() {
DCHECK(IsMainThread());
layer_tree_host_->DidReceiveCompositorFrameAck();
}
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(std::unique_ptr<MutatorEvents> events) {
TRACE_EVENT0("cc", "ProxyMain::SetAnimationEvents");
DCHECK(IsMainThread());
layer_tree_host_->SetAnimationEvents(std::move(events));
}
void ProxyMain::DidLoseCompositorFrameSink() {
TRACE_EVENT0("cc", "ProxyMain::DidLoseCompositorFrameSink");
DCHECK(IsMainThread());
layer_tree_host_->DidLoseCompositorFrameSink();
}
void ProxyMain::RequestNewCompositorFrameSink() {
TRACE_EVENT0("cc", "ProxyMain::RequestNewCompositorFrameSink");
DCHECK(IsMainThread());
layer_tree_host_->RequestNewCompositorFrameSink();
}
void ProxyMain::DidInitializeCompositorFrameSink(bool success) {
TRACE_EVENT0("cc", "ProxyMain::DidInitializeCompositorFrameSink");
DCHECK(IsMainThread());
if (!success)
layer_tree_host_->DidFailToInitializeCompositorFrameSink();
else
layer_tree_host_->DidInitializeCompositorFrameSink();
}
void ProxyMain::DidCompletePageScaleAnimation() {
DCHECK(IsMainThread());
layer_tree_host_->DidCompletePageScaleAnimation();
}
void ProxyMain::BeginMainFrame(
std::unique_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);
std::vector<std::unique_ptr<SwapPromise>> empty_swap_promises;
channel_main_->BeginMainFrameAbortedOnImpl(
CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT,
begin_main_frame_start_time, std::move(empty_swap_promises));
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_->GetSwapPromiseManager());
final_pipeline_stage_ = max_requested_pipeline_stage_;
max_requested_pipeline_stage_ = NO_PIPELINE_STAGE;
if (!layer_tree_host_->IsVisible()) {
TRACE_EVENT_INSTANT0("cc", "EarlyOut_NotVisible", TRACE_EVENT_SCOPE_THREAD);
std::vector<std::unique_ptr<SwapPromise>> empty_swap_promises;
channel_main_->BeginMainFrameAbortedOnImpl(
CommitEarlyOutReason::ABORTED_NOT_VISIBLE, begin_main_frame_start_time,
std::move(empty_swap_promises));
return;
}
current_pipeline_stage_ = ANIMATE_PIPELINE_STAGE;
layer_tree_host_->ApplyScrollAndScale(
begin_main_frame_state->scroll_info.get());
if (begin_main_frame_state->begin_frame_callbacks) {
for (auto& callback : *begin_main_frame_state->begin_frame_callbacks)
callback.Run();
}
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_->GetUIResourceManager()->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_->GetId());
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,
layer_tree_host_->GetSwapPromiseManager()->TakeSwapPromises());
// 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();
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_->NotifyReadyToCommitOnImpl(&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();
}
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::SetCompositorFrameSink(
CompositorFrameSink* compositor_frame_sink) {
channel_main_->InitializeCompositorFrameSinkOnImpl(compositor_frame_sink);
}
void ProxyMain::SetVisible(bool visible) {
TRACE_EVENT1("cc", "ProxyMain::SetVisible", "visible", visible);
channel_main_->SetVisibleOnImpl(visible);
}
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() {
DCHECK(IsMainThread());
DCHECK(layer_tree_host_->IsThreaded());
DCHECK(channel_main_);
// Create LayerTreeHostImpl.
channel_main_->SynchronouslyInitializeImpl(layer_tree_host_);
started_ = true;
}
void ProxyMain::Stop() {
TRACE_EVENT0("cc", "ProxyMain::Stop");
DCHECK(IsMainThread());
DCHECK(started_);
channel_main_->SynchronouslyCloseImpl();
layer_tree_host_ = nullptr;
started_ = false;
}
void ProxyMain::SetMutator(std::unique_ptr<LayerTreeMutator> mutator) {
TRACE_EVENT0("compositor-worker", "ThreadProxy::SetMutator");
channel_main_->InitializeMutatorOnImpl(std::move(mutator));
}
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::ReleaseCompositorFrameSink() {
DCHECK(IsMainThread());
DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_);
CompletionEvent completion;
channel_main_->ReleaseCompositorFrameSinkOnImpl(&completion);
completion.Wait();
}
void ProxyMain::UpdateBrowserControlsState(BrowserControlsState constraints,
BrowserControlsState current,
bool animate) {
DCHECK(IsMainThread());
channel_main_->UpdateBrowserControlsStateOnImpl(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