Skip to content

Commit 73c145c

Browse files
Reverts "[Impeller] Use the scissor to limit all draws by clip coverage. (flutter#51698)" (flutter#51728)
Reverts: flutter#51698 Initiated by: bdero Reason for reverting: Golden breakage on framework tree -- https://flutter-gold.skia.org/search?issue=145855&crs=github&patchsets=4&corpus=flutter Original PR Author: bdero Reviewed By: {jonahwilliams} This change reverts the following previous change: Attempts to improve flutter/flutter#145274. Our new clipping technique paints walls on the depth buffer "in front" of the Entities that will be affected by said clips. So when an intersect clip is drawn (the common case), the clip will cover the whole framebuffer. Depth is divvied up such that deeper clips get drawn _behind_ shallower clips, and so many clips actually don't end up drawing depth across the whole framebuffer. However, if the app does a lot of transitioning from a huge clips to a small clips, a lot of unnecessary depth churn occurs (very common in Flutter -- this happens with both the app bar and floating action button in the counter template, for example). Since progressively deeper layers in the clip coverage stack always subset their parent layers, we can reduce waste for small intersect clips by setting the scissor to the clip coverage rect instead of drawing the clip to the whole screen. Note that this change _does not_ help much with huge/fullscreen clips. Also, we could potentially improve this further by computing much stricter bounds. Rather than just using clip coverage for the scissor, we could intersect it with the union of all draws affected by the clip at the cost of a bit more CPU churn per draw. I don't think that's enough juice for the squeeze though. Before (`Play/AiksTest.CanRenderNestedClips/Metal`): https://github.com/flutter/engine/assets/919017/7858400f-793a-4f7b-a0e4-fa3581198beb After (`Play/AiksTest.CanRenderNestedClips/Metal`): https://github.com/flutter/engine/assets/919017/b2f7c96d-a820-454d-91df-f5fae4976e91
1 parent 2e0616a commit 73c145c

File tree

4 files changed

+52
-134
lines changed

4 files changed

+52
-134
lines changed

impeller/entity/entity_pass.cc

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
#include "impeller/entity/inline_pass_context.h"
2626
#include "impeller/geometry/color.h"
2727
#include "impeller/geometry/rect.h"
28-
#include "impeller/geometry/size.h"
2928
#include "impeller/renderer/command_buffer.h"
3029

3130
#ifdef IMPELLER_DEBUG
@@ -753,25 +752,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
753752
FML_UNREACHABLE();
754753
}
755754

