| Flutter Impeller |

Classes | Public Member Functions | List of all members
impeller::EntityPassClipStack Class Reference

A class that tracks all clips that have been recorded in the current entity pass stencil. More...

`#include <entity_pass_clip_stack.h>`

## Classes
struct | ClipStateResult
struct | ReplayResult

## Public Member Functions
| EntityPassClipStack (const Rect &initial_coverage_rect)
| Create a new [EntityPassClipStack] with an initialized coverage rect. More...
| ~EntityPassClipStack ()=default
std::optional< Rect > | CurrentClipCoverage () const
void | PushSubpass (std::optional< Rect > subpass_coverage, size_t clip_height)
void | PopSubpass ()
bool | HasCoverage () const
ClipStateResult | RecordClip (const ClipContents &clip_contents, Matrix transform, Point global_pass_position, uint32_t clip_depth, size_t clip_height_floor, bool is_aa)
ReplayResult & | GetLastReplayResult ()
ClipStateResult | RecordRestore (Point global_pass_position, size_t restore_height)
const std::vector< ReplayResult > & | GetReplayEntities () const
const std::vector< ClipCoverageLayer > | GetClipCoverageLayers () const

## Detailed Description

A class that tracks all clips that have been recorded in the current entity pass stencil.

These clips are replayed when restoring the backdrop so that the stencil buffer is left in an identical state.

Definition at line 23 of file entity_pass_clip_stack.h.

## Constructor & Destructor Documentation

## ◆ EntityPassClipStack()

impeller::EntityPassClipStack::EntityPassClipStack | ( | const Rect & | initial_coverage_rect | ) |
| explicit

Create a new [EntityPassClipStack] with an initialized coverage rect.

Definition at line 12 of file entity_pass_clip_stack.cc.

12 {
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 }

References impeller::ClipCoverageLayer::coverage.

## ◆ ~EntityPassClipStack()

impeller::EntityPassClipStack::~EntityPassClipStack | ( |  | ) |
| default

## Member Function Documentation

## ◆ CurrentClipCoverage()

std::optional< Rect > impeller::EntityPassClipStack::CurrentClipCoverage | ( |  | ) | const

Definition at line 24 of file entity_pass_clip_stack.cc.

24 {
25 return subpass_state_.back().clip_coverage.back().coverage;
26 }

Referenced by impeller::Canvas::ClipGeometry(), impeller::Canvas::GetLocalCoverageLimit(), RecordClip(), impeller::Canvas::Restore(), and impeller::testing::TEST().

## ◆ GetClipCoverageLayers()

const std::vector< ClipCoverageLayer > impeller::EntityPassClipStack::GetClipCoverageLayers | ( |  | ) | const

Definition at line 50 of file entity_pass_clip_stack.cc.

50 {
51 return subpass_state_.back().clip_coverage;
52 }

Referenced by impeller::testing::TEST().

## ◆ GetLastReplayResult()

ReplayResult& impeller::EntityPassClipStack::GetLastReplayResult | ( |  | ) |
| inline

Definition at line 61 of file entity_pass_clip_stack.h.

61 {
62 return GetCurrentSubpassState().rendered_clip_entities.back();
63 }

Referenced by impeller::Canvas::ClipGeometry().

## ◆ GetReplayEntities()

const std::vector< EntityPassClipStack::ReplayResult > & impeller::EntityPassClipStack::GetReplayEntities | ( |  | ) | const

Definition at line 209 of file entity_pass_clip_stack.cc.

209 {
210 return subpass_state_.back().rendered_clip_entities;
211 }

Referenced by impeller::testing::TEST().

## ◆ HasCoverage()

bool impeller::EntityPassClipStack::HasCoverage | ( |  | ) | const

Definition at line 28 of file entity_pass_clip_stack.cc.

28 {
29 return !subpass_state_.back().clip_coverage.empty();
30 }

Referenced by impeller::Canvas::GetLocalCoverageLimit().

## ◆ PopSubpass()

void impeller::EntityPassClipStack::PopSubpass | ( |  | ) |

Definition at line 44 of file entity_pass_clip_stack.cc.

44 {
45 subpass_state_.pop_back();
46 next_replay_index_ = subpass_state_.back().rendered_clip_entities.size();
47 }

Referenced by impeller::Canvas::Restore(), and impeller::testing::TEST().

## ◆ PushSubpass()

void impeller::EntityPassClipStack::PushSubpass | ( | std::optional< Rect > | subpass_coverage,
|  | size_t | clip_height
| ) |  |

Definition at line 32 of file entity_pass_clip_stack.cc.

33 {
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 }

References impeller::ClipCoverageLayer::coverage.

Referenced by impeller::Canvas::SaveLayer(), and impeller::testing::TEST().

## ◆ RecordClip()

EntityPassClipStack::ClipStateResult impeller::EntityPassClipStack::RecordClip | ( | const ClipContents & | clip_contents,
|  | Matrix | transform,
|  | Point | global_pass_position,
|  | uint32_t | clip_depth,
|  | size_t | clip_height_floor,
|  | bool | is_aa
| ) |  |

Definition at line 100 of file entity_pass_clip_stack.cc.

106 {
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 }
impeller::EntityPassClipStack::CurrentClipCoverage
std::optional< Rect > CurrentClipCoverage() const
Definition: entity_pass_clip_stack.cc:24
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:792
impeller::TRect< Scalar >::Round
Round(const TRect< U > &r)
Definition: rect.h:699

References impeller::EntityPassClipStack::ReplayResult::clip_contents, impeller::EntityPassClipStack::ClipStateResult::clip_did_change, impeller::ClipCoverage::coverage, impeller::ClipCoverageLayer::coverage, CurrentClipCoverage(), impeller::TRect< T >::GetBottom(), impeller::ClipContents::GetClipCoverage(), impeller::TRect< T >::GetLeft(), impeller::TRect< T >::GetRight(), impeller::TRect< T >::GetTop(), impeller::ClipCoverage::is_difference_or_non_square, impeller::TRect< Scalar >::Round(), impeller::EntityPassClipStack::ClipStateResult::should_render, and transform.

Referenced by impeller::Canvas::ClipGeometry(), and impeller::testing::TEST().

## ◆ RecordRestore()

EntityPassClipStack::ClipStateResult impeller::EntityPassClipStack::RecordRestore | ( | Point | global_pass_position,
|  | size_t | restore_height
| ) |  |

Definition at line 54 of file entity_pass_clip_stack.cc.

56 {
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 }

References impeller::EntityPassClipStack::ClipStateResult::clip_did_change, and impeller::EntityPassClipStack::ClipStateResult::should_render.

Referenced by impeller::Canvas::Restore(), and impeller::testing::TEST().

The documentation for this class was generated from the following files:

- impeller/entity/entity_pass_clip_stack.h
- impeller/entity/entity_pass_clip_stack.cc