Flutter Impeller
save_layer_utils.cc
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 
7 
8 namespace impeller {
9 
10 namespace {
11 bool SizeDifferenceUnderThreshold(Size a, Size b, Scalar threshold) {
12  return (std::abs(a.width - b.width) / b.width) < threshold &&
13  (std::abs(a.height - b.height) / b.height) < threshold;
14 }
15 
16 static constexpr Scalar kDefaultSizeThreshold = 0.3;
17 } // namespace
18 
19 std::optional<Rect> ComputeSaveLayerCoverage(
20  const Rect& content_coverage,
21  const Matrix& effect_transform,
22  const Rect& coverage_limit,
23  const std::shared_ptr<FilterContents>& image_filter,
24  bool flood_output_coverage,
25  bool flood_input_coverage) {
26  Rect coverage = content_coverage;
27  // There are three conditions that should cause input coverage to flood, the
28  // first is the presence of a backdrop filter on the saveLayer. The second is
29  // the presence of a color filter that effects transparent black on the
30  // saveLayer. The last is the presence of unbounded content within the
31  // saveLayer (such as a drawPaint, bdf, et cetera). Note that unbounded
32  // coverage is handled in the display list dispatcher.
33  //
34  // Backdrop filters apply before the saveLayer is restored. The presence of
35  // a backdrop filter causes the content coverage of the saveLayer to be
36  // unbounded.
37  //
38  // If there is a color filter that needs to flood its output. The color filter
39  // is applied before any image filters, so this floods input coverage and not
40  // the output coverage. Technically, we only need to flood the output of the
41  // color filter and could allocate a render target sized just to the content,
42  // but we don't currenty have the means to do so. Flooding the coverage is a
43  // non-optimal but technically correct way to render this.
44  //
45  // If the saveLayer contains unbounded content, then at this point the
46  // dl_dispatcher will have set content coverage to Rect::MakeMaximum().
47  if (flood_input_coverage) {
48  coverage = Rect::MakeMaximum();
49  }
50 
51  // The content coverage must be scaled by any image filters present on the
52  // saveLayer paint. For example, if a saveLayer has a coverage limit of
53  // 100x100, but it has a Matrix image filter that scales by one half, the
54  // actual coverage limit is 200x200.
55  if (image_filter) {
56  // Transform the input coverage into the global coordinate space before
57  // computing the bounds limit intersection. This is the "worst case"
58  // coverage value before we intersect with the content coverage below.
59  std::optional<Rect> source_coverage_limit =
60  image_filter->GetSourceCoverage(effect_transform, coverage_limit);
61  if (!source_coverage_limit.has_value()) {
62  // No intersection with parent coverage limit.
63  return std::nullopt;
64  }
65  // The image filter may change the coverage limit required to flood
66  // the parent layer. Returning the source coverage limit so that we
67  // can guarantee the render target is larger enough.
68  //
69  // See note below on flood_output_coverage.
70  if (flood_output_coverage || coverage.IsMaximum()) {
71  return source_coverage_limit;
72  }
73 
74  // Trimming the content coverage by the coverage limit can reduce memory
75  // bandwith. But in cases where there are animated matrix filters, such as
76  // in the framework's zoom transition, the changing scale values continually
77  // change the source_coverage_limit. Intersecting the source_coverage_limit
78  // with the coverage may result in slightly different texture sizes each
79  // frame of the animation. This leads to non-optimal allocation patterns as
80  // differently sized textures cannot be reused. Hence the following
81  // herustic: If the coverage is within a semi-arbitrary percentage of the
82  // intersected coverage, then just use the transformed coverage. In other
83  // cases, use the intersection.
84  auto transformed_coverage = coverage.TransformBounds(effect_transform);
85  auto intersected_coverage =
86  transformed_coverage.Intersection(source_coverage_limit.value());
87  if (intersected_coverage.has_value() &&
88  SizeDifferenceUnderThreshold(transformed_coverage.GetSize(),
89  intersected_coverage->GetSize(),
90  kDefaultSizeThreshold)) {
91  // Returning the transformed coverage is always correct, it just may
92  // be larger than the clip area or onscreen texture.
93  return transformed_coverage;
94  }
95  return intersected_coverage;
96  }
97 
98  // If the input coverage is maximum, just return the coverage limit that
99  // is already in the global coordinate space.
100  //
101  // If flood_output_coverage is true, then the restore is applied with a
102  // destructive blend mode that requires flooding to the coverage limit.
103  // Technically we could only allocated a render target as big as the input
104  // coverage and then use a decal sampling mode to perform the flood. Returning
105  // the coverage limit is a correct but non optimal means of ensuring correct
106  // rendering.
107  if (flood_output_coverage || coverage.IsMaximum()) {
108  return coverage_limit;
109  }
110 
111  // Transform the input coverage into the global coordinate space before
112  // computing the bounds limit intersection.
113  Rect transformed_coverage = coverage.TransformBounds(effect_transform);
114  std::optional<Rect> intersection =
115  transformed_coverage.Intersection(coverage_limit);
116  if (!intersection.has_value()) {
117  return std::nullopt;
118  }
119  // The the resulting coverage rect is nearly the same as the coverage_limit,
120  // round up to the coverage_limit.
121  Rect intersect_rect = intersection.value();
122  if (SizeDifferenceUnderThreshold(intersect_rect.GetSize(),
123  coverage_limit.GetSize(),
124  kDefaultSizeThreshold)) {
125  return coverage_limit;
126  }
127 
128  return intersect_rect;
129 }
130 
131 } // namespace impeller
float Scalar
Definition: scalar.h:19
TSize< Scalar > Size
Definition: size.h:159
std::optional< Rect > ComputeSaveLayerCoverage(const Rect &content_coverage, const Matrix &effect_transform, const Rect &coverage_limit, const std::shared_ptr< FilterContents > &image_filter, bool flood_output_coverage, bool flood_input_coverage)
Compute the coverage of a subpass in the global coordinate space.
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:476
constexpr bool IsMaximum() const
Definition: rect.h:318
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:532
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:331
constexpr static TRect MakeMaximum()
Definition: rect.h:188