Flutter Impeller
capture.h
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <functional>
8 #include <initializer_list>
9 #include <memory>
10 #include <string>
11 #include <type_traits>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <vector>
15 
16 #include "flutter/fml/logging.h"
17 #include "flutter/fml/macros.h"
21 #include "impeller/geometry/rect.h"
24 
25 namespace impeller {
26 
27 struct CaptureProcTable;
28 
29 #define _FOR_EACH_CAPTURE_PROPERTY(PROPERTY_V) \
30  PROPERTY_V(bool, Boolean, boolean) \
31  PROPERTY_V(int, Integer, integer) \
32  PROPERTY_V(Scalar, Scalar, scalar) \
33  PROPERTY_V(Point, Point, point) \
34  PROPERTY_V(Vector3, Vector3, vector3) \
35  PROPERTY_V(Rect, Rect, rect) \
36  PROPERTY_V(Color, Color, color) \
37  PROPERTY_V(Matrix, Matrix, matrix) \
38  PROPERTY_V(std::string, String, string)
39 
40 template <typename Type>
42  std::string label;
43 
44  explicit CaptureCursorListElement(const std::string& label) : label(label){};
45 
46  virtual ~CaptureCursorListElement() = default;
47 
48  //----------------------------------------------------------------------------
49  /// @brief Determines if previously captured data matches closely enough with
50  /// newly recorded data to safely emitted in its place. If this
51  /// returns `false`, then the remaining elements in the capture list
52  /// are discarded and re-recorded.
53  ///
54  /// This mechanism ensures that the UI of an interactive inspector can
55  /// never deviate from reality, even if the schema of the captured
56  /// data were to significantly deviate.
57  ///
58  virtual bool MatchesCloselyEnough(const Type& other) const = 0;
59 };
60 
61 #define _CAPTURE_TYPE(type_name, pascal_name, lower_name) k##pascal_name,
62 
63 #define _CAPTURE_PROPERTY_CAST_DECLARATION(type_name, pascal_name, lower_name) \
64  std::optional<type_name> As##pascal_name() const;
65 
66 /// A capturable property type
67 struct CaptureProperty : public CaptureCursorListElement<CaptureProperty> {
69 
70  struct Options {
71  struct Range {
74  };
75 
76  /// Readonly properties are always re-recorded during capture. Any edits
77  /// made to readonly values in-between captures are overwritten during the
78  /// next capture.
79  bool readonly = false;
80 
81  /// An inspector hint that can be used for displaying sliders. Only used for
82  /// numeric types. Rounded down for integer types.
83  std::optional<Range> range;
84  };
85 
87 
88  CaptureProperty(const std::string& label, Options options);
89 
90  virtual ~CaptureProperty();
91 
92  virtual Type GetType() const = 0;
93 
94  virtual void Invoke(const CaptureProcTable& proc_table) = 0;
95 
96  bool MatchesCloselyEnough(const CaptureProperty& other) const override;
97 
99 };
100 
101 #define _CAPTURE_PROPERTY_DECLARATION(type_name, pascal_name, lower_name) \
102  struct Capture##pascal_name##Property final : public CaptureProperty { \
103  type_name value; \
104  \
105  static std::shared_ptr<Capture##pascal_name##Property> \
106  Make(const std::string& label, type_name value, Options options); \
107  \
108  /* |CaptureProperty| */ \
109  Type GetType() const override; \
110  \
111  /* |CaptureProperty| */ \
112  void Invoke(const CaptureProcTable& proc_table) override; \
113  \
114  private: \
115  Capture##pascal_name##Property(const std::string& label, \
116  type_name value, \
117  Options options); \
118  \
119  FML_DISALLOW_COPY_AND_ASSIGN(Capture##pascal_name##Property); \
120  };
121 
123 
124 #define _CAPTURE_PROC(type_name, pascal_name, lower_name) \
125  std::function<void(Capture##pascal_name##Property&)> lower_name = \
126  [](Capture##pascal_name##Property& value) {};
127 
130 };
131 
132 template <typename Type>
134  public:
135  CapturePlaybackList() = default;
136 
138  // Force the list element type to inherit the CRTP type. We can't enforce
139  // this as a template requirement directly because `CaptureElement` has a
140  // recursive `CaptureCursorList<CaptureElement>` property, and so the
141  // compiler fails the check due to the type being incomplete.
142  static_assert(std::is_base_of_v<CaptureCursorListElement<Type>, Type>);
143  }
144 
145  void Rewind() { cursor_ = 0; }
146 
147  size_t Count() { return values_.size(); }
148 
149  std::shared_ptr<Type> GetNext(std::shared_ptr<Type> captured,
150  bool force_overwrite) {
151  if (cursor_ < values_.size()) {
152  std::shared_ptr<Type>& result = values_[cursor_];
153 
154  if (result->MatchesCloselyEnough(*captured)) {
155  if (force_overwrite) {
156  values_[cursor_] = captured;
157  }
158  // Safe playback is possible.
159  ++cursor_;
160  return result;
161  }
162  // The data has changed too much from the last capture to safely continue
163  // playback. Discard this and all subsequent elements to re-record.
164  values_.resize(cursor_);
165  }
166 
167  ++cursor_;
168  values_.push_back(captured);
169  return captured;
170  }
171 
172  std::shared_ptr<Type> FindFirstByLabel(const std::string& label) {
173  for (std::shared_ptr<Type>& value : values_) {
174  if (value->label == label) {
175  return value;
176  }
177  }
178  return nullptr;
179  }
180 
181  void Iterate(std::function<void(Type&)> iterator) const {
182  for (auto& value : values_) {
183  iterator(*value);
184  }
185  }
186 
187  private:
188  size_t cursor_ = 0;
189  std::vector<std::shared_ptr<Type>> values_;
190 
191  FML_DISALLOW_COPY_AND_ASSIGN(CapturePlaybackList);
192 };
193 
194 /// A document of capture data, containing a list of properties and a list
195 /// of subdocuments.
196 struct CaptureElement final : public CaptureCursorListElement<CaptureElement> {
199 
200  static std::shared_ptr<CaptureElement> Make(const std::string& label);
201 
202  void Rewind();
203 
204  bool MatchesCloselyEnough(const CaptureElement& other) const override;
205 
206  private:
207  explicit CaptureElement(const std::string& label);
208 
209  FML_DISALLOW_COPY_AND_ASSIGN(CaptureElement);
210 };
211 
212 #ifdef IMPELLER_ENABLE_CAPTURE
213 #define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, \
214  lower_name) \
215  type_name Add##pascal_name(const std::string& label, type_name value, \
216  CaptureProperty::Options options = {});
217 #else
218 #define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, \
219  lower_name) \
220  inline type_name Add##pascal_name(const std::string& label, type_name value, \
221  CaptureProperty::Options options = {}) { \
222  return value; \
223  }
224 #endif
225 
226 class Capture {
227  public:
228  explicit Capture(const std::string& label);
229 
230  Capture();
231 
232  static Capture MakeInactive();
233 
234  inline Capture CreateChild(const std::string& label) {
235 #ifdef IMPELLER_ENABLE_CAPTURE
236  if (!active_) {
237  return Capture();
238  }
239 
240  auto new_capture = Capture(label);
241  new_capture.element_ =
242  element_->children.GetNext(new_capture.element_, false);
243  new_capture.element_->Rewind();
244  return new_capture;
245 #else
246  return Capture();
247 #endif
248  }
249 
250  std::shared_ptr<CaptureElement> GetElement() const;
251 
252  void Rewind();
253 
255 
256  private:
257 #ifdef IMPELLER_ENABLE_CAPTURE
258  std::shared_ptr<CaptureElement> element_;
259  bool active_ = false;
260 #endif
261 };
262 
264  public:
265  CaptureContext();
266 
267  static CaptureContext MakeInactive();
268 
270  std::initializer_list<std::string> allowlist);
271 
272  bool IsActive() const;
273 
274  void Rewind();
275 
276  Capture GetDocument(const std::string& label);
277 
278  bool DoesDocumentExist(const std::string& label) const;
279 
280  private:
281  struct InactiveFlag {};
282  explicit CaptureContext(InactiveFlag);
283  CaptureContext(std::initializer_list<std::string> allowlist);
284 
285 #ifdef IMPELLER_ENABLE_CAPTURE
286  bool active_ = false;
287  std::optional<std::unordered_set<std::string>> allowlist_;
288  std::unordered_map<std::string, Capture> documents_;
289 #endif
290 };
291 
292 } // namespace impeller
impeller::CapturePlaybackList::GetNext
std::shared_ptr< Type > GetNext(std::shared_ptr< Type > captured, bool force_overwrite)
Definition: capture.h:149
impeller::CaptureCursorListElement::MatchesCloselyEnough
virtual bool MatchesCloselyEnough(const Type &other) const =0
Determines if previously captured data matches closely enough with newly recorded data to safely emit...
impeller::Capture::CreateChild
Capture CreateChild(const std::string &label)
Definition: capture.h:234
impeller::Capture
Definition: capture.h:226
point.h
impeller::CaptureProperty::Options::Range::min
Scalar min
Definition: capture.h:72
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::CapturePlaybackList
Definition: capture.h:133
impeller::CaptureProperty::options
Options options
Definition: capture.h:86
_CAPTURE_PROC
#define _CAPTURE_PROC(type_name, pascal_name, lower_name)
Definition: capture.h:124
impeller::CapturePlaybackList::Iterate
void Iterate(std::function< void(Type &)> iterator) const
Definition: capture.h:181
impeller::CaptureElement::Rewind
void Rewind()
Definition: capture.cc:77
impeller::CaptureProperty::Options::Range
Definition: capture.h:71
impeller::Capture::MakeInactive
static Capture MakeInactive()
Definition: capture.cc:101
impeller::CapturePlaybackList::Count
size_t Count()
Definition: capture.h:147
impeller::CapturePlaybackList::FindFirstByLabel
std::shared_ptr< Type > FindFirstByLabel(const std::string &label)
Definition: capture.h:172
matrix.h
impeller::CaptureProperty::Options::Range::max
Scalar max
Definition: capture.h:73
impeller::Capture::GetElement
std::shared_ptr< CaptureElement > GetElement() const
Definition: capture.cc:105
impeller::CaptureCursorListElement::CaptureCursorListElement
CaptureCursorListElement(const std::string &label)
Definition: capture.h:44
impeller::CaptureContext::MakeAllowlist
static CaptureContext MakeAllowlist(std::initializer_list< std::string > allowlist)
Definition: capture.cc:159
impeller::CaptureElement::MatchesCloselyEnough
bool MatchesCloselyEnough(const CaptureElement &other) const override
Determines if previously captured data matches closely enough with newly recorded data to safely emit...
Definition: capture.cc:82
impeller::CaptureProperty::Options::range
std::optional< Range > range
Definition: capture.h:83
impeller::CaptureCursorListElement::label
std::string label
Definition: capture.h:42
impeller::CaptureContext
Definition: capture.h:263
impeller::CaptureProperty::Invoke
virtual void Invoke(const CaptureProcTable &proc_table)=0
impeller::CaptureCursorListElement::~CaptureCursorListElement
virtual ~CaptureCursorListElement()=default
impeller::CaptureContext::DoesDocumentExist
bool DoesDocumentExist(const std::string &label) const
Definition: capture.cc:209
impeller::CaptureProperty::Options::readonly
bool readonly
Definition: capture.h:79
impeller::CapturePlaybackList::Rewind
void Rewind()
Definition: capture.h:145
impeller::CaptureContext::Rewind
void Rewind()
Definition: capture.cc:172
impeller::_FOR_EACH_CAPTURE_PROPERTY
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_CAST_DEFINITION)
impeller::CaptureElement::Make
static std::shared_ptr< CaptureElement > Make(const std::string &label)
Definition: capture.cc:73
_CAPTURE_TYPE
#define _CAPTURE_TYPE(type_name, pascal_name, lower_name)
Definition: capture.h:61
impeller::CapturePlaybackList::CapturePlaybackList
CapturePlaybackList()=default
impeller::CaptureElement::children
CapturePlaybackList< CaptureElement > children
Definition: capture.h:198
impeller::CaptureContext::MakeInactive
static CaptureContext MakeInactive()
Definition: capture.cc:155
impeller::CaptureElement
Definition: capture.h:196
impeller::CaptureProperty::MatchesCloselyEnough
bool MatchesCloselyEnough(const CaptureProperty &other) const override
Determines if previously captured data matches closely enough with newly recorded data to safely emit...
Definition: capture.cc:21
impeller::Capture::Rewind
void Rewind()
Definition: capture.cc:113
scalar.h
impeller::CaptureProperty::CaptureProperty
CaptureProperty(const std::string &label, Options options)
Definition: capture.cc:16
_FOR_EACH_CAPTURE_PROPERTY
#define _FOR_EACH_CAPTURE_PROPERTY(PROPERTY_V)
Definition: capture.h:29
impeller::CaptureProperty
A capturable property type.
Definition: capture.h:67
impeller::CaptureProperty::Type
Type
Definition: capture.h:68
_CAPTURE_PROPERTY_DECLARATION
#define _CAPTURE_PROPERTY_DECLARATION(type_name, pascal_name, lower_name)
Definition: capture.h:101
vector.h
impeller::CaptureProperty::~CaptureProperty
virtual ~CaptureProperty()
rect.h
impeller::CaptureContext::IsActive
bool IsActive() const
Definition: capture.cc:164
impeller::CaptureProcTable
Definition: capture.h:128
impeller::CaptureContext::CaptureContext
CaptureContext()
Definition: capture.cc:149
impeller::CaptureProperty::Type::_FOR_EACH_CAPTURE_PROPERTY
@ _FOR_EACH_CAPTURE_PROPERTY
impeller::CaptureProperty::Options
Definition: capture.h:70
color.h
impeller::CaptureCursorListElement
Definition: capture.h:41
impeller::Capture::Capture
Capture()
impeller::CapturePlaybackList::~CapturePlaybackList
~CapturePlaybackList()
Definition: capture.h:137
impeller::CaptureContext::GetDocument
Capture GetDocument(const std::string &label)
Definition: capture.cc:182
_CAPTURE_PROPERTY_CAST_DECLARATION
#define _CAPTURE_PROPERTY_CAST_DECLARATION(type_name, pascal_name, lower_name)
Definition: capture.h:63
_CAPTURE_PROPERTY_RECORDER_DECLARATION
#define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, lower_name)
Definition: capture.h:218
impeller
Definition: aiks_context.cc:10
impeller::CaptureElement::properties
CapturePlaybackList< CaptureProperty > properties
Definition: capture.h:197
impeller::CaptureProperty::GetType
virtual Type GetType() const =0