[DisplayList] Disable group opacity when a RuntimeEffect is in use (#56936)
Fixes https://github.com/flutter/flutter/issues/158500
Impeller does not support group opacity for RuntimeEffects so we disable the optimization with a flag when it is detected.
diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc
index d7307d5..cfd1f6b 100644
--- a/display_list/display_list_unittests.cc
+++ b/display_list/display_list_unittests.cc
@@ -1367,7 +1367,7 @@
DisplayListBuilder builder;
// This empty draw rect will not actually be inserted into the stream,
// but the Src blend mode will be synchronized as an attribute. The
- // saveLayer following it should not use that attribute to base its
+ // SaveLayer following it should not use that attribute to base its
// decisions about group opacity and the draw rect after that comes
// with its own compatible blend mode.
builder.DrawRect(SkRect{0, 0, 0, 0},
@@ -1416,7 +1416,7 @@
DlPaint save_paint;
builder.SaveLayer(nullptr, &save_paint);
builder.DrawRect(SkRect{50, 50, 100, 100}, DlPaint());
- // This image filter should be ignored since it was not set before saveLayer
+ // This image filter should be ignored since it was not set before SaveLayer
// And the rect drawn with it will not contribute any more area to the bounds
DlPaint draw_paint;
draw_paint.setImageFilter(&kTestBlurImageFilter1);
@@ -2510,7 +2510,7 @@
builder.DrawRect(SkRect{10, 10, 20, 20}, default_paint);
builder.SaveLayer(nullptr, &filter_paint);
// the following rectangle will be expanded to 50,50,60,60
- // by the saveLayer filter during the restore operation
+ // by the SaveLayer filter during the restore operation
builder.DrawRect(SkRect{53, 53, 57, 57}, default_paint);
builder.Restore();
auto display_list = builder.Build();
@@ -3272,7 +3272,7 @@
builder.ClipRect(SkRect{50, 50, 60, 60}, ClipOp::kIntersect, false);
builder.SaveLayer(nullptr, &filter_paint);
// the following rectangle will be expanded to 23,23,87,87
- // by the saveLayer filter during the restore operation
+ // by the SaveLayer filter during the restore operation
// but it will then be clipped to 50,50,60,60
builder.DrawRect(SkRect{53, 53, 57, 57}, default_paint);
builder.Restore();
@@ -3862,7 +3862,7 @@
builder.SaveLayer(nullptr, nullptr);
builder.TransformReset();
builder.Scale(20.0f, 20.0f);
- // Net local transform for saveLayer is Scale(2, 2)
+ // Net local transform for SaveLayer is Scale(2, 2)
{ //
builder.DrawRect(rect, DlPaint());
}
@@ -4451,7 +4451,7 @@
builder.Restore();
// Double check that kModulate is the max blend mode for the first
- // saveLayer operations
+ // SaveLayer operations
auto expect = std::max(DlBlendMode::kModulate, DlBlendMode::kSrc);
ASSERT_EQ(expect, DlBlendMode::kModulate);
@@ -4487,8 +4487,8 @@
auto dl = builder.Build();
EXPECT_TRUE(dl->root_has_backdrop_filter());
- // The saveLayer itself, though, does not have the contains backdrop
- // flag set because its content does not contain a saveLayer with backdrop
+ // The SaveLayer itself, though, does not have the contains backdrop
+ // flag set because its content does not contain a SaveLayer with backdrop
SAVE_LAYER_EXPECTOR(expector);
expector.addExpectation(
SaveLayerOptions::kNoAttributes.with_can_distribute_opacity());
@@ -5948,5 +5948,132 @@
EXPECT_TRUE(!!builder.Build());
}
+TEST_F(DisplayListTest, DisplayListDetectsRuntimeEffect) {
+ const auto runtime_effect = DlRuntimeEffect::MakeSkia(
+ SkRuntimeEffect::MakeForShader(
+ SkString("vec4 main(vec2 p) { return vec4(0); }"))
+ .effect);
+ auto color_source = DlColorSource::MakeRuntimeEffect(
+ runtime_effect, {}, std::make_shared<std::vector<uint8_t>>());
+ auto image_filter = DlImageFilter::MakeRuntimeEffect(
+ runtime_effect, {}, std::make_shared<std::vector<uint8_t>>());
+
+ {
+ // Default - no runtime effects, supports group opacity
+ DisplayListBuilder builder;
+ DlPaint paint;
+
+ builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
+ EXPECT_TRUE(builder.Build()->can_apply_group_opacity());
+ }
+
+ {
+ // Draw with RTE color source does not support group opacity
+ DisplayListBuilder builder;
+ DlPaint paint;
+
+ paint.setColorSource(color_source);
+ builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
+
+ EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
+ }
+
+ {
+ // Draw with RTE image filter does not support group opacity
+ DisplayListBuilder builder;
+ DlPaint paint;
+
+ paint.setImageFilter(image_filter);
+ builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
+
+ EXPECT_FALSE(builder.Build()->can_apply_group_opacity());
+ }
+
+ {
+ // Draw with RTE color source inside SaveLayer does not support group
+ // opacity on the SaveLayer, but does support it on the DisplayList
+ DisplayListBuilder builder;
+ DlPaint paint;
+
+ builder.SaveLayer(nullptr, nullptr);
+ paint.setColorSource(color_source);
+ builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
+ builder.Restore();
+
+ auto display_list = builder.Build();
+ EXPECT_TRUE(display_list->can_apply_group_opacity());
+
+ SAVE_LAYER_EXPECTOR(expector);
+ expector.addExpectation([](const SaveLayerOptions& options) {
+ return !options.can_distribute_opacity();
+ });
+ display_list->Dispatch(expector);
+ }
+
+ {
+ // Draw with RTE image filter inside SaveLayer does not support group
+ // opacity on the SaveLayer, but does support it on the DisplayList
+ DisplayListBuilder builder;
+ DlPaint paint;
+
+ builder.SaveLayer(nullptr, nullptr);
+ paint.setImageFilter(image_filter);
+ builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
+ builder.Restore();
+
+ auto display_list = builder.Build();
+ EXPECT_TRUE(display_list->can_apply_group_opacity());
+
+ SAVE_LAYER_EXPECTOR(expector);
+ expector.addExpectation([](const SaveLayerOptions& options) {
+ return !options.can_distribute_opacity();
+ });
+ display_list->Dispatch(expector);
+ }
+
+ {
+ // Draw with RTE color source inside nested saveLayers does not support
+ // group opacity on the inner SaveLayer, but does support it on the
+ // outer SaveLayer and the DisplayList
+ DisplayListBuilder builder;
+ DlPaint paint;
+
+ builder.SaveLayer(nullptr, nullptr);
+
+ builder.SaveLayer(nullptr, nullptr);
+ paint.setColorSource(color_source);
+ builder.DrawRect(DlRect::MakeLTRB(0, 0, 50, 50), paint);
+ paint.setColorSource(nullptr);
+ builder.Restore();
+
+ builder.SaveLayer(nullptr, nullptr);
+ paint.setImageFilter(image_filter);
+ // Make sure these DrawRects are non-overlapping otherwise the outer
+ // SaveLayer and DisplayList will be incompatible due to overlaps
+ builder.DrawRect(DlRect::MakeLTRB(60, 60, 100, 100), paint);
+ paint.setImageFilter(nullptr);
+ builder.Restore();
+
+ builder.Restore();
+ auto display_list = builder.Build();
+ EXPECT_TRUE(display_list->can_apply_group_opacity());
+
+ SAVE_LAYER_EXPECTOR(expector);
+ expector.addExpectation([](const SaveLayerOptions& options) {
+ // outer SaveLayer supports group opacity
+ return options.can_distribute_opacity();
+ });
+ expector.addExpectation([](const SaveLayerOptions& options) {
+ // first inner SaveLayer does not support group opacity
+ return !options.can_distribute_opacity();
+ });
+ expector.addExpectation([](const SaveLayerOptions& options) {
+ // second inner SaveLayer does not support group opacity
+ return !options.can_distribute_opacity();
+ });
+ display_list->Dispatch(expector);
+ }
+}
+
} // namespace testing
} // namespace flutter
diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc
index 3e0e18d..682e882 100644
--- a/display_list/dl_builder.cc
+++ b/display_list/dl_builder.cc
@@ -244,6 +244,7 @@
}
}
}
+ UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
if (filter == nullptr) {
@@ -289,6 +290,7 @@
}
}
}
+ UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) {
if (filter == nullptr) {
diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h
index fb7464a..92dc9b8 100644
--- a/display_list/dl_builder.h
+++ b/display_list/dl_builder.h
@@ -723,6 +723,7 @@
current_opacity_compatibility_ = //
current_.getColorFilter() == nullptr && //
!current_.isInvertColors() && //
+ !current_.usesRuntimeEffect() && //
IsOpacityCompatible(current_.getBlendMode());
}
diff --git a/display_list/dl_paint.h b/display_list/dl_paint.h
index ca5e69a..409bd80 100644
--- a/display_list/dl_paint.h
+++ b/display_list/dl_paint.h
@@ -178,6 +178,11 @@
bool isDefault() const { return *this == kDefault; }
+ bool usesRuntimeEffect() const {
+ return ((color_source_ && color_source_->asRuntimeEffect()) ||
+ (image_filter_ && image_filter_->asRuntimeEffectFilter()));
+ }
+
bool operator==(DlPaint const& other) const;
bool operator!=(DlPaint const& other) const { return !(*this == other); }
diff --git a/display_list/dl_paint_unittests.cc b/display_list/dl_paint_unittests.cc
index e03f02e..1f76459 100644
--- a/display_list/dl_paint_unittests.cc
+++ b/display_list/dl_paint_unittests.cc
@@ -154,5 +154,49 @@
EXPECT_NE(paint, DlPaint());
}
+TEST(DisplayListPaint, PaintDetectsRuntimeEffects) {
+ const auto runtime_effect = DlRuntimeEffect::MakeSkia(
+ SkRuntimeEffect::MakeForShader(
+ SkString("vec4 main(vec2 p) { return vec4(0); }"))
+ .effect);
+ auto color_source = DlColorSource::MakeRuntimeEffect(
+ runtime_effect, {}, std::make_shared<std::vector<uint8_t>>());
+ auto image_filter = DlImageFilter::MakeRuntimeEffect(
+ runtime_effect, {}, std::make_shared<std::vector<uint8_t>>());
+ DlPaint paint;
+
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+ paint.setColorSource(color_source);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setColorSource(nullptr);
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+ paint.setImageFilter(image_filter);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setImageFilter(nullptr);
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+ paint.setColorSource(color_source);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setImageFilter(image_filter);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setImageFilter(nullptr);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setColorSource(nullptr);
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+ paint.setColorSource(color_source);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setImageFilter(image_filter);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setColorSource(nullptr);
+ EXPECT_TRUE(paint.usesRuntimeEffect());
+ paint.setImageFilter(nullptr);
+ EXPECT_FALSE(paint.usesRuntimeEffect());
+}
+
} // namespace testing
} // namespace flutter