[Impeller] Skip clip entity replay that cannot impact current clip. (#162113)

Fixes https://github.com/flutter/flutter/issues/161262

Sometimes we end up with clip replay entities that have clip depth
values substantially below the current depth. I suspect this is due to
either mismatched save/restore or a bug in our code. Update: this isn't
a bug/bug but its definitely a bug. We can have multiple clips per save,
but the restore will remove at most one from the record/replay.


If a clip has a depth value that is less than the current clip depth, it
cannot by definition impact anything that draws after it.
This commit is contained in:
Jonah Williams 2025-01-24 11:49:40 -08:00 committed by GitHub
parent 74ade43341
commit b82e9022a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 38 additions and 0 deletions

View File

@ -1592,5 +1592,33 @@ TEST_P(AiksTest, NoDimplesInRRectPath) {
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, BackdropFilterOverUnclosedClip) {
DisplayListBuilder builder;
builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
builder.Save();
{
builder.ClipRect(DlRect::MakeLTRB(100, 100, 800, 800));
builder.Save();
{
builder.ClipRect(DlRect::MakeLTRB(600, 600, 800, 800));
builder.DrawPaint(DlPaint().setColor(DlColor::kRed()));
builder.DrawPaint(DlPaint().setColor(DlColor::kBlue().withAlphaF(0.5)));
builder.ClipRect(DlRect::MakeLTRB(700, 700, 750, 800));
builder.DrawPaint(DlPaint().setColor(DlColor::kRed().withAlphaF(0.5)));
}
builder.Restore();
auto image_filter = DlImageFilter::MakeBlur(10, 10, DlTileMode::kDecal);
builder.SaveLayer(std::nullopt, nullptr, image_filter.get());
}
builder.Restore();
builder.DrawCircle(SkPoint{100, 100}, 100,
DlPaint().setColor(DlColor::kAqua()));
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
} // namespace testing
} // namespace impeller

View File

@ -1662,6 +1662,10 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
// applied.
auto& replay_entities = clip_coverage_stack_.GetReplayEntities();
for (const auto& replay : replay_entities) {
if (replay.clip_depth <= current_depth_) {
continue;
}
SetClipScissor(replay.clip_coverage, current_render_pass,
global_pass_position);
if (!replay.clip_contents.Render(renderer_, current_render_pass,

View File

@ -83,6 +83,9 @@ EntityPassClipStack::ClipStateResult EntityPassClipStack::RecordRestore(
if (subpass_state.clip_coverage.back().coverage.has_value()) {
FML_DCHECK(next_replay_index_ <=
subpass_state.rendered_clip_entities.size());
// https://github.com/flutter/flutter/issues/162172
// This code is slightly wrong and should be popping more than one clip
// entry.
if (!subpass_state.rendered_clip_entities.empty()) {
subpass_state.rendered_clip_entities.pop_back();

View File

@ -3,6 +3,9 @@ impeller_GoldenTests_ConicalGradient.png
impeller_Play_AiksTest_AdvancedBlendColorFilterWithDestinationOpacity_Metal.png
impeller_Play_AiksTest_AdvancedBlendColorFilterWithDestinationOpacity_OpenGLES.png
impeller_Play_AiksTest_AdvancedBlendColorFilterWithDestinationOpacity_Vulkan.png
impeller_Play_AiksTest_BackdropFilterOverUnclosedClip_Metal.png
impeller_Play_AiksTest_BackdropFilterOverUnclosedClip_OpenGLES.png
impeller_Play_AiksTest_BackdropFilterOverUnclosedClip_Vulkan.png
impeller_Play_AiksTest_BackdropRestoreUsesCorrectCoverageForFirstRestoredClip_Metal.png
impeller_Play_AiksTest_BackdropRestoreUsesCorrectCoverageForFirstRestoredClip_OpenGLES.png
impeller_Play_AiksTest_BackdropRestoreUsesCorrectCoverageForFirstRestoredClip_Vulkan.png