| Flutter Impeller |

entity_pass_clip_stack.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/entity_pass_clip_stack.h"
6
7 #include "flutter/fml/logging.h"
8 #include "impeller/entity/contents/clip_contents.h"
9
10 namespace impeller {
11
12 EntityPassClipStack::EntityPassClipStack(const Rect& initial_coverage_rect) {
13 subpass_state_.push_back(SubpassState{
14 .clip_coverage =
15 {
16 {ClipCoverageLayer{
17 .coverage = initial_coverage_rect,
18 .clip_height = 0,
19 }},
20 },
21 });
22 }
23
24 std::optional<Rect> EntityPassClipStack::CurrentClipCoverage() const {
25 return subpass_state_.back().clip_coverage.back().coverage;
26 }
27
28 bool EntityPassClipStack::HasCoverage() const {
29 return !subpass_state_.back().clip_coverage.empty();
30 }
31
32 void EntityPassClipStack::PushSubpass(std::optional<Rect> subpass_coverage,
33 size_t clip_height) {
34 subpass_state_.push_back(SubpassState{
35 .clip_coverage =
36 {
37 ClipCoverageLayer{.coverage = subpass_coverage,
38 .clip_height = clip_height},
39 },
40 });
41 next_replay_index_ = 0;
42 }
43
44 void EntityPassClipStack::PopSubpass() {
45 subpass_state_.pop_back();
46 next_replay_index_ = subpass_state_.back().rendered_clip_entities.size();
47 }
48
49 const std::vector<ClipCoverageLayer>
50 EntityPassClipStack::GetClipCoverageLayers() const {
51 return subpass_state_.back().clip_coverage;
52 }
53
54 EntityPassClipStack::ClipStateResult EntityPassClipStack::RecordRestore(
55 Point global_pass_position,
56 size_t restore_height) {
57 ClipStateResult result = {.should_render = false, .clip_did_change = false};
58 auto& subpass_state = GetCurrentSubpassState();
59
60 if (subpass_state.clip_coverage.back().clip_height <= restore_height) {
61 // Drop clip restores that will do nothing.
62 return result;
63 }
64
65 auto restoration_index =
66 restore_height - subpass_state.clip_coverage.front().clip_height;
67 FML_DCHECK(restoration_index < subpass_state.clip_coverage.size());
68
69 // We only need to restore the area that covers the coverage of the
70 // clip rect at target height + 1.
71 std::optional<Rect> restore_coverage =
72 (restoration_index + 1 < subpass_state.clip_coverage.size())
73 ? subpass_state.clip_coverage[restoration_index + 1].coverage
74 : std::nullopt;
75 if (restore_coverage.has_value()) {
76 // Make the coverage rectangle relative to the current pass.
77 restore_coverage = restore_coverage->Shift(-global_pass_position);
78 }
79
80 subpass_state.clip_coverage.resize(restoration_index + 1);
81 result.clip_did_change = true;
82
83 if (subpass_state.clip_coverage.back().coverage.has_value()) {
84 FML_DCHECK(next_replay_index_ <=
85 subpass_state.rendered_clip_entities.size());
86 // https://github.com/flutter/flutter/issues/162172
87 // This code is slightly wrong and should be popping more than one clip
88 // entry.
89 if (!subpass_state.rendered_clip_entities.empty()) {
90 subpass_state.rendered_clip_entities.pop_back();
91
92 if (next_replay_index_ > subpass_state.rendered_clip_entities.size()) {
93 next_replay_index_ = subpass_state.rendered_clip_entities.size();
94 }
95 }
96 }
97 return result;
98 }
99
100 EntityPassClipStack::ClipStateResult EntityPassClipStack::RecordClip(
101 const ClipContents& clip_contents,
102 Matrix transform,
103 Point global_pass_position,
104 uint32_t clip_depth,
105 size_t clip_height_floor,
106 bool is_aa) {
107 ClipStateResult result = {.should_render = false, .clip_did_change = false};
108
109 std::optional<Rect> maybe_clip_coverage = CurrentClipCoverage();
110 // Running this append op won't impact the clip buffer because the
111 // whole screen is already being clipped, so skip it.
112 if (!maybe_clip_coverage.has_value()) {
113 return result;
114 }
115 auto current_clip_coverage = maybe_clip_coverage.value();
116 // Entity transforms are relative to the current pass position, so we need
117 // to check clip coverage in the same space.
118 current_clip_coverage = current_clip_coverage.Shift(-global_pass_position);
119
120 ClipCoverage clip_coverage =
121 clip_contents.GetClipCoverage(current_clip_coverage);
122 if (clip_coverage.coverage.has_value()) {
123 clip_coverage.coverage =
124 clip_coverage.coverage->Shift(global_pass_position);
125 }
126
127 SubpassState& subpass_state = GetCurrentSubpassState();
128
129 // Compute the previous clip height.
130 size_t previous_clip_height = 0;
131 if (!subpass_state.clip_coverage.empty()) {
132 previous_clip_height = subpass_state.clip_coverage.back().clip_height;
133 } else {
134 // If there is no clip coverage, then the previous clip height is the
135 // clip height floor.
136 previous_clip_height = clip_height_floor;
137 }
138
139 // If the new clip coverage is bigger than the existing coverage for
140 // intersect clips, we do not need to change the clip region.
141 if (!clip_coverage.is_difference_or_non_square &&
142 clip_coverage.coverage.has_value() &&
143 clip_coverage.coverage.value().Contains(current_clip_coverage)) {
144 subpass_state.clip_coverage.push_back(ClipCoverageLayer{
145 .coverage = current_clip_coverage, //
146 .clip_height = previous_clip_height + 1 //
147 });
148
149 return result;
150 }
151
152 // If the clip is an axis aligned rect and either is_aa is false or
153 // the clip is very nearly integral, then the depth write can be
154 // skipped for intersect clips. Since we use 4x MSAA, anything within
155 // < ~0.125 of an integral value in either axis can be treated as
156 // approximately the same as an integral value.
157 bool should_render = true;
158 std::optional<Rect> coverage_value = clip_coverage.coverage;
159 if (!clip_coverage.is_difference_or_non_square &&
160 coverage_value.has_value()) {
161 const Rect& coverage = coverage_value.value();
162 constexpr Scalar threshold = 0.124;
163 if (!is_aa ||
164 (std::abs(std::round(coverage.GetLeft()) - coverage.GetLeft()) <=
165 threshold &&
166 std::abs(std::round(coverage.GetTop()) - coverage.GetTop()) <=
167 threshold &&
168 std::abs(std::round(coverage.GetRight()) - coverage.GetRight()) <=
169 threshold &&
170 std::abs(std::round(coverage.GetBottom()) - coverage.GetBottom()) <=
171 threshold)) {
172 coverage_value = Rect::Round(clip_coverage.coverage.value());
173 should_render = false;
174 }
175 }
176
177 subpass_state.clip_coverage.push_back(ClipCoverageLayer{
178 .coverage = coverage_value, //
179 .clip_height = previous_clip_height + 1 //
180
181 });
182 result.clip_did_change = true;
183 result.should_render = should_render;
184
185 FML_DCHECK(subpass_state.clip_coverage.back().clip_height ==
186 subpass_state.clip_coverage.front().clip_height +
187 subpass_state.clip_coverage.size() - 1);
188
189 FML_DCHECK(next_replay_index_ == subpass_state.rendered_clip_entities.size())
190 << "Not all clips have been replayed before appending new clip.";
191
192 subpass_state.rendered_clip_entities.push_back(ReplayResult{
193 .clip_contents = clip_contents, //
194 .transform = transform, //
195 .clip_coverage = coverage_value, //
196 .clip_depth = clip_depth //
197 });
198 next_replay_index_++;
199
200 return result;
201 }
202
203 EntityPassClipStack::SubpassState&
204 EntityPassClipStack::GetCurrentSubpassState() {
205 return subpass_state_.back();
206 }
207
208 const std::vector<EntityPassClipStack::ReplayResult>&
209 EntityPassClipStack::GetReplayEntities() const {
210 return subpass_state_.back().rendered_clip_entities;
211 }
212
213 } // namespace impeller
impeller::ClipContents
Definition: clip_contents.h:29
impeller::ClipContents::GetClipCoverage
ClipCoverage GetClipCoverage(const std::optional< Rect > &current_clip_coverage) const
Given the current pass space bounding rectangle of the clip buffer, return the expected clip coverage...
Definition: clip_contents.cc:45
impeller::EntityPassClipStack::CurrentClipCoverage
std::optional< Rect > CurrentClipCoverage() const
Definition: entity_pass_clip_stack.cc:24
impeller::EntityPassClipStack::PushSubpass
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
Definition: entity_pass_clip_stack.cc:32
impeller::EntityPassClipStack::GetReplayEntities
const std::vector< ReplayResult > & GetReplayEntities() const
Definition: entity_pass_clip_stack.cc:209
impeller::EntityPassClipStack::RecordClip
ClipStateResult RecordClip(const ClipContents &clip_contents, Matrix transform, Point global_pass_position, uint32_t clip_depth, size_t clip_height_floor, bool is_aa)
Definition: entity_pass_clip_stack.cc:100
impeller::EntityPassClipStack::RecordRestore
ClipStateResult RecordRestore(Point global_pass_position, size_t restore_height)
Definition: entity_pass_clip_stack.cc:54
impeller::EntityPassClipStack::PopSubpass
void PopSubpass()
Definition: entity_pass_clip_stack.cc:44
impeller::EntityPassClipStack::HasCoverage
bool HasCoverage() const
Definition: entity_pass_clip_stack.cc:28
impeller::EntityPassClipStack::EntityPassClipStack
EntityPassClipStack(const Rect &initial_coverage_rect)
Create a new [EntityPassClipStack] with an initialized coverage rect.
Definition: entity_pass_clip_stack.cc:12
impeller::EntityPassClipStack::GetClipCoverageLayers
const std::vector< ClipCoverageLayer > GetClipCoverageLayers() const
Definition: entity_pass_clip_stack.cc:50
clip_contents.h
entity_pass_clip_stack.h
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
impeller
Definition: allocation.cc:12
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::ClipCoverage
Definition: clip_contents.h:14
impeller::ClipCoverage::is_difference_or_non_square
bool is_difference_or_non_square
Definition: clip_contents.h:17
impeller::ClipCoverage::coverage
std::optional< Rect > coverage
This coverage is the outer coverage of the clip.
Definition: clip_contents.h:26
impeller::ClipCoverageLayer
Definition: entity_pass_clip_stack.h:13
impeller::ClipCoverageLayer::coverage
std::optional< Rect > coverage
Definition: entity_pass_clip_stack.h:14
impeller::EntityPassClipStack::ClipStateResult
Definition: entity_pass_clip_stack.h:32
impeller::EntityPassClipStack::ClipStateResult::clip_did_change
bool clip_did_change
Definition: entity_pass_clip_stack.h:38
impeller::EntityPassClipStack::ClipStateResult::should_render
bool should_render
Definition: entity_pass_clip_stack.h:35
impeller::EntityPassClipStack::ReplayResult
Definition: entity_pass_clip_stack.h:25
impeller::EntityPassClipStack::ReplayResult::clip_contents
ClipContents clip_contents
Definition: entity_pass_clip_stack.h:26
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::TPoint< Scalar >
impeller::TRect< Scalar >
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:361
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:357
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:355
impeller::TRect< Scalar >::Round
Round(const TRect< U > &r)
Definition: rect.h:699
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:359