From 12613fdb211b7f936f64f5f7d2d6603ec2f539cc Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 13 Jun 2022 19:43:03 +0200 Subject: [PATCH] All layers with raster cache should use integer CTM (flutter/engine#33981) --- .../checkerboard_layertree_unittests.cc | 9 + .../flow/layers/clip_path_layer_unittests.cc | 5 + .../flow/layers/clip_rect_layer_unittests.cc | 5 + .../flow/layers/clip_rrect_layer_unittests.cc | 5 + .../flutter/flow/layers/clip_shape_layer.h | 17 +- .../flutter/flow/layers/color_filter_layer.cc | 19 ++- .../layers/color_filter_layer_unittests.cc | 104 +++++++----- .../flutter/flow/layers/image_filter_layer.cc | 19 ++- .../layers/image_filter_layer_unittests.cc | 48 ++++-- .../flutter/flow/layers/shader_mask_layer.cc | 17 +- .../layers/shader_mask_layer_unittests.cc | 157 ++++++++++-------- 11 files changed, 270 insertions(+), 135 deletions(-) diff --git a/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc b/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc index 1923b772783..354eb120c84 100644 --- a/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc +++ b/engine/src/flutter/flow/layers/checkerboard_layertree_unittests.cc @@ -59,6 +59,9 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { MockCanvas::DrawCall{ 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{layer->paint_bounds(), clip_paint, nullptr, 2}}, @@ -150,6 +153,9 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerNotCheckBoard) { MockCanvas::DrawCall{ 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{child_bounds, clip_paint, nullptr, 2}}, @@ -232,6 +238,9 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerNotCheckBoard) { MockCanvas::DrawCall{ 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, MockCanvas::kSoft_ClipEdgeStyle}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{child_bounds, clip_paint, nullptr, 2}}, diff --git a/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc index 15ba6ecdb8c..613a684136e 100644 --- a/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_path_layer_unittests.cc @@ -479,6 +479,11 @@ TEST_F(ClipPathLayerTest, OpacityInheritanceSaveLayerPainting) { /* ClipRectLayer::Paint() */ { expected_builder.save(); expected_builder.clipPath(layer_clip, SkClipOp::kIntersect, true); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + /* ClipShapeLayer::Paint() Integer CTM */ + expected_builder.transformReset(); + expected_builder.transform(opacity_integer_transform); +#endif expected_builder.setColor(opacity_alpha << 24); expected_builder.saveLayer(&children_bounds, true); /* child layer1 paint */ { diff --git a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc index 64e7ce9ea04..f6c61dc56ef 100644 --- a/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rect_layer_unittests.cc @@ -469,6 +469,11 @@ TEST_F(ClipRectLayerTest, OpacityInheritanceSaveLayerPainting) { /* ClipRectLayer::Paint() */ { expected_builder.save(); expected_builder.clipRect(clip_rect, SkClipOp::kIntersect, true); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + /* ClipShapeLayer::Paint() Integer CTM */ + expected_builder.transformReset(); + expected_builder.transform(opacity_integer_transform); +#endif expected_builder.setColor(opacity_alpha << 24); expected_builder.saveLayer(&children_bounds, true); /* child layer1 paint */ { diff --git a/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc b/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc index 3ca52e2e6c0..06c484cd551 100644 --- a/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/clip_rrect_layer_unittests.cc @@ -479,6 +479,11 @@ TEST_F(ClipRRectLayerTest, OpacityInheritanceSaveLayerPainting) { /* ClipRectLayer::Paint() */ { expected_builder.save(); expected_builder.clipRRect(clip_r_rect, SkClipOp::kIntersect, true); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + /* ClipShapeLayer::Paint() Integer CTM */ + expected_builder.transformReset(); + expected_builder.transform(opacity_integer_transform); +#endif expected_builder.setColor(opacity_alpha << 24); expected_builder.saveLayer(&children_bounds, true); /* child layer1 paint */ { diff --git a/engine/src/flutter/flow/layers/clip_shape_layer.h b/engine/src/flutter/flow/layers/clip_shape_layer.h index 08489b4e2ea..6f310022882 100644 --- a/engine/src/flutter/flow/layers/clip_shape_layer.h +++ b/engine/src/flutter/flow/layers/clip_shape_layer.h @@ -31,6 +31,12 @@ class ClipShapeLayer : public ContainerLayer { context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); } } +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + if (UsesSaveLayer()) { + context->SetTransform( + RasterCache::GetIntegralTransCTM(context->GetTransform())); + } +#endif if (context->PushCullRect(clip_shape_bounds())) { DiffChildren(context, prev); } @@ -61,7 +67,11 @@ class ClipShapeLayer : public ContainerLayer { if (UsesSaveLayer()) { context->subtree_can_inherit_opacity = true; if (render_count_ >= kMinimumRendersBeforeCachingLayer) { - TryToPrepareRasterCache(context, this, matrix, + SkMatrix child_matrix(matrix); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); +#endif + TryToPrepareRasterCache(context, this, child_matrix, RasterCacheLayerStrategy::kLayer); } else { render_count_++; @@ -83,6 +93,11 @@ class ClipShapeLayer : public ContainerLayer { return; } +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + AutoCachePaint cache_paint(context); if (context.raster_cache && context.raster_cache->Draw(this, *context.leaf_nodes_canvas, diff --git a/engine/src/flutter/flow/layers/color_filter_layer.cc b/engine/src/flutter/flow/layers/color_filter_layer.cc index 17de94b4bd6..364fbbd7e68 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer.cc @@ -19,6 +19,11 @@ void ColorFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { } } +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context->SetTransform( + RasterCache::GetIntegralTransCTM(context->GetTransform())); +#endif + DiffChildren(context, prev); context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); @@ -34,12 +39,17 @@ void ColorFilterLayer::Preroll(PrerollContext* context, // can always apply opacity in those cases. context->subtree_can_inherit_opacity = true; + SkMatrix child_matrix(matrix); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); +#endif + if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { - TryToPrepareRasterCache(context, this, matrix, + TryToPrepareRasterCache(context, this, child_matrix, RasterCacheLayerStrategy::kLayer); } else { render_count_++; - TryToPrepareRasterCache(context, this, matrix, + TryToPrepareRasterCache(context, this, child_matrix, RasterCacheLayerStrategy::kLayerChildren); } } @@ -50,6 +60,11 @@ void ColorFilterLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + if (context.raster_cache) { if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, RasterCacheLayerStrategy::kLayer, diff --git a/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc b/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc index 7090e57e9b2..278d3e4b2c6 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer_unittests.cc @@ -63,14 +63,17 @@ TEST_F(ColorFilterLayerTest, EmptyFilter) { SkPaint filter_paint; filter_paint.setColorFilter(nullptr); layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ColorFilterLayerTest, SimpleFilter) { @@ -93,14 +96,17 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) { SkPaint filter_paint; filter_paint.setColorFilter(layer_filter); layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ColorFilterLayerTest, MultipleChildren) { @@ -135,16 +141,19 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) { SkPaint filter_paint; filter_paint.setColorFilter(layer_filter); layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, - filter_paint, nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ColorFilterLayerTest, Nested) { @@ -187,20 +196,26 @@ TEST_F(ColorFilterLayerTest, Nested) { filter_paint1.setColorFilter(layer_filter1); filter_paint2.setColorFilter(layer_filter2); layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, - filter_paint1, nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), - filter_paint2, nullptr, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, filter_paint1, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), + filter_paint2, nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ColorFilterLayerTest, Readback) { @@ -356,19 +371,22 @@ TEST_F(ColorFilterLayerTest, OpacityInheritance) { SkMatrix::Translate(offset.fX, offset.fY)); #endif DisplayListBuilder expected_builder; - /* opacity_layer::Paint() */ { + /* OpacityLayer::Paint() */ { expected_builder.save(); { expected_builder.translate(offset.fX, offset.fY); #ifndef SUPPORT_FRACTIONAL_TRANSLATION expected_builder.transformReset(); expected_builder.transform(opacity_integer_transform); + /* Integer CTM in ColorFilterLayer::Paint() */ + expected_builder.transformReset(); + expected_builder.transform(opacity_integer_transform); #endif - /* image_filter_layer::Paint() */ { + /* ColorFilterLayer::Paint() */ { expected_builder.setColor(opacity_alpha << 24); expected_builder.setColorFilter(&layer_filter); expected_builder.saveLayer(&child_path.getBounds(), true); - /* mock_layer::Paint() */ { + /* MockLayer::Paint() */ { expected_builder.setColor(0xFF000000); expected_builder.setColorFilter(nullptr); expected_builder.drawPath(child_path); diff --git a/engine/src/flutter/flow/layers/image_filter_layer.cc b/engine/src/flutter/flow/layers/image_filter_layer.cc index e5158b73b59..4a2a707063e 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer.cc @@ -21,6 +21,11 @@ void ImageFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { } } +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context->SetTransform( + RasterCache::GetIntegralTransCTM(context->GetTransform())); +#endif + if (filter_) { auto filter = filter_->makeWithLocalMatrix(context->GetTransform()); if (filter) { @@ -62,13 +67,18 @@ void ImageFilterLayer::Preroll(PrerollContext* context, set_paint_bounds(child_bounds); + SkMatrix child_matrix(matrix); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); +#endif + transformed_filter_ = nullptr; if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { // We have rendered this same ImageFilterLayer object enough // times to consider its properties and children to be stable // from frame to frame so we try to cache the layer itself // for maximum performance. - TryToPrepareRasterCache(context, this, matrix, + TryToPrepareRasterCache(context, this, child_matrix, RasterCacheLayerStrategy::kLayer); } else { // This ImageFilterLayer is not yet considered stable so we @@ -83,7 +93,7 @@ void ImageFilterLayer::Preroll(PrerollContext* context, // instances can do this operation on some transforms and some // (filters or transforms) cannot. We can only cache the children // and apply the filter on the fly if this operation succeeds. - transformed_filter_ = filter_->makeWithLocalMatrix(matrix); + transformed_filter_ = filter_->makeWithLocalMatrix(child_matrix); if (transformed_filter_) { // With a modified SkImageFilter we can now try to cache the // children to avoid their rendering costs if they remain @@ -102,6 +112,11 @@ void ImageFilterLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + if (context.raster_cache) { if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas, RasterCacheLayerStrategy::kLayer, diff --git a/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc b/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc index bd6c4295ff7..835ab257f3d 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer_unittests.cc @@ -63,6 +63,9 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, nullptr, 1}}, @@ -98,6 +101,9 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, nullptr, 1}}, @@ -133,6 +139,9 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, nullptr, 1}}, @@ -177,16 +186,19 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { SkPaint filter_paint; filter_paint.setImageFilter(layer_filter); layer->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, - filter_paint, nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, filter_paint, + nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ImageFilterLayerTest, Nested) { @@ -238,11 +250,18 @@ TEST_F(ImageFilterLayerTest, Nested) { layer1->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ 0, MockCanvas::SaveLayerData{children_bounds, filter_paint1, nullptr, 1}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{child_path2.getBounds(), filter_paint2, nullptr, 2}}, @@ -407,19 +426,22 @@ TEST_F(ImageFilterLayerTest, OpacityInheritance) { #endif auto dl_image_filter = DlImageFilter::From(layer_filter); DisplayListBuilder expected_builder; - /* opacity_layer::Paint() */ { + /* OpacityLayer::Paint() */ { expected_builder.save(); { expected_builder.translate(offset.fX, offset.fY); #ifndef SUPPORT_FRACTIONAL_TRANSLATION expected_builder.transformReset(); expected_builder.transform(opacity_integer_transform); + /* Integer CTM in ImageFilterLayer::Paint() */ + expected_builder.transformReset(); + expected_builder.transform(opacity_integer_transform); #endif - /* image_filter_layer::Paint() */ { + /* ImageFilterLayer::Paint() */ { expected_builder.setColor(opacity_alpha << 24); expected_builder.setImageFilter(dl_image_filter.get()); expected_builder.saveLayer(&child_path.getBounds(), true); - /* mock_layer::Paint() */ { + /* MockLayer::Paint() */ { expected_builder.setColor(child_paint.getColor()); expected_builder.setImageFilter(nullptr); expected_builder.drawPath(child_path); diff --git a/engine/src/flutter/flow/layers/shader_mask_layer.cc b/engine/src/flutter/flow/layers/shader_mask_layer.cc index c4289873830..2f0518606ac 100644 --- a/engine/src/flutter/flow/layers/shader_mask_layer.cc +++ b/engine/src/flutter/flow/layers/shader_mask_layer.cc @@ -25,6 +25,11 @@ void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { } } +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context->SetTransform( + RasterCache::GetIntegralTransCTM(context->GetTransform())); +#endif + DiffChildren(context, prev); context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); @@ -39,8 +44,13 @@ void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { // so we can always apply opacity in any of those cases. context->subtree_can_inherit_opacity = true; + SkMatrix child_matrix(matrix); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); +#endif + if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) { - TryToPrepareRasterCache(context, this, matrix, + TryToPrepareRasterCache(context, this, child_matrix, RasterCacheLayerStrategy::kLayer); } else { render_count_++; @@ -53,6 +63,11 @@ void ShaderMaskLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + if (context.raster_cache && context.raster_cache->Draw(this, *context.leaf_nodes_canvas, RasterCacheLayerStrategy::kLayer, diff --git a/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc b/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc index eebc9cd6848..4327c423acf 100644 --- a/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/shader_mask_layer_unittests.cc @@ -71,20 +71,22 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) { layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkM44::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawRectData{SkRect::MakeWH( - layer_bounds.width(), - layer_bounds.height()), - filter_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, + MockCanvas::SaveLayerData{child_bounds, SkPaint(), nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH(layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ShaderMaskLayerTest, SimpleFilter) { @@ -112,20 +114,22 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) { layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkM44::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawRectData{SkRect::MakeWH( - layer_bounds.width(), - layer_bounds.height()), - filter_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, + MockCanvas::SaveLayerData{child_bounds, SkPaint(), nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH(layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ShaderMaskLayerTest, MultipleChildren) { @@ -165,22 +169,24 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) { layer->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), - nullptr, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkM44::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawRectData{SkRect::MakeWH( - layer_bounds.width(), - layer_bounds.height()), - filter_paint}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, + 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH(layer_bounds.width(), + layer_bounds.height()), + filter_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ShaderMaskLayerTest, Nested) { @@ -230,35 +236,37 @@ TEST_F(ShaderMaskLayerTest, Nested) { layer1->Paint(paint_context()); EXPECT_EQ( mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, - 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(), - nullptr, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{2, - MockCanvas::ConcatMatrixData{SkM44::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 2, - MockCanvas::DrawRectData{ - SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), - filter_paint2}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, - MockCanvas::ConcatMatrixData{SkM44::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, - MockCanvas::DrawCall{ - 1, - MockCanvas::DrawRectData{ - SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), - filter_paint1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + std::vector({ +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{0, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, + 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(), + nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawRectData{SkRect::MakeWH(layer_bounds.width(), + layer_bounds.height()), + filter_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{SkRect::MakeWH(layer_bounds.width(), + layer_bounds.height()), + filter_paint1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ShaderMaskLayerTest, Readback) { @@ -354,6 +362,9 @@ TEST_F(ShaderMaskLayerTest, OpacityInheritance) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION expected_builder.transformReset(); expected_builder.transform(opacity_integer_transform); + /* Integer CTM in ShaderMaskLayer::Paint() */ + expected_builder.transformReset(); + expected_builder.transform(opacity_integer_transform); #endif /* ShaderMaskLayer::Paint() */ { expected_builder.setColor(opacity_alpha << 24);