Flutter Engine
The Flutter Engine
layer_state_stack.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#ifndef FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
6#define FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
7
8#include "flutter/display_list/dl_canvas.h"
9#include "flutter/flow/embedded_views.h"
10#include "flutter/flow/paint_utils.h"
11
12namespace flutter {
13
14/// The LayerStateStack manages the inherited state passed down between
15/// |Layer| objects in a |LayerTree| during |Preroll| and |Paint|.
16///
17/// More specifically, it manages the clip and transform state during
18/// recursive rendering and will hold and lazily apply opacity, ImageFilter
19/// and ColorFilter attributes to recursive content. This is not a truly
20/// general state management mechnanism as it makes assumptions that code
21/// will be applying the attributes to rendered content that happens in
22/// recursive calls. The automatic save/restore mechanisms only work in
23/// a context where C++ auto-destruct calls will engage the restore at
24/// the end of a code block and that any applied attributes will only
25/// be applied to the content rendered inside that block. These restrictions
26/// match the organization of the |LayerTree| methods precisely.
27///
28/// The stack can manage a single state delegate. The delegate will provide
29/// tracking of the current transform and clip and will also execute
30/// saveLayer calls at the appropriate time if it is a rendering delegate.
31/// The delegate can be swapped out on the fly (as is typically done by
32/// PlatformViewLayer when recording the state for multiple "overlay"
33/// layers that occur between embedded view subtrees. The old delegate
34/// will be restored to its original state before it became a delegate
35/// and the new delegate will have all of the state recorded by the stack
36/// replayed into it to bring it up to speed with the current rendering
37/// context.
38///
39/// The delegate can be any one of:
40/// - Preroll delegate: used during Preroll to remember the outstanding
41/// state for embedded platform layers
42/// - DlCanvas: used during Paint for rendering output
43/// The stack will know which state needs to be conveyed to any of these
44/// delegates and when is the best time to convey that state (i.e. lazy
45/// saveLayer calls for example).
46///
47/// The rendering state attributes will be automatically applied to the
48/// nested content using a |saveLayer| call at the point at which we
49/// encounter rendered content (i.e. various nested layers that exist only
50/// to apply new state will not trigger the |saveLayer| and the attributes
51/// can accumulate until we reach actual content that is rendered.) Some
52/// rendered content can avoid the |saveLayer| if it reports to the object
53/// that it is able to apply all of the attributes that happen to be
54/// outstanding (accumulated from parent state-modifiers). A |ContainerLayer|
55/// can also monitor the attribute rendering capabilities of a list of
56/// children and can ask the object to apply a protective |saveLayer| or
57/// not based on the negotiated capabilities of the entire group.
58///
59/// Any code that is planning to modify the clip, transform, or rendering
60/// attributes for its child content must start by calling the |save| method
61/// which returns a MutatorContext object. The methods that modify such
62/// state only exist on the MutatorContext object so it is difficult to get
63/// that wrong, but the caller must make sure that the call happens within
64/// a C++ code block that will define the "rendering scope" of those
65/// state changes as they will be automatically restored on exit from that
66/// block. Note that the layer might make similar state calls directly on
67/// the canvas or builder during the Paint cycle (via saveLayer, transform,
68/// or clip calls), but should avoid doing so if there is any nested content
69/// that needs to track or react to those state calls.
70///
71/// Code that needs to render content can simply inform the parent of their
72/// abilities by setting the |PrerollContext::renderable_state_flags| during
73/// |Preroll| and then render with those attributes during |Paint| by
74/// requesting the outstanding values of those attributes from the state_stack
75/// object. Individual leaf layers can ignore this feature as the default
76/// behavior during |Preroll| will have their parent |ContainerLayer| assume
77/// that they cannot render any outstanding state attributes and will apply
78/// the protective saveLayer on their behalf if needed. As such, this object
79/// only provides "opt-in" features for leaf layers and no responsibilities
80/// otherwise.
81/// See |LayerStateStack::fill|
82/// See |LayerStateStack::outstanding_opacity|
83/// See |LayerStateStack::outstanding_color_filter|
84/// See |LayerStateStack::outstanding_image_filter|
85///
86/// State-modifying layers should contain code similar to this pattern in both
87/// their |Preroll| and |Paint| methods.
88///
89/// void [LayerType]::[Preroll/Paint](context) {
90/// auto mutator = context.state_stack.save();
91/// mutator.translate(origin.x, origin.y);
92/// mutator.applyOpacity(content_bounds, opacity_value);
93/// mutator.applyColorFilter(content_bounds, color_filter);
94/// // or any of the mutator transform, clip or attribute methods
95///
96/// // Children will react to the state applied above during their
97/// // Preroll/Paint methods or ContainerLayer will protect them
98/// // conservatively by default.
99/// [Preroll/Paint]Children(context);
100///
101/// // here the mutator will be auto-destructed and the state accumulated
102/// // by it will be restored out of the state_stack and its associated
103/// // delegates.
104/// }
106 public:
108
109 // Clears out any old delegate to make room for a new one.
110 void clear_delegate();
111
112 // Return the DlCanvas delegate if the state stack has such a delegate.
113 // The state stack will only have one delegate at a time holding either
114 // a DlCanvas or a preroll accumulator.
115 DlCanvas* canvas_delegate() { return delegate_->canvas(); }
116
117 // Clears the old delegate and sets the canvas delegate to the indicated
118 // DL canvas (if not nullptr). This ensures that only one delegate - either
119 // a DlCanvas or a preroll accumulator - is present at any one time.
120 void set_delegate(DlCanvas* canvas);
121
122 // Clears the old delegate and sets the state stack up to accumulate
123 // clip and transform information for a Preroll phase. This ensures
124 // that only one delegate - either a DlCanvas or a preroll accumulator -
125 // is present at any one time.
126 void set_preroll_delegate(const SkRect& cull_rect, const SkMatrix& matrix);
127 void set_preroll_delegate(const SkRect& cull_rect);
129
130 // Fills the supplied MatatorsStack object with the mutations recorded
131 // by this LayerStateStack in the order encountered.
132 void fill(MutatorsStack* mutators);
133
135 public:
137 layer_state_stack_->restore_to_count(stack_restore_count_);
138 }
139
140 private:
141 AutoRestore(LayerStateStack* stack, const SkRect& bounds, int flags)
142 : layer_state_stack_(stack),
143 stack_restore_count_(stack->stack_count()) {
144 if (stack->needs_save_layer(flags)) {
145 stack->save_layer(bounds);
146 }
147 }
148 friend class LayerStateStack;
149
150 LayerStateStack* layer_state_stack_;
151 const size_t stack_restore_count_;
152
154 };
155
157 public:
159 layer_state_stack_->restore_to_count(stack_restore_count_);
160 }
161
162 // Immediately executes a saveLayer with all accumulated state
163 // onto the canvas or builder to be applied at the next matching
164 // restore. A saveLayer is always executed by this method even if
165 // there are no outstanding attributes.
166 void saveLayer(const SkRect& bounds);
167
168 // Records the opacity for application at the next call to
169 // saveLayer or applyState. A saveLayer may be executed at
170 // this time if the opacity cannot be batched with other
171 // outstanding attributes.
172 void applyOpacity(const SkRect& bounds, SkScalar opacity);
173
174 // Records the image filter for application at the next call to
175 // saveLayer or applyState. A saveLayer may be executed at
176 // this time if the image filter cannot be batched with other
177 // outstanding attributes.
178 // (Currently only opacity is recorded for batching)
179 void applyImageFilter(const SkRect& bounds,
180 const std::shared_ptr<const DlImageFilter>& filter);
181
182 // Records the color filter for application at the next call to
183 // saveLayer or applyState. A saveLayer may be executed at
184 // this time if the color filter cannot be batched with other
185 // outstanding attributes.
186 // (Currently only opacity is recorded for batching)
187 void applyColorFilter(const SkRect& bounds,
188 const std::shared_ptr<const DlColorFilter>& filter);
189
190 // Saves the state stack and immediately executes a saveLayer
191 // with the indicated backdrop filter and any outstanding
192 // state attributes. Since the backdrop filter only applies
193 // to the pixels alrady on the screen when this call is made,
194 // the backdrop filter will only be applied to the canvas or
195 // builder installed at the time that this call is made, and
196 // subsequent canvas or builder objects that are made delegates
197 // will only see a saveLayer with the indicated blend_mode.
199 const std::shared_ptr<const DlImageFilter>& filter,
200 DlBlendMode blend_mode);
201
202 void translate(SkScalar tx, SkScalar ty);
203 void translate(SkPoint tp) { translate(tp.fX, tp.fY); }
204 void transform(const SkM44& m44);
205 void transform(const SkMatrix& matrix);
206 void integralTransform();
207
208 void clipRect(const SkRect& rect, bool is_aa);
209 void clipRRect(const SkRRect& rrect, bool is_aa);
210 void clipPath(const SkPath& path, bool is_aa);
211
212 private:
213 explicit MutatorContext(LayerStateStack* stack)
214 : layer_state_stack_(stack),
215 stack_restore_count_(stack->stack_count()) {}
216 friend class LayerStateStack;
217
218 LayerStateStack* layer_state_stack_;
219 const size_t stack_restore_count_;
220 bool save_needed_ = true;
221
223 };
224
225 static constexpr int kCallerCanApplyOpacity = 0x1;
226 static constexpr int kCallerCanApplyColorFilter = 0x2;
227 static constexpr int kCallerCanApplyImageFilter = 0x4;
228 static constexpr int kCallerCanApplyAnything =
231
232 // Apply the outstanding state via saveLayer if necessary,
233 // respecting the flags representing which potentially
234 // outstanding attributes the calling layer can apply
235 // themselves.
236 //
237 // A saveLayer may or may not be sent to the delegates depending
238 // on how the outstanding state intersects with the flags supplied
239 // by the caller.
240 //
241 // An AutoRestore instance will always be returned even if there
242 // was no saveLayer applied.
243 [[nodiscard]] inline AutoRestore applyState(const SkRect& bounds,
244 int can_apply_flags) {
245 return AutoRestore(this, bounds, can_apply_flags);
246 }
247
248 SkScalar outstanding_opacity() const { return outstanding_.opacity; }
249
250 std::shared_ptr<const DlColorFilter> outstanding_color_filter() const {
251 return outstanding_.color_filter;
252 }
253
254 std::shared_ptr<const DlImageFilter> outstanding_image_filter() const {
255 return outstanding_.image_filter;
256 }
257
258 // The outstanding bounds are the bounds recorded during the last
259 // attribute applied to this state stack. The assumption is that
260 // the nested calls to the state stack will each supply bounds relative
261 // to the content of that single attribute and the bounds of the content
262 // of any outstanding attributes will include the output bounds of
263 // applying any nested attributes. Thus, only the innermost content
264 // bounds received will be sufficient to apply all outstanding attributes.
265 SkRect outstanding_bounds() const { return outstanding_.save_layer_bounds; }
266
267 // Fill the provided paint object with any oustanding attributes and
268 // return a pointer to it, or return a nullptr if there were no
269 // outstanding attributes to paint with.
270 DlPaint* fill(DlPaint& paint) const { return outstanding_.fill(paint); }
271
272 // The cull_rect (not the exact clip) relative to the device pixels.
273 // This rectangle may be a conservative estimate of the true clip region.
274 SkRect device_cull_rect() const { return delegate_->device_cull_rect(); }
275
276 // The cull_rect (not the exact clip) relative to the local coordinates.
277 // This rectangle may be a conservative estimate of the true clip region.
278 SkRect local_cull_rect() const { return delegate_->local_cull_rect(); }
279
280 // The transform from the local coordinates to the device coordinates
281 // in the most capable 4x4 matrix representation. This matrix may be
282 // more information than is needed to compute bounds for a 2D rendering
283 // primitive, but it will accurately concatenate with other 4x4 matrices
284 // without losing information.
285 SkM44 transform_4x4() const { return delegate_->matrix_4x4(); }
286
287 // The transform from the local coordinates to the device coordinates
288 // in a more compact 3x3 matrix represenation that provides enough
289 // information to accurately transform 2D primitives into their
290 // resulting 2D bounds. This matrix also has enough information to
291 // concat with other 2D affine transforms, but does not carry enough
292 // information to accurately concat with fully perspective matrics.
293 SkMatrix transform_3x3() const { return delegate_->matrix_3x3(); }
294
295 // Tests if painting content with the current outstanding attributes
296 // will produce any content. This method does not check the current
297 // transform or clip for being singular or empty.
298 // See |content_culled|
299 bool painting_is_nop() const { return outstanding_.opacity <= 0; }
300
301 // Tests if painting content with the given bounds will produce any output.
302 // This method does not check the outstanding attributes to verify that
303 // they produce visible results.
304 // See |painting_is_nop|
305 bool content_culled(const SkRect& content_bounds) const {
306 return delegate_->content_culled(content_bounds);
307 }
308
309 // Saves the current state of the state stack and returns a
310 // MutatorContext which can be used to manipulate the state.
311 // The state stack will be restored to its current state
312 // when the MutatorContext object goes out of scope.
313 [[nodiscard]] inline MutatorContext save() { return MutatorContext(this); }
314
315 // Returns true if the state stack is in, or has returned to,
316 // its initial state.
317 bool is_empty() const { return state_stack_.empty(); }
318
319 private:
320 size_t stack_count() const { return state_stack_.size(); }
321 void restore_to_count(size_t restore_count);
322 void reapply_all();
323
324 void apply_last_entry() { state_stack_.back()->apply(this); }
325
326 // The push methods simply push an associated StateEntry on the stack
327 // and then apply it to the current canvas and builder.
328 // ---------------------
329 // void push_attributes();
330 void push_opacity(const SkRect& rect, SkScalar opacity);
331 void push_color_filter(const SkRect& bounds,
332 const std::shared_ptr<const DlColorFilter>& filter);
333 void push_image_filter(const SkRect& bounds,
334 const std::shared_ptr<const DlImageFilter>& filter);
335 void push_backdrop(const SkRect& bounds,
336 const std::shared_ptr<const DlImageFilter>& filter,
337 DlBlendMode blend_mode);
338
339 void push_translate(SkScalar tx, SkScalar ty);
340 void push_transform(const SkM44& matrix);
341 void push_transform(const SkMatrix& matrix);
342 void push_integral_transform();
343
344 void push_clip_rect(const SkRect& rect, bool is_aa);
345 void push_clip_rrect(const SkRRect& rrect, bool is_aa);
346 void push_clip_path(const SkPath& path, bool is_aa);
347 // ---------------------
348
349 // The maybe/needs_save_layer methods will determine if the indicated
350 // attribute can be incorporated into the outstanding attributes as is,
351 // or if the apply_flags are compatible with the outstanding attributes.
352 // If the oustanding attributes are incompatible with the new attribute
353 // or the apply flags, then a protective saveLayer will be executed.
354 // ---------------------
355 bool needs_save_layer(int flags) const;
356 void do_save();
357 void save_layer(const SkRect& bounds);
358 void maybe_save_layer_for_transform(bool needs_save);
359 void maybe_save_layer_for_clip(bool needs_save);
360 void maybe_save_layer(int apply_flags);
361 void maybe_save_layer(SkScalar opacity);
362 void maybe_save_layer(const std::shared_ptr<const DlColorFilter>& filter);
363 void maybe_save_layer(const std::shared_ptr<const DlImageFilter>& filter);
364 // ---------------------
365
366 struct RenderingAttributes {
367 // We need to record the last bounds we received for the last
368 // attribute that we recorded so that we can perform a saveLayer
369 // on the proper area. When an attribute is applied that cannot
370 // be merged with the existing attributes, it will be submitted
371 // with a bounds for its own source content, not the bounds for
372 // the content that will be included in the saveLayer that applies
373 // the existing outstanding attributes - thus we need to record
374 // the bounds that were supplied with the most recent previous
375 // attribute to be applied.
376 SkRect save_layer_bounds{0, 0, 0, 0};
377
378 SkScalar opacity = SK_Scalar1;
379 std::shared_ptr<const DlColorFilter> color_filter;
380 std::shared_ptr<const DlImageFilter> image_filter;
381
384
385 bool operator==(const RenderingAttributes& other) const {
386 return save_layer_bounds == other.save_layer_bounds &&
387 opacity == other.opacity &&
388 Equals(color_filter, other.color_filter) &&
389 Equals(image_filter, other.image_filter);
390 }
391 };
392
393 class StateEntry {
394 public:
395 virtual ~StateEntry() = default;
396
397 virtual void apply(LayerStateStack* stack) const = 0;
398 virtual void reapply(LayerStateStack* stack) const { apply(stack); }
399 virtual void restore(LayerStateStack* stack) const {}
400 virtual void update_mutators(MutatorsStack* mutators_stack) const {}
401
402 protected:
403 StateEntry() = default;
404
406 };
407 friend class SaveEntry;
408 friend class SaveLayerEntry;
410 friend class OpacityEntry;
411 friend class ImageFilterEntry;
412 friend class ColorFilterEntry;
413 friend class TranslateEntry;
415 friend class TransformM44Entry;
417 friend class ClipEntry;
418 friend class ClipRectEntry;
419 friend class ClipRRectEntry;
420 friend class ClipPathEntry;
421
422 class Delegate {
423 protected:
424 using ClipOp = DlCanvas::ClipOp;
425
426 public:
427 virtual ~Delegate() = default;
428
429 // Mormally when a |Paint| or |Preroll| cycle is completed, the
430 // delegate will have been rewound to its initial state by the
431 // trailing recursive actions of the paint and preroll methods.
432 // When a delegate is swapped out, there may be unresolved state
433 // that the delegate received. This method is called when the
434 // delegate is cleared or swapped out to inform it to rewind its
435 // state and finalize all outstanding save or saveLayer operations.
436 virtual void decommission() = 0;
437
438 virtual DlCanvas* canvas() const { return nullptr; }
439
440 virtual SkRect local_cull_rect() const = 0;
441 virtual SkRect device_cull_rect() const = 0;
442 virtual SkM44 matrix_4x4() const = 0;
443 virtual SkMatrix matrix_3x3() const = 0;
444 virtual bool content_culled(const SkRect& content_bounds) const = 0;
445
446 virtual void save() = 0;
447 virtual void saveLayer(const SkRect& bounds,
448 RenderingAttributes& attributes,
450 const DlImageFilter* backdrop) = 0;
451 virtual void restore() = 0;
452
453 virtual void translate(SkScalar tx, SkScalar ty) = 0;
454 virtual void transform(const SkM44& m44) = 0;
455 virtual void transform(const SkMatrix& matrix) = 0;
456 virtual void integralTransform() = 0;
457
458 virtual void clipRect(const SkRect& rect, ClipOp op, bool is_aa) = 0;
459 virtual void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) = 0;
460 virtual void clipPath(const SkPath& path, ClipOp op, bool is_aa) = 0;
461 };
462 friend class DummyDelegate;
463 friend class DlCanvasDelegate;
464 friend class PrerollDelegate;
465
466 std::vector<std::unique_ptr<StateEntry>> state_stack_;
467 friend class MutatorContext;
468
469 std::shared_ptr<Delegate> delegate_;
470 RenderingAttributes outstanding_;
471
472 friend class SaveLayerEntry;
473};
474
475} // namespace flutter
476
477#endif // FLUTTER_FLOW_LAYERS_LAYER_STATE_STACK_H_
static void apply_flags(SkFont *font, unsigned flags)
Definition: FontTest.cpp:65
static sk_sp< SkImage > color_filter(const SkImage *image, SkColorFilter *colorFilter)
static bool apply(Pass *pass, SkRecord *record)
#define SK_Scalar1
Definition: SkScalar.h:18
Definition: SkM44.h:150
Definition: SkPath.h:59
Developer-facing API for rendering anything within the engine.
Definition: dl_canvas.h:38
void applyColorFilter(const SkRect &bounds, const std::shared_ptr< const DlColorFilter > &filter)
void translate(SkScalar tx, SkScalar ty)
void applyImageFilter(const SkRect &bounds, const std::shared_ptr< const DlImageFilter > &filter)
void clipPath(const SkPath &path, bool is_aa)
void applyBackdropFilter(const SkRect &bounds, const std::shared_ptr< const DlImageFilter > &filter, DlBlendMode blend_mode)
void clipRRect(const SkRRect &rrect, bool is_aa)
void clipRect(const SkRect &rect, bool is_aa)
void applyOpacity(const SkRect &bounds, SkScalar opacity)
std::shared_ptr< const DlImageFilter > outstanding_image_filter() const
SkRect outstanding_bounds() const
SkMatrix transform_3x3() const
void fill(MutatorsStack *mutators)
AutoRestore applyState(const SkRect &bounds, int can_apply_flags)
DlPaint * fill(DlPaint &paint) const
void set_preroll_delegate(const SkRect &cull_rect, const SkMatrix &matrix)
SkScalar outstanding_opacity() const
static constexpr int kCallerCanApplyAnything
static constexpr int kCallerCanApplyColorFilter
void set_delegate(DlCanvas *canvas)
bool content_culled(const SkRect &content_bounds) const
std::shared_ptr< const DlColorFilter > outstanding_color_filter() const
static constexpr int kCallerCanApplyImageFilter
static constexpr int kCallerCanApplyOpacity
const Paint & paint
Definition: color_source.cc:38
MockDelegate delegate_
float SkScalar
Definition: extension.cpp:12
FlutterSemanticsFlag flags
#define FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName)
Definition: macros.h:31
static SkColor blend(SkColor dst, SkColor src, void(*mode)(float, float, float, float *, float *, float *))
Definition: hsl.cpp:142
clipRRect(r.rrect, r.opAA.op(), r.opAA.aa())) DRAW(ClipRect
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
clipPath(r.path, r.opAA.op(), r.opAA.aa())) DRAW(ClipRRect
sk_sp< const SkImageFilter > backdrop
Definition: SkRecords.h:191
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DlCanvas::ClipOp ClipOp
bool Equals(const T *a, const T *b)
Definition: dl_comparable.h:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
bool operator==(const ViewportMetrics &a, const ViewportMetrics &b)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
@ kSrcOver
r = s + (1-sa)*d
flutter::DlPaint DlPaint
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165