diff --git a/flow/layers/image_filter_layer.cc b/flow/layers/image_filter_layer.cc index 4df19eb66416b..212c396b6efbf 100644 --- a/flow/layers/image_filter_layer.cc +++ b/flow/layers/image_filter_layer.cc @@ -31,6 +31,9 @@ void ImageFilterLayer::Preroll(PrerollContext* context, if (!context->has_platform_view && context->raster_cache && SkRect::Intersects(context->cull_rect, paint_bounds())) { SkMatrix ctm = matrix; +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + ctm = RasterCache::GetIntegralTransCTM(ctm); +#endif context->raster_cache->Prepare(context, this, ctm); } } @@ -39,6 +42,12 @@ void ImageFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ImageFilterLayer::Paint"); FML_DCHECK(needs_painting()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); + context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + if (context.raster_cache) { const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); RasterCacheResult layer_cache = diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index 305ec01611068..f4f621a529a08 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -60,11 +60,14 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ + MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, + 1, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 2}}, MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, + 2, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -93,11 +96,14 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ + MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, + 1, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 2}}, MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, + 2, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -126,11 +132,14 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ + MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 1}}, + 1, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 2}}, MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, child_paint}}, + 2, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -168,16 +177,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( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{children_bounds, filter_paint, + nullptr, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ImageFilterLayerTest, Nested) { @@ -225,16 +237,22 @@ TEST_F(ImageFilterLayerTest, Nested) { layer1->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ + MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, filter_paint1, - nullptr, 1}}, + 1, MockCanvas::SaveLayerData{children_bounds, filter_paint1, + nullptr, 2}}, MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + 2, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, + MockCanvas::DrawCall{3, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), - filter_paint2, nullptr, 2}}, + 3, MockCanvas::SaveLayerData{child_path2.getBounds(), + filter_paint2, nullptr, 4}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + 4, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{4, MockCanvas::RestoreData{3}}, + MockCanvas::DrawCall{3, MockCanvas::RestoreData{2}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 51fcc6c507fcb..7899c31784b89 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -51,6 +51,9 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { if (!context->has_platform_view && context->raster_cache && SkRect::Intersects(context->cull_rect, paint_bounds())) { SkMatrix ctm = child_matrix; +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + ctm = RasterCache::GetIntegralTransCTM(ctm); +#endif context->raster_cache->Prepare(context, container, ctm); } } @@ -66,6 +69,11 @@ void OpacityLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + if (context.raster_cache) { ContainerLayer* container = GetChildContainer(); const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); @@ -79,7 +87,8 @@ void OpacityLayer::Paint(PaintContext& context) const { // Skia may clip the content with saveLayerBounds (although it's not a // guaranteed clip). So we have to provide a big enough saveLayerBounds. To do // so, we first remove the offset from paint bounds since it's already in the - // matrix. Then we round out the bounds. + // matrix. Then we round out the bounds because of our + // RasterCache::GetIntegralTransCTM optimization. // // Note that the following lines are only accessible when the raster cache is // not available (e.g., when we're using the software backend in golden diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index ae08109fa25cf..f7cc77ffe2ddd 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -58,6 +58,10 @@ TEST_F(OpacityLayerTest, FullyOpaque) { const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); const SkMatrix layer_transform = SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif const SkPaint child_paint = SkPaint(SkColors::kGreen); const SkRect expected_layer_bounds = layer_transform.mapRect(child_path.getBounds()); @@ -82,6 +86,10 @@ TEST_F(OpacityLayerTest, FullyOpaque) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, 2}}, @@ -99,6 +107,10 @@ TEST_F(OpacityLayerTest, FullyTransparent) { const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); const SkMatrix layer_transform = SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif const SkPaint child_paint = SkPaint(SkColors::kGreen); const SkRect expected_layer_bounds = layer_transform.mapRect(child_path.getBounds()); @@ -121,6 +133,10 @@ TEST_F(OpacityLayerTest, FullyTransparent) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, MockCanvas::DrawCall{ 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, @@ -139,6 +155,10 @@ TEST_F(OpacityLayerTest, HalfTransparent) { const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); const SkMatrix layer_transform = SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer_transform)); +#endif const SkPaint child_paint = SkPaint(SkColors::kGreen); const SkRect expected_layer_bounds = layer_transform.mapRect(child_path.getBounds()); @@ -165,6 +185,10 @@ TEST_F(OpacityLayerTest, HalfTransparent) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer_transform}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, 2}}, @@ -187,6 +211,13 @@ TEST_F(OpacityLayerTest, Nested) { SkMatrix::MakeTrans(layer1_offset.fX, layer1_offset.fY); const SkMatrix layer2_transform = SkMatrix::MakeTrans(layer2_offset.fX, layer2_offset.fY); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + const SkMatrix integral_layer1_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(initial_transform, layer1_transform)); + const SkMatrix integral_layer2_transform = RasterCache::GetIntegralTransCTM( + SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), + layer2_transform)); +#endif const SkPaint child1_paint = SkPaint(SkColors::kRed); const SkPaint child2_paint = SkPaint(SkColors::kBlue); const SkPaint child3_paint = SkPaint(SkColors::kGreen); @@ -247,6 +278,10 @@ TEST_F(OpacityLayerTest, Nested) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{integral_layer1_transform}}, +#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint, nullptr, 2}}, @@ -254,6 +289,10 @@ TEST_F(OpacityLayerTest, Nested) { 2, MockCanvas::DrawPathData{child1_path, child1_paint}}, MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 3, MockCanvas::SetMatrixData{integral_layer2_transform}}, +#endif MockCanvas::DrawCall{ 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint, nullptr, 4}}, diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 5a615168b1de5..08c09cc9e833b 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -26,6 +26,9 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix ctm = matrix; ctm.postTranslate(offset_.x(), offset_.y()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + ctm = RasterCache::GetIntegralTransCTM(ctm); +#endif cache->Prepare(context->gr_context, sk_picture, ctm, context->dst_color_space, is_complex_, will_change_); } @@ -41,6 +44,10 @@ void PictureLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif if (context.raster_cache) { const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 0cdb2f94f4542..4f565cf500ecc 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -11,6 +11,10 @@ #include "flutter/testing/mock_canvas.h" #include "third_party/skia/include/core/SkPicture.h" +#ifndef SUPPORT_FRACTIONAL_TRANSLATION +#include "flutter/flow/raster_cache.h" +#endif + namespace flutter { namespace testing { @@ -81,11 +85,16 @@ TEST_F(PictureLayerTest, SimplePicture) { EXPECT_FALSE(layer->needs_system_composite()); layer->Paint(paint_context()); - auto expected_draw_calls = - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer_offset_matrix}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{1, + MockCanvas::ConcatMatrixData{layer_offset_matrix}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( + layer_offset_matrix)}}, +#endif + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 2cbe07db5c6ba..b51382a5c8d2d 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -62,6 +62,13 @@ class RasterCache { return bounds; } + static SkMatrix GetIntegralTransCTM(const SkMatrix& ctm) { + SkMatrix result = ctm; + result[SkMatrix::kMTransX] = SkScalarRoundToScalar(ctm.getTranslateX()); + result[SkMatrix::kMTransY] = SkScalarRoundToScalar(ctm.getTranslateY()); + return result; + } + // Return true if the cache is generated. // // We may return false and not generate the cache if diff --git a/flow/raster_cache_key.h b/flow/raster_cache_key.h index f5fd1e0ad1edb..5eae4d3dccacb 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -15,8 +15,11 @@ template class RasterCacheKey { public: RasterCacheKey(ID id, const SkMatrix& ctm) : id_(id), matrix_(ctm) { - matrix_[SkMatrix::kMTransX] = 0; - matrix_[SkMatrix::kMTransY] = 0; + matrix_[SkMatrix::kMTransX] = SkScalarFraction(ctm.getTranslateX()); + matrix_[SkMatrix::kMTransY] = SkScalarFraction(ctm.getTranslateY()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + FML_DCHECK(matrix_.getTranslateX() == 0 && matrix_.getTranslateY() == 0); +#endif } ID id() const { return id_; }