756-
static void SetClipScissor(std::optional<Rect> clip_coverage,
757-
RenderPass& pass,
758-
Point global_pass_position) {
759-
if constexpr (!ContentContext::kEnableStencilThenCover) {
760-
return;
761-
}
762-
// Set the scissor to the clip coverage area. We do this prior to rendering
763-
// the clip itself and all its contents.
764-
IRect scissor;
765-
if (clip_coverage.has_value()) {
766-
clip_coverage = clip_coverage->Shift(-global_pass_position);
767-
scissor = IRect::RoundOut(clip_coverage.value());
768-
// The scissor rect must not exceed the size of the render target.
769-
scissor = scissor.Intersection(IRect::MakeSize(pass.GetRenderTargetSize()))
770-
.value_or(IRect());
771-
}
772-
pass.SetScissor(scissor);
773-
}
774-
775755
bool EntityPass::RenderElement(Entity& element_entity,
776756
size_t clip_depth_floor,
777757
InlinePassContext& pass_context,
@@ -791,10 +771,8 @@ bool EntityPass::RenderElement(Entity& element_entity,
791771
// Restore any clips that were recorded before the backdrop filter was
792772
// applied.
793773
auto& replay_entities = clip_coverage_stack.GetReplayEntities();
794-
for (const auto& replay : replay_entities) {
795-
SetClipScissor(clip_coverage_stack.CurrentClipCoverage(), *result.pass,
796-
global_pass_position);
797-
if (!replay.entity.Render(renderer, *result.pass)) {
774+
for (const auto& entity : replay_entities) {
775+
if (!entity.Render(renderer, *result.pass)) {
798776
VALIDATION_LOG << "Failed to render entity for clip restore.";
799777
}
800778
}
@@ -848,18 +826,11 @@ bool EntityPass::RenderElement(Entity& element_entity,
848826
element_entity.GetContents()->SetCoverageHint(
849827
Rect::Intersection(element_coverage_hint, current_clip_coverage));
850828

851-
EntityPassClipStack::ClipStateResult clip_state_result =
852-
clip_coverage_stack.ApplyClipState(clip_coverage, element_entity,
853-
clip_depth_floor,
854-
global_pass_position);
855-
856-
if (clip_state_result.clip_did_change) {
857-
// We only need to update the pass scissor if the clip state has changed.
858-
SetClipScissor(clip_coverage_stack.CurrentClipCoverage(), *result.pass,
859-
global_pass_position);
860-
}
861-
862-
if (!clip_state_result.should_render) {
829+
if (!clip_coverage_stack.AppendClipCoverage(clip_coverage, element_entity,
830+
clip_depth_floor,
831+
global_pass_position)) {
832+
// If the entity's coverage change did not change the clip coverage, we
833+
// don't need to render it.
863834
return true;
864835
}
865836

impeller/entity/entity_pass_clip_stack.cc

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,20 @@ EntityPassClipStack::GetClipCoverageLayers() const {
4949
return subpass_state_.back().clip_coverage;
5050
}
5151

52-
EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState(
53-
Contents::ClipCoverage global_clip_coverage,
52+
bool EntityPassClipStack::AppendClipCoverage(
53+
Contents::ClipCoverage clip_coverage,
5454
Entity& entity,
5555
size_t clip_depth_floor,
5656
Point global_pass_position) {
57-
ClipStateResult result = {.should_render = false, .clip_did_change = false};
58-
5957
auto& subpass_state = GetCurrentSubpassState();
60-
switch (global_clip_coverage.type) {
58+
switch (clip_coverage.type) {
6159
case Contents::ClipCoverage::Type::kNoChange:
6260
break;
6361
case Contents::ClipCoverage::Type::kAppend: {
6462
auto op = CurrentClipCoverage();
6563
subpass_state.clip_coverage.push_back(
66-
ClipCoverageLayer{.coverage = global_clip_coverage.coverage,
64+
ClipCoverageLayer{.coverage = clip_coverage.coverage,
6765
.clip_depth = entity.GetClipDepth() + 1});
68-
result.clip_did_change = true;
6966

7067
FML_DCHECK(subpass_state.clip_coverage.back().clip_depth ==
7168
subpass_state.clip_coverage.front().clip_depth +
@@ -74,14 +71,14 @@ EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState(
7471
if (!op.has_value()) {
7572
// Running this append op won't impact the clip buffer because the
7673
// whole screen is already being clipped, so skip it.
77-
return result;
74+
return false;
7875
}
7976
} break;
8077
case Contents::ClipCoverage::Type::kRestore: {
8178
if (subpass_state.clip_coverage.back().clip_depth <=
8279
entity.GetClipDepth()) {
8380
// Drop clip restores that will do nothing.
84-
return result;
81+
return false;
8582
}
8683

8784
auto restoration_index = entity.GetClipDepth() -
@@ -99,19 +96,18 @@ EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState(
9996
restore_coverage = restore_coverage->Shift(-global_pass_position);
10097
}
10198
subpass_state.clip_coverage.resize(restoration_index + 1);
102-
result.clip_did_change = true;
10399

104100
if constexpr (ContentContext::kEnableStencilThenCover) {
105101
// Skip all clip restores when stencil-then-cover is enabled.
106102
if (subpass_state.clip_coverage.back().coverage.has_value()) {
107-
RecordEntity(entity, global_clip_coverage.type, Rect());
103+
RecordEntity(entity, clip_coverage.type);
108104
}
109-
return result;
105+
return false;
110106
}
111107

112108
if (!subpass_state.clip_coverage.back().coverage.has_value()) {
113109
// Running this restore op won't make anything renderable, so skip it.
114-
return result;
110+
return false;
115111
}
116112

117113
auto restore_contents =
@@ -134,23 +130,19 @@ EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState(
134130
#endif
135131

136132
entity.SetClipDepth(entity.GetClipDepth() - clip_depth_floor);
137-
RecordEntity(entity, global_clip_coverage.type,
138-
subpass_state.clip_coverage.back().coverage);
133+
RecordEntity(entity, clip_coverage.type);
139134

140-
result.should_render = true;
141-
return result;
135+
return true;
142136
}
143137

144138
void EntityPassClipStack::RecordEntity(const Entity& entity,
145-
Contents::ClipCoverage::Type type,
146-
std::optional<Rect> clip_coverage) {
139+
Contents::ClipCoverage::Type type) {
147140
auto& subpass_state = GetCurrentSubpassState();
148141
switch (type) {
149142
case Contents::ClipCoverage::Type::kNoChange:
150143
return;
151144
case Contents::ClipCoverage::Type::kAppend:
152-
subpass_state.rendered_clip_entities.push_back(
153-
{.entity = entity.Clone(), .clip_coverage = clip_coverage});
145+
subpass_state.rendered_clip_entities.push_back(entity.Clone());
154146
break;
155147
case Contents::ClipCoverage::Type::kRestore:
156148
if (!subpass_state.rendered_clip_entities.empty()) {
@@ -165,8 +157,7 @@ EntityPassClipStack::GetCurrentSubpassState() {
165157
return subpass_state_.back();
166158
}
167159

168-
const std::vector<EntityPassClipStack::ReplayResult>&
169-
EntityPassClipStack::GetReplayEntities() const {
160+
const std::vector<Entity>& EntityPassClipStack::GetReplayEntities() const {
170161
return subpass_state_.back().rendered_clip_entities;
171162
}
172163

impeller/entity/entity_pass_clip_stack.h

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
#define FLUTTER_IMPELLER_ENTITY_ENTITY_PASS_CLIP_STACK_H_
77

88
#include "impeller/entity/contents/contents.h"
9-
#include "impeller/entity/entity.h"
10-
#include "impeller/geometry/rect.h"
119

1210
namespace impeller {
1311

@@ -23,20 +21,6 @@ struct ClipCoverageLayer {
2321
/// stencil buffer is left in an identical state.
2422
class EntityPassClipStack {
2523
public:
26-
struct ReplayResult {
27-
Entity entity;
28-
std::optional<Rect> clip_coverage;
29-
};
30-
31-
struct ClipStateResult {
32-
/// Whether or not the Entity should be rendered. If false, the Entity may
33-
/// be safely skipped.
34-
bool should_render = false;
35-
/// Whether or not the current clip coverage changed during the call to
36-
/// `ApplyClipState`.
37-
bool clip_did_change = false;
38-
};
39-
4024
/// Create a new [EntityPassClipStack] with an initialized coverage rect.
4125
explicit EntityPassClipStack(const Rect& initial_coverage_rect);
4226

@@ -50,27 +34,24 @@ class EntityPassClipStack {
5034

5135
bool HasCoverage() const;
5236

53-
/// @brief Applies the current clip state to an Entity. If the given Entity
54-
/// is a clip operation, then the clip state is updated accordingly.
55-
ClipStateResult ApplyClipState(Contents::ClipCoverage global_clip_coverage,
56-
Entity& entity,
57-
size_t clip_depth_floor,
58-
Point global_pass_position);
37+
/// Returns true if entity should be rendered.
38+
bool AppendClipCoverage(Contents::ClipCoverage clip_coverage,
39+
Entity& entity,
40+
size_t clip_depth_floor,
41+
Point global_pass_position);
5942

6043
// Visible for testing.
61-
void RecordEntity(const Entity& entity,
62-
Contents::ClipCoverage::Type type,
63-
std::optional<Rect> clip_coverage);
44+
void RecordEntity(const Entity& entity, Contents::ClipCoverage::Type type);
6445

6546
// Visible for testing.
66-
const std::vector<ReplayResult>& GetReplayEntities() const;
47+
const std::vector<Entity>& GetReplayEntities() const;
6748

6849
// Visible for testing.
6950
const std::vector<ClipCoverageLayer> GetClipCoverageLayers() const;
7051

7152
private:
7253
struct SubpassState {
73-
std::vector<ReplayResult> rendered_clip_entities;
54+
std::vector<Entity> rendered_clip_entities;
7455
std::vector<ClipCoverageLayer> clip_coverage;
7556
};
7657

impeller/entity/entity_pass_unittests.cc

Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,16 @@ TEST(EntityPassClipStackTest, CanPushAndPopEntities) {
1717
EXPECT_TRUE(recorder.GetReplayEntities().empty());
1818

1919
Entity entity;
20-
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend,
21-
Rect::MakeLTRB(0, 0, 100, 100));
20+
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend);
2221
EXPECT_EQ(recorder.GetReplayEntities().size(), 1u);
2322

24-
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend,
25-
Rect::MakeLTRB(0, 0, 50, 50));
23+
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend);
2624
EXPECT_EQ(recorder.GetReplayEntities().size(), 2u);
27-
ASSERT_TRUE(recorder.GetReplayEntities()[0].clip_coverage.has_value());
28-
ASSERT_TRUE(recorder.GetReplayEntities()[1].clip_coverage.has_value());
29-
// NOLINTBEGIN(bugprone-unchecked-optional-access)
30-
EXPECT_EQ(recorder.GetReplayEntities()[0].clip_coverage.value(),
31-
Rect::MakeLTRB(0, 0, 100, 100));
32-
EXPECT_EQ(recorder.GetReplayEntities()[1].clip_coverage.value(),
33-
Rect::MakeLTRB(0, 0, 50, 50));
34-
// NOLINTEND(bugprone-unchecked-optional-access)
35-
36-
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore, Rect());
25+
26+
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore);
3727
EXPECT_EQ(recorder.GetReplayEntities().size(), 1u);
3828

39-
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore, Rect());
29+
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore);
4030
EXPECT_TRUE(recorder.GetReplayEntities().empty());
4131
}
4232

@@ -47,7 +37,7 @@ TEST(EntityPassClipStackTest, CanPopEntitiesSafely) {
4737
EXPECT_TRUE(recorder.GetReplayEntities().empty());
4838

4939
Entity entity;
50-
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore, Rect());
40+
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore);
5141
EXPECT_TRUE(recorder.GetReplayEntities().empty());
5242
}
5343

@@ -58,8 +48,7 @@ TEST(EntityPassClipStackTest, CanAppendNoChange) {
5848
EXPECT_TRUE(recorder.GetReplayEntities().empty());
5949

6050
Entity entity;
61-
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kNoChange,
62-
Rect());
51+
recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kNoChange);
6352
EXPECT_TRUE(recorder.GetReplayEntities().empty());
6453
}
6554

@@ -72,14 +61,12 @@ TEST(EntityPassClipStackTest, AppendCoverageNoChange) {
7261
EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_depth, 0u);
7362

7463
Entity entity;
75-
EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
64+
recorder.AppendClipCoverage(
7665
Contents::ClipCoverage{
7766
.type = Contents::ClipCoverage::Type::kNoChange,
7867
.coverage = std::nullopt,
7968
},
8069
entity, 0, Point(0, 0));
81-
EXPECT_TRUE(result.should_render);
82-
EXPECT_FALSE(result.clip_did_change);
8370

8471
EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
8572
Rect::MakeSize(Size::MakeWH(100, 100)));
@@ -95,14 +82,12 @@ TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) {
9582
// Push a clip.
9683
Entity entity;
9784
entity.SetClipDepth(0);
98-
EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
85+
recorder.AppendClipCoverage(
9986
Contents::ClipCoverage{
10087
.type = Contents::ClipCoverage::Type::kAppend,
10188
.coverage = Rect::MakeLTRB(50, 50, 55, 55),
10289
},
10390
entity, 0, Point(0, 0));
104-
EXPECT_TRUE(result.should_render);
105-
EXPECT_TRUE(result.clip_did_change);
10691

10792
ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u);
10893
EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
@@ -112,7 +97,7 @@ TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) {
11297

11398
// Restore the clip.
11499
entity.SetClipDepth(0);
115-
recorder.ApplyClipState(
100+
recorder.AppendClipCoverage(
116101
Contents::ClipCoverage{
117102
.type = Contents::ClipCoverage::Type::kRestore,
118103
.coverage = Rect::MakeLTRB(50, 50, 55, 55),
@@ -135,14 +120,12 @@ TEST(EntityPassClipStackTest, UnbalancedRestore) {
135120
// Restore the clip.
136121
Entity entity;
137122
entity.SetClipDepth(0);
138-
EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
123+
recorder.AppendClipCoverage(
139124
Contents::ClipCoverage{
140125
.type = Contents::ClipCoverage::Type::kRestore,
141126
.coverage = Rect::MakeLTRB(50, 50, 55, 55),
142127
},
143128
entity, 0, Point(0, 0));
144-
EXPECT_FALSE(result.should_render);
145-
EXPECT_FALSE(result.clip_did_change);
146129

147130
ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
148131
EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
@@ -160,16 +143,12 @@ TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) {
160143
// Push a clip.
161144
Entity entity;
162145
entity.SetClipDepth(0u);
163-
{
164-
EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
165-
Contents::ClipCoverage{
166-
.type = Contents::ClipCoverage::Type::kAppend,
167-
.coverage = Rect::MakeLTRB(50, 50, 55, 55),
168-
},
169-
entity, 0, Point(0, 0));
170-
EXPECT_TRUE(result.should_render);
171-
EXPECT_TRUE(result.clip_did_change);
172-
}
146+
recorder.AppendClipCoverage(
147+
Contents::ClipCoverage{
148+
.type = Contents::ClipCoverage::Type::kAppend,
149+
.coverage = Rect::MakeLTRB(50, 50, 55, 55),
150+
},
151+
entity, 0, Point(0, 0));
173152

174153
ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u);
175154
EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
@@ -184,16 +163,12 @@ TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) {
184163
Rect::MakeLTRB(50, 50, 55, 55));
185164

186165
entity.SetClipDepth(1);
187-
{
188-
EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
189-
Contents::ClipCoverage{
190-
.type = Contents::ClipCoverage::Type::kAppend,
191-
.coverage = Rect::MakeLTRB(54, 54, 55, 55),
192-
},
193-
entity, 0, Point(0, 0));
194-
EXPECT_TRUE(result.should_render);
195-
EXPECT_TRUE(result.clip_did_change);
196-
}
166+
recorder.AppendClipCoverage(
167+
Contents::ClipCoverage{
168+
.type = Contents::ClipCoverage::Type::kAppend,
169+
.coverage = Rect::MakeLTRB(54, 54, 55, 55),
170+
},
171+
entity, 0, Point(0, 0));
197172

198173
EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
199174
Rect::MakeLTRB(54, 54, 55, 55));

0 commit comments

Comments
 (0)