summaryrefslogtreecommitdiffstats
path: root/cc/base/synced_property.h
blob: 5c4793282259c8266cf88c0f18bd05fc2d2fa331 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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_