Flutter Engine
 
Loading...
Searching...
No Matches
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.
 
 ~EntityPassClipStack ()=default
 
std::optional< RectCurrentClipCoverage () 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)
 
ReplayResultGetLastReplayResult ()
 
ClipStateResult RecordRestore (Point global_pass_position, size_t restore_height)
 
const std::vector< ReplayResult > & GetReplayEntities () const
 
const std::vector< ClipCoverageLayerGetClipCoverageLayers () 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(), impeller::testing::TEST(), and impeller::testing::TEST().

◆ GetClipCoverageLayers()

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

◆ 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

◆ 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(), impeller::testing::TEST(), 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(), impeller::testing::TEST(), 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}
std::optional< Rect > CurrentClipCoverage() const
#define FML_DCHECK(condition)
Definition logging.h:122
float Scalar
Definition scalar.h:19
TRect< Scalar > Rect
Definition rect.h:788
Round(const TRect< U > &r)
Definition rect.h:695

References impeller::EntityPassClipStack::ReplayResult::clip_contents, impeller::EntityPassClipStack::ClipStateResult::clip_did_change, impeller::ClipCoverage::coverage, impeller::ClipCoverageLayer::coverage, CurrentClipCoverage(), FML_DCHECK, 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(), impeller::testing::TEST(), impeller::testing::TEST(), impeller::testing::TEST(), impeller::testing::TEST(), impeller::testing::TEST(), impeller::testing::TEST(), impeller::testing::TEST(), impeller::testing::TEST(), 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}
Definition ref_ptr.h:261

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

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


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