File: synced_property.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (222 lines) | stat: -rw-r--r-- 9,792 bytes parent folder | download | duplicates (10)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2014 The Chromium Authors
// 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:
  using BaseT = typename T::BaseType;
  using DeltaT = typename T::DeltaType;

  // 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.
  BaseT Current(bool is_active_tree) const {
    if (is_active_tree)
      return T::ApplyDelta(active_base_, active_delta_);
    return T::ApplyDelta(pending_base_, PendingDelta());
  }

  // 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 PullDeltaForMainThread.
  bool SetCurrent(BaseT current) {
    DeltaT delta = T::DeltaBetweenBases(current, active_base_);
    if (active_delta_ == delta)
      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.
  DeltaT Delta() const { return active_delta_; }

  // Returns the latest active tree delta and also makes a note that this value
  // was sent to the main thread.
  DeltaT PullDeltaForMainThread(bool next_bmf) {
    DeltaT& target = next_bmf ? next_reflected_delta_in_main_tree_
                              : reflected_delta_in_main_tree_;
    DCHECK_EQ(target, T::IdentityDelta());
    target = UnsentDelta();
    return target;
  }

  // Push the latest value from the main thread onto pending tree-associated
  // state. Returns true if pushing the value results in different values
  // between the main layer tree and the pending tree.
  bool PushMainToPending(BaseT main_thread_value) {
    reflected_delta_in_pending_tree_ = reflected_delta_in_main_tree_;
    reflected_delta_in_main_tree_ = next_reflected_delta_in_main_tree_;
    next_reflected_delta_in_main_tree_ = T::IdentityDelta();
    pending_base_ = main_thread_value;

    return Current(false) != main_thread_value;
  }

  // Push the value associated with the pending tree to be the active base
  // value. As part of this, subtract the delta reflected in the pending tree
  // 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).
  // Returns true if pushing the update results in:
  // 1) Different values on the pending tree and the active tree.
  // 2) An update to the current value on the active tree.
  // The reason for considering the second case only when pushing to the active
  // tree, as opposed to when pushing to the pending tree, is that only the
  // active tree computes state using this value which is not computed on the
  // pending tree and not pushed during activation (aka scrollbar geometries).
  bool PushPendingToActive() {
    BaseT pending_value_before_push = Current(false);
    BaseT active_value_before_push = Current(true);

    active_base_ = pending_base_;
    active_delta_ = PendingDelta();
    reflected_delta_in_pending_tree_ = T::IdentityDelta();
    clobber_active_value_ = false;

    BaseT current_active_value = Current(true);
    return pending_value_before_push != current_active_value ||
           active_value_before_push != current_active_value;
  }

  void AbortCommit(bool next_bmf, bool main_frame_applied_deltas) {
    // Finish processing the delta that was sent to the main thread, and reset
    // the corresponding the delta_in_main_tree_ variable. If
    // main_frame_applied_deltas is true, we send the delta on to the active
    // tree just as would happen for a successful commit. Otherwise, we treat
    // the delta as never having been sent to the main thread and just drop it.
    if (next_bmf) {
      // The previous main frame has not yet run commit; the aborted main frame
      // corresponds to the delta in the "next" slot (if any).  In this case, if
      // the main thread processed the delta from this aborted commit we can
      // simply add the delta to reflected_delta_in_main_tree_.
      if (main_frame_applied_deltas) {
        reflected_delta_in_main_tree_ = T::CombineDeltas(
            reflected_delta_in_main_tree_, next_reflected_delta_in_main_tree_);
      }
      next_reflected_delta_in_main_tree_ = T::IdentityDelta();
    } else {
      // There is no "next" main frame, this abort was for the primary.
      if (main_frame_applied_deltas) {
        DeltaT delta = reflected_delta_in_main_tree_;
        // This simulates the consequences of the sent value getting committed
        // and activated.
        pending_base_ = T::ApplyDelta(pending_base_, delta);
        active_base_ = T::ApplyDelta(active_base_, delta);
        active_delta_ = T::DeltaBetweenDeltas(active_delta_, delta);
      }
      reflected_delta_in_main_tree_ = T::IdentityDelta();
    }
  }

  // Values sent to the main thread and not yet resolved in the pending or
  // active tree.
  DeltaT reflected_delta_in_main_tree() const {
    return reflected_delta_in_main_tree_;
  }
  DeltaT next_reflected_delta_in_main_tree() const {
    return next_reflected_delta_in_main_tree_;
  }
  // Values as last pushed to the pending or active tree respectively, with no
  // impl-thread delta applied.
  BaseT PendingBase() const { return pending_base_; }
  BaseT ActiveBase() const { return active_base_; }

  // The new delta we would use if we decide to activate now.  This delta
  // excludes the amount that we know is reflected in the pending tree.
  DeltaT PendingDelta() const {
    if (clobber_active_value_)
      return T::IdentityDelta();

    return T::DeltaBetweenDeltas(active_delta_,
                                 reflected_delta_in_pending_tree_);
  }

  DeltaT UnsentDelta() const {
    return T::DeltaBetweenDeltas(PendingDelta(), reflected_delta_in_main_tree_);
  }

  void set_clobber_active_value() { clobber_active_value_ = true; }

 private:
  friend class base::RefCounted<SyncedProperty<T>>;
  ~SyncedProperty() = default;

  // Value last committed to the pending tree.
  BaseT pending_base_ = T::IdentityBase();
  // Value last committed to the active tree on the last activation.
  BaseT active_base_ = T::IdentityBase();
  // The difference between |active_base_| and the user-perceived value.
  DeltaT active_delta_ = T::IdentityDelta();
  // A value sent to the main thread on a BeginMainFrame, but not yet applied to
  // the resulting pending tree.
  DeltaT reflected_delta_in_main_tree_ = T::IdentityDelta();
  // A value sent to the main thread on a BeginMainFrame at a time when
  // the previous BeginMainFrame is in the ready-to-commit state.
  DeltaT next_reflected_delta_in_main_tree_ = T::IdentityDelta();
  // The value that was sent to the main thread for BeginMainFrame for the
  // current pending tree. This is always identity outside of the
  // BeginMainFrame to activation interval.
  DeltaT reflected_delta_in_pending_tree_ = T::IdentityDelta();
  // When true the pending delta is always identity so that it does not change
  // and will clobber the active value on push.
  bool clobber_active_value_ = false;
};

