diff options
Diffstat (limited to 'cc/base/synced_property.h')
-rw-r--r-- | cc/base/synced_property.h | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/cc/base/synced_property.h b/cc/base/synced_property.h new file mode 100644 index 0000000..5c47932 --- /dev/null +++ b/cc/base/synced_property.h @@ -0,0 +1,172 @@ +// Copyright 2014 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_SYNCED_PROPERTY_H_ +#define CC_BASE_SYNCED_PROPERTY_H_ + +#include "base/memory/ref_counted.h" + +namespace cc { + +// This class is the basic primitive used for impl-thread scrolling. Its job is +// to sanely resolve the case where both the main and impl thread are +// concurrently updating the same value (for example, when Javascript sets the +// scroll offset during an ongoing impl-side scroll). +// +// There are three trees (main, pending, and active) and therefore also three +// places with their own idea of the scroll offsets (and analogous properties +// like page scale). Objects of this class are meant to be held on the Impl +// side, and contain the canonical reference for the pending and active trees, +// as well as keeping track of the latest delta sent to the main thread (which +// is necessary for conflict resolution). + +template <typename T> +class SyncedProperty : public base::RefCounted<SyncedProperty<T>> { + public: + SyncedProperty() {} + + // Returns the canonical value for the specified tree, including the sum of + // all deltas. The pending tree should use this for activation purposes and + // the active tree should use this for drawing. + typename T::ValueType Current(bool is_active_tree) const { + if (is_active_tree) + return active_base_.Combine(active_delta_).get(); + else + return pending_base_.Combine(PendingDelta()).get(); + } + + // Sets the value on the impl thread, due to an impl-thread-originating + // action. Returns true if this had any effect. This will remain + // impl-thread-only information at first, and will get pulled back to the main + // thread on the next call of PullDeltaToMainThread (which happens right + // before the commit). + bool SetCurrent(typename T::ValueType current) { + T delta = T(current).InverseCombine(active_base_); + if (active_delta_.get() == delta.get()) + return false; + + active_delta_ = delta; + return true; + } + + // Returns the difference between the last value that was committed and + // activated from the main thread, and the current total value. + typename T::ValueType Delta() const { return active_delta_.get(); } + + // Returns the latest active tree delta and also makes a note that this value + // was sent to the main thread. + typename T::ValueType PullDeltaForMainThread() { + sent_delta_ = active_delta_; + return active_delta_.get(); + } + + // Push the latest value from the main thread onto pending tree-associated + // state. Returns true if this had any effect. + bool PushFromMainThread(typename T::ValueType main_thread_value) { + if (pending_base_.get() == main_thread_value) + return false; + + pending_base_ = T(main_thread_value); + + return true; + } + + // Push the value associated with the pending tree to be the active base + // value. As part of this, subtract the last sent value from the active tree + // delta (which will make the delta zero at steady state, or make it contain + // only the difference since the last send). + bool PushPendingToActive() { + if (active_base_.get() == pending_base_.get() && + sent_delta_.get() == T::Identity().get()) + return false; + + active_base_ = pending_base_; + active_delta_ = PendingDelta(); + sent_delta_ = T::Identity(); + + return true; + } + + // This simulates the consequences of the sent value getting committed and + // activated. The value sent to the main thread ends up combined with the + // active value, and the sent_delta is subtracted from the delta. + void AbortCommit() { + active_base_ = active_base_.Combine(sent_delta_); + active_delta_ = PendingDelta(); + sent_delta_ = T::Identity(); + } + + private: + // The new delta we would use if we decide to activate now. This delta + // excludes the amount that we expect the main thread to reflect back at the + // impl thread during the commit. + T PendingDelta() const { return active_delta_.InverseCombine(sent_delta_); } + + // Value last committed to the pending tree. + T pending_base_; + // Value last committed to the active tree (on the last activation). + T active_base_; + // The difference between the active_base_ and the user-perceived value. + T active_delta_; + // The value sent to the main thread (on the last BeginFrame); this is always + // identity outside of the BeginFrame-to-activation interval. + T sent_delta_; + + friend class base::RefCounted<SyncedProperty<T>>; + ~SyncedProperty() {} +}; + +// SyncedProperty's delta-based conflict resolution logic makes sense for any +// mathematical group. In practice, there are two that are useful: +// 1. Numbers/vectors with addition and identity = 0 (like scroll offsets) +// 2. Real numbers with multiplication and identity = 1 (like page scale) + +template <class V> +class AdditionGroup { + public: + typedef V ValueType; + + AdditionGroup() : value_(Identity().get()) {} + explicit AdditionGroup(V value) : value_(value) {} + + V& get() { return value_; } + const V& get() const { return value_; } + + static AdditionGroup<V> Identity() { return AdditionGroup(V()); } // zero + AdditionGroup<V> Combine(AdditionGroup<V> p) const { + return AdditionGroup<V>(value_ + p.value_); + } + AdditionGroup<V> InverseCombine(AdditionGroup<V> p) const { + return AdditionGroup<V>(value_ - p.value_); + } + + private: + V value_; +}; + +class ScaleGroup { + public: + typedef float ValueType; + + ScaleGroup() : value_(Identity().get()) {} + explicit ScaleGroup(float value) : value_(value) {} + + float& get() { return value_; } + const float& get() const { return value_; } + + static ScaleGroup Identity() { return ScaleGroup(1.f); } + ScaleGroup Combine(ScaleGroup p) const { + return ScaleGroup(value_ * p.value_); + } + ScaleGroup InverseCombine(ScaleGroup p) const { + return ScaleGroup(value_ / p.value_); + } + + private: + float value_; +}; + +} // namespace cc + +#endif // CC_BASE_SYNCED_PROPERTY_H_ |