Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit a760ce7

Browse files
committed
tests to verify saveLayer attribute commutability assumptions
1 parent 065cd41 commit a760ce7

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed

display_list/display_list_canvas_unittests.cc

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,7 @@ class CanvasCompareTester {
20402040
const uint32_t* test_row = test_pixels->addr32(0, y);
20412041
for (int x = 0; x < test_pixels->width(); x++) {
20422042
if (ref_row[x] != test_row[x]) {
2043+
// FML_LOG(ERROR) << std::hex << ref_row[x] << " != " << test_row[x];
20432044
pixels_different++;
20442045
}
20452046
}
@@ -3238,5 +3239,175 @@ TEST_F(DisplayListCanvas, DrawShadowDpr) {
32383239
CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3));
32393240
}
32403241

3242+
TEST_F(DisplayListCanvas, SaveLayerConsolidation) {
3243+
float commutable_color_matrix[]{
3244+
// clang-format off
3245+
0, 1, 0, 0, 0,
3246+
0, 0, 1, 0, 0,
3247+
1, 0, 0, 0, 0,
3248+
0, 0, 0, 1, 0,
3249+
// clang-format on
3250+
};
3251+
float non_commutable_color_matrix[]{
3252+
// clang-format off
3253+
0, 1, 0, .1, 0,
3254+
0, 0, 1, .1, 0,
3255+
1, 0, 0, .1, 0,
3256+
0, 0, 0, .7, 0,
3257+
// clang-format on
3258+
};
3259+
SkMatrix contract_matrix;
3260+
contract_matrix.setScale(0.9f, 0.9f, kRenderCenterX, kRenderCenterY);
3261+
3262+
std::vector<SkScalar> opacities = {
3263+
0,
3264+
0.5f,
3265+
SK_Scalar1,
3266+
};
3267+
std::vector<std::shared_ptr<DlColorFilter>> color_filters = {
3268+
std::make_shared<DlBlendColorFilter>(DlColor::kCyan(),
3269+
DlBlendMode::kSrcATop),
3270+
std::make_shared<DlMatrixColorFilter>(commutable_color_matrix),
3271+
std::make_shared<DlMatrixColorFilter>(non_commutable_color_matrix),
3272+
DlSrgbToLinearGammaColorFilter::instance,
3273+
DlLinearToSrgbGammaColorFilter::instance,
3274+
};
3275+
std::vector<std::shared_ptr<DlImageFilter>> image_filters = {
3276+
std::make_shared<DlBlurImageFilter>(5.0f, 5.0f, DlTileMode::kDecal),
3277+
std::make_shared<DlDilateImageFilter>(5.0f, 5.0f),
3278+
std::make_shared<DlErodeImageFilter>(5.0f, 5.0f),
3279+
std::make_shared<DlMatrixImageFilter>(contract_matrix,
3280+
DlImageSampling::kLinear),
3281+
};
3282+
RenderEnvironment env = RenderEnvironment::MakeN32();
3283+
3284+
auto render_content = [](DisplayListBuilder& builder) {
3285+
builder.drawRect(
3286+
SkRect{kRenderLeft, kRenderTop, kRenderCenterX, kRenderCenterY},
3287+
DlPaint().setColor(DlColor::kYellow()));
3288+
builder.drawRect(
3289+
SkRect{kRenderCenterX, kRenderTop, kRenderRight, kRenderCenterY},
3290+
DlPaint().setColor(DlColor::kRed()));
3291+
builder.drawRect(
3292+
SkRect{kRenderLeft, kRenderCenterY, kRenderCenterX, kRenderBottom},
3293+
DlPaint().setColor(DlColor::kBlue()));
3294+
builder.drawRect(
3295+
SkRect{kRenderCenterX, kRenderCenterY, kRenderRight, kRenderBottom},
3296+
DlPaint().setColor(DlColor::kRed().modulateOpacity(0.5f)));
3297+
};
3298+
3299+
// clang-format off
3300+
auto test_attributes = [&env, render_content]
3301+
(DlPaint& paint1, DlPaint& paint2, DlPaint paint_both,
3302+
bool same, bool rev_same, std::string desc1, std::string desc2) {
3303+
// clang-format on
3304+
DisplayListBuilder nested_builder;
3305+
nested_builder.saveLayer(&kTestBounds, &paint1);
3306+
nested_builder.saveLayer(&kTestBounds, &paint2);
3307+
render_content(nested_builder);
3308+
auto nested_surface = env.MakeSurface();
3309+
nested_builder.Build()->RenderTo(nested_surface->canvas());
3310+
3311+
DisplayListBuilder reverse_builder;
3312+
reverse_builder.saveLayer(&kTestBounds, &paint2);
3313+
reverse_builder.saveLayer(&kTestBounds, &paint1);
3314+
render_content(reverse_builder);
3315+
auto reverse_surface = env.MakeSurface();
3316+
reverse_builder.Build()->RenderTo(reverse_surface->canvas());
3317+
3318+
DisplayListBuilder combined_builder;
3319+
combined_builder.saveLayer(&kTestBounds, &paint_both);
3320+
render_content(combined_builder);
3321+
auto combined_surface = env.MakeSurface();
3322+
combined_builder.Build()->RenderTo(combined_surface->canvas());
3323+
3324+
// Set this boolean to true to test if combinations that are marked
3325+
// as incompatible actually are compatible despite our predictions.
3326+
// Some of the combinations that we treat as incompatible actually
3327+
// are compatible with swapping the order of the operations, but
3328+
// it would take a bit of new infrastructure to really identify
3329+
// those combinations. The only hard constraint to test here is
3330+
// when we claim that they are compatible and they aren't.
3331+
const bool always = false;
3332+
3333+
if (always || same) {
3334+
CanvasCompareTester::quickCompareToReference(
3335+
nested_surface->pixmap(), combined_surface->pixmap(), same,
3336+
"nested " + desc1 + " then " + desc2);
3337+
}
3338+
if (always || rev_same) {
3339+
CanvasCompareTester::quickCompareToReference(
3340+
reverse_surface->pixmap(), combined_surface->pixmap(), rev_same,
3341+
"nested " + desc2 + " then " + desc1);
3342+
}
3343+
};
3344+
3345+
// CF then Opacity should always work.
3346+
// The reverse sometimes works.
3347+
for (size_t cfi = 0; cfi < color_filters.size(); cfi++) {
3348+
auto color_filter = color_filters[cfi];
3349+
std::string cf_desc = "color filter #" + std::to_string(cfi + 1);
3350+
DlPaint nested_paint1 = DlPaint().setColorFilter(color_filter);
3351+
3352+
for (size_t oi = 0; oi < opacities.size(); oi++) {
3353+
SkScalar opacity = opacities[oi];
3354+
std::string op_desc = "opacity " + std::to_string(opacity);
3355+
DlPaint nested_paint2 = DlPaint().setOpacity(opacity);
3356+
3357+
DlPaint combined_paint = nested_paint1;
3358+
combined_paint.setOpacity(opacity);
3359+
3360+
bool op_then_cf_works = opacity <= 0.0 || opacity >= 1.0 ||
3361+
color_filter->can_commute_with_opacity();
3362+
3363+
test_attributes(nested_paint1, nested_paint2, combined_paint, true,
3364+
op_then_cf_works, cf_desc, op_desc);
3365+
}
3366+
}
3367+
3368+
// Opacity then IF should always work.
3369+
// The reverse can also work for some values of opacity.
3370+
// The reverse should also theoretically work for some IFs, but we
3371+
// get some rounding errors that are more than just trivial.
3372+
for (size_t oi = 0; oi < opacities.size(); oi++) {
3373+
SkScalar opacity = opacities[oi];
3374+
std::string op_desc = "opacity " + std::to_string(opacity);
3375+
DlPaint nested_paint1 = DlPaint().setOpacity(opacity);
3376+
3377+
for (size_t ifi = 0; ifi < image_filters.size(); ifi++) {
3378+
auto image_filter = image_filters[ifi];
3379+
std::string if_desc = "image filter #" + std::to_string(ifi + 1);
3380+
DlPaint nested_paint2 = DlPaint().setImageFilter(image_filter);
3381+
3382+
DlPaint combined_paint = nested_paint1;
3383+
combined_paint.setImageFilter(image_filter);
3384+
3385+
bool if_then_op_works = opacity <= 0.0 || opacity >= 1.0;
3386+
test_attributes(nested_paint1, nested_paint2, combined_paint, true,
3387+
if_then_op_works, op_desc, if_desc);
3388+
}
3389+
}
3390+
3391+
// CF then IF should always work.
3392+
// The reverse might work, but we lack the infrastructure to check it.
3393+
for (size_t cfi = 0; cfi < color_filters.size(); cfi++) {
3394+
auto color_filter = color_filters[cfi];
3395+
std::string cf_desc = "color filter #" + std::to_string(cfi + 1);
3396+
DlPaint nested_paint1 = DlPaint().setColorFilter(color_filter);
3397+
3398+
for (size_t ifi = 0; ifi < image_filters.size(); ifi++) {
3399+
auto image_filter = image_filters[ifi];
3400+
std::string if_desc = "image filter #" + std::to_string(ifi + 1);
3401+
DlPaint nested_paint2 = DlPaint().setImageFilter(image_filter);
3402+
3403+
DlPaint combined_paint = nested_paint1;
3404+
combined_paint.setImageFilter(image_filter);
3405+
3406+
test_attributes(nested_paint1, nested_paint2, combined_paint, true, false,
3407+
cf_desc, if_desc);
3408+
}
3409+
}
3410+
}
3411+
32413412
} // namespace testing
32423413
} // namespace flutter

0 commit comments

Comments
 (0)