// SyncedProperty's delta-based conflict resolution logic makes sense for any
// mathematical group.  In practice, there are two that are useful:
// 1. Numbers/classes with addition and subtraction operations, and
//    identity = constructor() (like gfx::Vector2dF for scroll offset and
//    scroll delta)
// 2. Real numbers with multiplication and division operations, and
//    identity = 1 (like page scale)

template <typename BaseT, typename DeltaT = BaseT>
class AdditionGroup {
 public:
  using BaseType = BaseT;
  using DeltaType = DeltaT;
  static constexpr BaseT IdentityBase() { return BaseT(); }
  static constexpr DeltaT IdentityDelta() { return DeltaT(); }
  static BaseT ApplyDelta(BaseT v, DeltaT delta) { return v + delta; }
  static DeltaT DeltaBetweenBases(BaseT v1, BaseT v2) { return v1 - v2; }
  static DeltaT DeltaBetweenDeltas(DeltaT d1, DeltaT d2) { return d1 - d2; }
  static DeltaT CombineDeltas(DeltaT d1, DeltaT d2) { return d1 + d2; }
};

class ScaleGroup {
 public:
  using BaseType = float;
  using DeltaType = float;
  static constexpr float IdentityBase() { return 1.f; }
  static constexpr float IdentityDelta() { return 1.f; }
  static float ApplyDelta(float v, float delta) { return v * delta; }
  static float DeltaBetweenBases(float v1, float v2) { return v1 / v2; }
  static float DeltaBetweenDeltas(float d1, float d2) { return d1 / d2; }
  static float CombineDeltas(float d1, float d2) { return d1 * d2; }
};

}  // namespace cc

#endif  // CC_BASE_SYNCED_PROPERTY_H_