diff options
-rw-r--r-- | cc/base/swap_promise.h | 48 | ||||
-rw-r--r-- | cc/cc.gyp | 1 | ||||
-rw-r--r-- | cc/test/layer_tree_test.cc | 14 | ||||
-rw-r--r-- | cc/test/layer_tree_test.h | 2 | ||||
-rw-r--r-- | cc/trees/layer_tree_host.cc | 18 | ||||
-rw-r--r-- | cc/trees/layer_tree_host.h | 9 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.cc | 5 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest.cc | 121 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.cc | 29 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.h | 13 | ||||
-rw-r--r-- | cc/trees/thread_proxy.cc | 23 |
11 files changed, 282 insertions, 1 deletions
diff --git a/cc/base/swap_promise.h b/cc/base/swap_promise.h new file mode 100644 index 0000000..90bf6af --- /dev/null +++ b/cc/base/swap_promise.h @@ -0,0 +1,48 @@ +// Copyright 2013 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. + +#ifndef CC_BASE_SWAP_PROMISE_H_ +#define CC_BASE_SWAP_PROMISE_H_ + +namespace cc { + +const unsigned int kMaxQueuedSwapPromiseNumber = 100; + +// When a change to the compositor's state/invalidation/whatever happens, a +// Swap Promise can be inserted into LayerTreeHost/LayerTreeImpl, to track +// whether the compositor's reply to the new state/invaliadtion/whatever is +// completed in the compositor, i.e. the compositor knows it has been sent +// to its output or not. +// +// If the new compositor state is sent to the output, SwapPromise::DidSwap() +// will be called, and if the compositor fails to send its new state to the +// output, SwapPromise::DidNotSwap() will be called. +// +// Client wishes to use SwapPromise should have a subclass that defines +// the behavior of DidSwap() and DidNotSwap(). Notice that the promise can +// be broken at either main or impl thread, e.g. commit fails on main thread, +// new frame data has no actual damage so LayerTreeHostImpl::SwapBuffers() +// bails out early on impl thread, so don't assume that DidSwap() and +// DidNotSwap() are called at a particular thread. It is better to let the +// subclass carry thread-safe member data and operate on that member data in +// DidSwap() and DidNotSwap(). +class SwapPromise { + public: + SwapPromise() {} + virtual ~SwapPromise() {} + + enum DidNotSwapReason { + DID_NOT_SWAP_UNKNOWN, + SWAP_FAILS, + COMMIT_FAILS, + SWAP_PROMISE_LIST_OVERFLOW, + }; + + virtual void DidSwap() = 0; + virtual void DidNotSwap(DidNotSwapReason reason) = 0; +}; + +} // namespace cc + +#endif // CC_BASE_SWAP_PROMISE_H_ @@ -66,6 +66,7 @@ 'base/scoped_ptr_algorithm.h', 'base/scoped_ptr_deque.h', 'base/scoped_ptr_vector.h', + 'base/swap_promise.h', 'base/switches.cc', 'base/switches.h', 'base/tiling_data.cc', diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index d70d797..de0b907 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -409,6 +409,13 @@ void LayerTreeTest::PostSetNeedsCommitToMainThread() { main_thread_weak_ptr_)); } +void LayerTreeTest::PostSetNeedsUpdateLayersToMainThread() { + main_task_runner_->PostTask( + FROM_HERE, + base::Bind(&LayerTreeTest::DispatchSetNeedsUpdateLayers, + main_thread_weak_ptr_)); +} + void LayerTreeTest::PostReadbackToMainThread() { main_task_runner_->PostTask( FROM_HERE, @@ -538,6 +545,13 @@ void LayerTreeTest::DispatchSetNeedsCommit() { layer_tree_host_->SetNeedsCommit(); } +void LayerTreeTest::DispatchSetNeedsUpdateLayers() { + DCHECK(!proxy() || proxy()->IsMainThread()); + + if (layer_tree_host_) + layer_tree_host_->SetNeedsUpdateLayers(); +} + void LayerTreeTest::DispatchReadback() { DCHECK(!proxy() || proxy()->IsMainThread()); diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 1800c1a..ac35a65 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h @@ -114,6 +114,7 @@ class LayerTreeTest : public testing::Test, public TestHooks { void PostAddInstantAnimationToMainThread(Layer* layer_to_receive_animation); void PostAddLongAnimationToMainThread(Layer* layer_to_receive_animation); void PostSetNeedsCommitToMainThread(); + void PostSetNeedsUpdateLayersToMainThread(); void PostReadbackToMainThread(); void PostAcquireLayerTextures(); void PostSetNeedsRedrawToMainThread(); @@ -136,6 +137,7 @@ class LayerTreeTest : public testing::Test, public TestHooks { virtual void DispatchAddAnimation(Layer* layer_to_receive_animation, double animation_duration); void DispatchSetNeedsCommit(); + void DispatchSetNeedsUpdateLayers(); void DispatchReadback(); void DispatchAcquireLayerTextures(); void DispatchSetNeedsRedraw(); diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index df82093..ac471c3 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc @@ -385,6 +385,8 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { sync_tree->SetLatencyInfo(latency_info_); latency_info_.Clear(); + sync_tree->PassSwapPromises(&swap_promise_list_); + host_impl->SetViewportSize(device_viewport_size_); host_impl->SetOverdrawBottomHeight(overdraw_bottom_height_); host_impl->SetDeviceScaleFactor(device_scale_factor_); @@ -1268,4 +1270,20 @@ bool LayerTreeHost::ScheduleMicroBenchmark( benchmark_name, value.Pass(), callback); } +void LayerTreeHost::QueueSwapPromise(scoped_ptr<SwapPromise> swap_promise) { + if (proxy_->HasImplThread()) { + DCHECK(proxy_->CommitRequested() || proxy_->BeginMainFrameRequested()); + } + DCHECK(swap_promise); + if (swap_promise_list_.size() > kMaxQueuedSwapPromiseNumber) + BreakSwapPromises(SwapPromise::SWAP_PROMISE_LIST_OVERFLOW); + swap_promise_list_.push_back(swap_promise.Pass()); +} + +void LayerTreeHost::BreakSwapPromises(SwapPromise::DidNotSwapReason reason) { + for (size_t i = 0; i < swap_promise_list_.size(); i++) + swap_promise_list_[i]->DidNotSwap(reason); + swap_promise_list_.clear(); +} + } // namespace cc diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index 54962fc..47bfa11 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -21,6 +21,7 @@ #include "cc/animation/animation_events.h" #include "cc/base/cc_export.h" #include "cc/base/scoped_ptr_vector.h" +#include "cc/base/swap_promise.h" #include "cc/debug/micro_benchmark.h" #include "cc/debug/micro_benchmark_controller.h" #include "cc/input/input_handler.h" @@ -286,6 +287,12 @@ class CC_EXPORT LayerTreeHost { scoped_ptr<base::Value> value, const MicroBenchmark::DoneCallback& callback); + // Call this function when you expect there to be a swap buffer. + // See swap_promise.h for how to use SwapPromise. + void QueueSwapPromise(scoped_ptr<SwapPromise> swap_promise); + + void BreakSwapPromises(SwapPromise::DidNotSwapReason reason); + protected: LayerTreeHost(LayerTreeHostClient* client, SharedBitmapManager* manager, @@ -433,6 +440,8 @@ class CC_EXPORT LayerTreeHost { SharedBitmapManager* shared_bitmap_manager_; + ScopedPtrVector<SwapPromise> swap_promise_list_; + DISALLOW_COPY_AND_ASSIGN(LayerTreeHost); }; diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 8a01b80..2faac42 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1420,10 +1420,13 @@ const RendererCapabilities& LayerTreeHostImpl::GetRendererCapabilities() const { } bool LayerTreeHostImpl::SwapBuffers(const LayerTreeHostImpl::FrameData& frame) { - if (frame.has_no_damage) + if (frame.has_no_damage) { + active_tree()->BreakSwapPromises(SwapPromise::SWAP_FAILS); return false; + } renderer_->SwapBuffers(); active_tree_->ClearLatencyInfo(); + active_tree()->FinishSwapPromises(); return true; } diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 82134e3..e7dfa97 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -9,6 +9,7 @@ #include "base/auto_reset.h" #include "base/synchronization/lock.h" #include "cc/animation/timing_function.h" +#include "cc/base/swap_promise.h" #include "cc/debug/frame_rate_counter.h" #include "cc/layers/content_layer.h" #include "cc/layers/content_layer_client.h" @@ -5215,4 +5216,124 @@ class LayerTreeHostTestSetMemoryPolicyOnLostOutputSurface SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostTestSetMemoryPolicyOnLostOutputSurface); +struct TestSwapPromiseResult { + TestSwapPromiseResult() : did_swap_called(false), + did_not_swap_called(false), + dtor_called(false), + reason(SwapPromise::DID_NOT_SWAP_UNKNOWN) { + } + + bool did_swap_called; + bool did_not_swap_called; + bool dtor_called; + SwapPromise::DidNotSwapReason reason; + base::Lock lock; +}; + +class TestSwapPromise : public SwapPromise { + public: + explicit TestSwapPromise(TestSwapPromiseResult* result) + : result_(result) { + } + + virtual ~TestSwapPromise() { + base::AutoLock lock(result_->lock); + result_->dtor_called = true; + } + + virtual void DidSwap() OVERRIDE { + base::AutoLock lock(result_->lock); + EXPECT_FALSE(result_->did_swap_called); + EXPECT_FALSE(result_->did_not_swap_called); + result_->did_swap_called = true; + } + + virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE { + base::AutoLock lock(result_->lock); + EXPECT_FALSE(result_->did_swap_called); + EXPECT_FALSE(result_->did_not_swap_called); + result_->did_not_swap_called = true; + result_->reason = reason; + } + + private: + // Not owned. + TestSwapPromiseResult* result_; +}; + +class LayerTreeHostTestBreakSwapPromise + : public LayerTreeHostTest { + protected: + LayerTreeHostTestBreakSwapPromise() + : commit_count_(0), commit_complete_count_(0) { + } + + virtual void WillBeginMainFrame() OVERRIDE { + ASSERT_LE(commit_count_, 2); + scoped_ptr<SwapPromise> swap_promise(new TestSwapPromise( + &swap_promise_result_[commit_count_])); + layer_tree_host()->QueueSwapPromise(swap_promise.Pass()); + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + commit_count_++; + if (commit_count_ == 2) { + // This commit will finish. + layer_tree_host()->SetNeedsCommit(); + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + commit_complete_count_++; + if (commit_complete_count_ == 1) { + // This commit will be aborted because no actual update. + PostSetNeedsUpdateLayersToMainThread(); + } else { + EndTest(); + } + } + + virtual void AfterTest() OVERRIDE { + // 3 commits are scheduled. 2 completes. 1 is aborted. + EXPECT_EQ(commit_count_, 3); + EXPECT_EQ(commit_complete_count_, 2); + + { + // The first commit completes and causes swap buffer which finishes + // the promise. + base::AutoLock lock(swap_promise_result_[0].lock); + EXPECT_TRUE(swap_promise_result_[0].did_swap_called); + EXPECT_FALSE(swap_promise_result_[0].did_not_swap_called); + EXPECT_TRUE(swap_promise_result_[0].dtor_called); + } + + { + // The second commit aborts. + base::AutoLock lock(swap_promise_result_[1].lock); + EXPECT_FALSE(swap_promise_result_[1].did_swap_called); + EXPECT_TRUE(swap_promise_result_[1].did_not_swap_called); + EXPECT_EQ(SwapPromise::COMMIT_FAILS, swap_promise_result_[1].reason); + EXPECT_TRUE(swap_promise_result_[1].dtor_called); + } + + { + // The last commit completes but it does not cause swap buffer because + // there is no damage in the frame data. + base::AutoLock lock(swap_promise_result_[2].lock); + EXPECT_FALSE(swap_promise_result_[2].did_swap_called); + EXPECT_TRUE(swap_promise_result_[2].did_not_swap_called); + EXPECT_EQ(SwapPromise::SWAP_FAILS, swap_promise_result_[2].reason); + EXPECT_TRUE(swap_promise_result_[2].dtor_called); + } + } + + int commit_count_; + int commit_complete_count_; + TestSwapPromiseResult swap_promise_result_[3]; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromise); + } // namespace cc diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 15bd750..ea3218d 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -122,6 +122,9 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->SetLatencyInfo(latency_info_); latency_info_.Clear(); + + target_tree->PassSwapPromises(&swap_promise_list_); + target_tree->SetPageScaleFactorAndLimits( page_scale_factor(), min_page_scale_factor(), max_page_scale_factor()); target_tree->SetPageScaleDelta( @@ -708,6 +711,32 @@ void LayerTreeImpl::ClearLatencyInfo() { latency_info_.Clear(); } +void LayerTreeImpl::QueueSwapPromise(scoped_ptr<SwapPromise> swap_promise) { + DCHECK(swap_promise); + if (swap_promise_list_.size() > kMaxQueuedSwapPromiseNumber) + BreakSwapPromises(SwapPromise::SWAP_PROMISE_LIST_OVERFLOW); + swap_promise_list_.push_back(swap_promise.Pass()); +} + +void LayerTreeImpl::PassSwapPromises( + ScopedPtrVector<SwapPromise>* new_swap_promise) { + swap_promise_list_.insert_and_take(swap_promise_list_.end(), + *new_swap_promise); + new_swap_promise->clear(); +} + +void LayerTreeImpl::FinishSwapPromises() { + for (size_t i = 0; i < swap_promise_list_.size(); i++) + swap_promise_list_[i]->DidSwap(); + swap_promise_list_.clear(); +} + +void LayerTreeImpl::BreakSwapPromises(SwapPromise::DidNotSwapReason reason) { + for (size_t i = 0; i < swap_promise_list_.size(); i++) + swap_promise_list_[i]->DidNotSwap(reason); + swap_promise_list_.clear(); +} + void LayerTreeImpl::DidModifyTilePriorities() { layer_tree_host_impl_->DidModifyTilePriorities(); } diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index 179b189..63d01ef 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -11,6 +11,8 @@ #include "base/containers/hash_tables.h" #include "base/values.h" +#include "cc/base/scoped_ptr_vector.h" +#include "cc/base/swap_promise.h" #include "cc/layers/layer_impl.h" #include "cc/resources/ui_resource_client.h" #include "ui/events/latency_info.h" @@ -209,6 +211,15 @@ class CC_EXPORT LayerTreeImpl { const ui::LatencyInfo& GetLatencyInfo(); void ClearLatencyInfo(); + // Call this function when you expect there to be a swap buffer. + // See swap_promise.h for how to use SwapPromise. + void QueueSwapPromise(scoped_ptr<SwapPromise> swap_promise); + + // Take the |new_swap_promise| and append it to |swap_promise_list_|. + void PassSwapPromises(ScopedPtrVector<SwapPromise>* new_swap_promise); + void FinishSwapPromises(); + void BreakSwapPromises(SwapPromise::DidNotSwapReason reason); + void DidModifyTilePriorities(); ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const; @@ -271,6 +282,8 @@ class CC_EXPORT LayerTreeImpl { ui::LatencyInfo latency_info_; + ScopedPtrVector<SwapPromise> swap_promise_list_; + UIResourceRequestQueue ui_resource_request_queue_; private: diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index 89a6773..c4b164c 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" +#include "cc/base/swap_promise.h" #include "cc/debug/benchmark_instrumentation.h" #include "cc/input/input_handler.h" #include "cc/output/context_provider.h" @@ -24,6 +25,8 @@ #include "cc/trees/layer_tree_impl.h" #include "ui/gfx/frame_time.h" +namespace { + // Measured in seconds. const double kSmoothnessTakesPriorityExpirationDelay = 0.25; @@ -32,6 +35,21 @@ const double kCommitAndActivationDurationEstimationPercentile = 50.0; const double kDrawDurationEstimationPercentile = 100.0; const int kDrawDurationEstimatePaddingInMicroseconds = 0; +class SwapPromiseChecker { + public: + explicit SwapPromiseChecker(cc::LayerTreeHost* layer_tree_host) + : layer_tree_host_(layer_tree_host) {} + + ~SwapPromiseChecker() { + layer_tree_host_->BreakSwapPromises(cc::SwapPromise::COMMIT_FAILS); + } + + private: + cc::LayerTreeHost* layer_tree_host_; +}; + +} // namespace + namespace cc { struct ThreadProxy::ReadbackRequest { @@ -754,6 +772,11 @@ void ThreadProxy::BeginMainFrame( return; } + // If the commit finishes, LayerTreeHost will transfer its swap promises to + // LayerTreeImpl. The destructor of SwapPromiseChecker checks LayerTressHost's + // swap promises. + SwapPromiseChecker swap_promise_checker(layer_tree_host()); + // Do not notify the impl thread of commit requests that occur during // the apply/animate/layout part of the BeginMainFrameAndCommit process since // those commit requests will get painted immediately. Once we have done |