summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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