Flutter Impeller
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< 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(), 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 }
std::optional< Rect > CurrentClipCoverage() const
float Scalar
Definition: scalar.h:19
TRect< Scalar > Rect
Definition: rect.h:792
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: