[DisplayList] Disable group opacity when a RuntimeEffect is in use (flutter/engine#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.
This commit is contained in:
Jim Graham 2024-12-05 13:39:50 -08:00 committed by GitHub
parent 72d1c2fea6
commit 1c2cd45f88
5 changed files with 187 additions and 8 deletions

View File

@ -1367,7 +1367,7 @@ TEST_F(DisplayListTest, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) {
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 @@ TEST_F(DisplayListTest, SaveLayerBoundsSnapshotsImageFilter) {
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 @@ TEST_F(DisplayListTest, RTreeOfSaveLayerFilterScene) {
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 @@ TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) {
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 @@ TEST_F(DisplayListTest, TransformResetSaveLayerBoundsComputationOfSimpleRect) {
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 @@ TEST_F(DisplayListTest, MaxBlendModeInsideComplexSaveLayers) {
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 @@ TEST_F(DisplayListTest, BackdropDetectionSimpleSaveLayer) {
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 @@ TEST_F(DisplayListTest, RecordSingleLargeDisplayListOperation) {
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

View File

@ -244,6 +244,7 @@ void DisplayListBuilder::onSetColorSource(const DlColorSource* source) {
}
}
}
UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
if (filter == nullptr) {
@ -289,6 +290,7 @@ void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
}
}
}
UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) {
if (filter == nullptr) {

View File

@ -723,6 +723,7 @@ class DisplayListBuilder final : public virtual DlCanvas,
current_opacity_compatibility_ = //
current_.getColorFilter() == nullptr && //
!current_.isInvertColors() && //
!current_.usesRuntimeEffect() && //
IsOpacityCompatible(current_.getBlendMode());
}

View File

@ -178,6 +178,11 @@ class DlPaint {
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); }

View File

@ -154,5 +154,49 @@ TEST(DisplayListPaint, ChainingConstructor) {
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