| 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
5 #include "impeller/entity/save_layer_utils.h"
6
7 namespace impeller {
8
9 namespace {
10 bool SizeDifferenceUnderThreshold(Size a, Size b, Scalar threshold) {
11 return (std::abs(a.width - b.width) / b.width) < threshold &&
12 (std::abs(a.height - b.height) / b.height) < threshold;
13 }
14 } // namespace
15
16 std::optional<Rect> ComputeSaveLayerCoverage(
17 const Rect& content_coverage,
18 const Matrix& effect_transform,
19 const Rect& coverage_limit,
20 const std::shared_ptr<FilterContents>& image_filter,
21 bool flood_output_coverage,
22 bool flood_input_coverage) {
23 Rect coverage = content_coverage;
24 // There are three conditions that should cause input coverage to flood, the
25 // first is the presence of a backdrop filter on the saveLayer. The second is
26 // the presence of a color filter that effects transparent black on the
27 // saveLayer. The last is the presence of unbounded content within the
28 // saveLayer (such as a drawPaint, bdf, et cetera). Note that currently
29 // only the presence of a backdrop filter impacts this flag, while color
30 // filters are not yet handled
31 // (https://github.com/flutter/flutter/issues/154035) and unbounded coverage
32 // 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(), 0.2)) {
90 // Returning the transformed coverage is always correct, it just may
91 // be larger than the clip area or onscreen texture.
92 return transformed_coverage;
93 }
94 return intersected_coverage;
95 }
96
97 // If the input coverage is maximum, just return the coverage limit that
98 // is already in the global coordinate space.
99 //
100 // If flood_output_coverage is true, then the restore is applied with a
101 // destructive blend mode that requires flooding to the coverage limit.
102 // Technically we could only allocated a render target as big as the input
103 // coverage and then use a decal sampling mode to perform the flood. Returning
104 // the coverage limit is a correct but non optimal means of ensuring correct
105 // rendering.
106 if (flood_output_coverage || coverage.IsMaximum()) {
107 return coverage_limit;
108 }
109
110 // Transform the input coverage into the global coordinate space before
111 // computing the bounds limit intersection.
112 return coverage.TransformBounds(effect_transform)
113 .Intersection(coverage_limit);
114 }
115
116 } // namespace impeller
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:466
impeller::TRect::IsMaximum
constexpr bool IsMaximum() const
Definition: rect.h:308
impeller::ComputeSaveLayerCoverage
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.
Definition: save_layer_utils.cc:16
impeller::TRect::Intersection
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:522
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
save_layer_utils.h
impeller::TRect< Scalar >::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:178
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller
Definition: allocation.cc:12
impeller::TRect
Definition: rect.h:122
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37