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 
6 
7 #include "flutter/fml/logging.h"
9 
10 namespace impeller {
11 
12 EntityPassClipStack::EntityPassClipStack(const Rect& initial_coverage_rect) {
13  subpass_state_.push_back(SubpassState{
14  .clip_coverage =
15  {
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 
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 
45  subpass_state_.pop_back();
46  next_replay_index_ = subpass_state_.back().rendered_clip_entities.size();
47 }
48 
49 const std::vector<ClipCoverageLayer>
51  return subpass_state_.back().clip_coverage;
52 }
53 
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 
101  const ClipContents& clip_contents,
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>&
210  return subpass_state_.back().rendered_clip_entities;
211 }
212 
213 } // namespace impeller
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...
std::optional< Rect > CurrentClipCoverage() const
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
const std::vector< ReplayResult > & GetReplayEntities() 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)
ClipStateResult RecordRestore(Point global_pass_position, size_t restore_height)
EntityPassClipStack(const Rect &initial_coverage_rect)
Create a new [EntityPassClipStack] with an initialized coverage rect.
const std::vector< ClipCoverageLayer > GetClipCoverageLayers() const
float Scalar
Definition: scalar.h:19
std::optional< Rect > coverage
This coverage is the outer coverage of the clip.
Definition: clip_contents.h:26
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
constexpr auto GetBottom() const
Definition: rect.h:361
constexpr auto GetTop() const
Definition: rect.h:357
constexpr auto GetLeft() const
Definition: rect.h:355
Round(const TRect< U > &r)
Definition: rect.h:699
constexpr auto GetRight() const
Definition: rect.h:359