summaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authormiletus@chromium.org <miletus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-27 00:05:19 +0000
committermiletus@chromium.org <miletus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-27 00:05:19 +0000
commitb69c1dbe6e7252b01f2fc06fb24906c96efce821 (patch)
treed633eb4d16ead3f91b08ef8f608b7175d8a43daa /cc
parenta405e37563185523f301deb92436a2db09658c91 (diff)
downloadchromium_src-b69c1dbe6e7252b01f2fc06fb24906c96efce821.zip
chromium_src-b69c1dbe6e7252b01f2fc06fb24906c96efce821.tar.gz
chromium_src-b69c1dbe6e7252b01f2fc06fb24906c96efce821.tar.bz2
Add SwapPromise support to LayerTreeHost and LayerTreeImpl
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, e.g. commit fails on main thread, new frame data has no actual damage so LayerTreeHostImpl::SwapBuffers() bails out early, then Promise::DidNotSwap() will be called. BUG=246034, 271583 TEST=unittests pass. Review URL: https://codereview.chromium.org/60513007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237444 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
-rw-r--r--cc/base/swap_promise.h48
-rw-r--r--cc/cc.gyp1
-rw-r--r--cc/test/layer_tree_test.cc14
-rw-r--r--cc/test/layer_tree_test.h2
-rw-r--r--cc/trees/layer_tree_host.cc18
-rw-r--r--cc/trees/layer_tree_host.h9
-rw-r--r--cc/trees/layer_tree_host_impl.cc5
-rw-r--r--cc/trees/layer_tree_host_unittest.cc121
-rw-r--r--cc/trees/layer_tree_impl.cc29
-rw-r--r--cc/trees/layer_tree_impl.h13
-rw-r--r--cc/trees/thread_proxy.cc23
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_
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 36251c6..3914f41 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -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