diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 970e019ff56bf..ee4d5a3e49dc3 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -4,6 +4,7 @@ #include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list.h" #include "flutter/display_list/display_list_blend_mode.h" #include "flutter/display_list/display_list_ops.h" @@ -413,19 +414,29 @@ void DisplayListBuilder::setAttributesFromPaint( } } +void DisplayListBuilder::checkForDeferredSave() { + if (current_layer_->has_deferred_save_op_) { + Push(0, 1); + current_layer_->has_deferred_save_op_ = false; + } +} + void DisplayListBuilder::save() { - Push(0, 1); layer_stack_.emplace_back(current_layer_); current_layer_ = &layer_stack_.back(); + current_layer_->has_deferred_save_op_ = true; } + void DisplayListBuilder::restore() { if (layer_stack_.size() > 1) { + if (!current_layer_->has_deferred_save_op_) { + Push(0, 1); + } // Grab the current layer info before we push the restore // on the stack. LayerInfo layer_info = layer_stack_.back(); layer_stack_.pop_back(); current_layer_ = &layer_stack_.back(); - Push(0, 1); if (layer_info.has_layer) { if (layer_info.is_group_opacity_compatible()) { // We are now going to go back and modify the matching saveLayer @@ -504,6 +515,7 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds, void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && (tx != 0.0 || ty != 0.0)) { + checkForDeferredSave(); Push(0, 1, tx, ty); current_layer_->matrix.preTranslate(tx, ty); } @@ -511,12 +523,14 @@ void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && (sx != 1.0 || sy != 1.0)) { + checkForDeferredSave(); Push(0, 1, sx, sy); current_layer_->matrix.preScale(sx, sy); } } void DisplayListBuilder::rotate(SkScalar degrees) { if (SkScalarMod(degrees, 360.0) != 0.0) { + checkForDeferredSave(); Push(0, 1, degrees); current_layer_->matrix.preConcat(SkMatrix::RotateDeg(degrees)); } @@ -524,6 +538,7 @@ void DisplayListBuilder::rotate(SkScalar degrees) { void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && (sx != 0.0 || sy != 0.0)) { + checkForDeferredSave(); Push(0, 1, sx, sy); current_layer_->matrix.preConcat(SkMatrix::Skew(sx, sy)); } @@ -540,6 +555,7 @@ void DisplayListBuilder::transform2DAffine( SkScalarsAreFinite(mxt, myt) && !(mxx == 1 && mxy == 0 && mxt == 0 && myx == 0 && myy == 1 && myt == 0)) { + checkForDeferredSave(); Push(0, 1, mxx, mxy, mxt, myx, myy, myt); @@ -565,6 +581,7 @@ void DisplayListBuilder::transformFullPerspective( SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { + checkForDeferredSave(); Push(0, 1, mxx, mxy, mxz, mxt, myx, myy, myz, myt, @@ -578,6 +595,7 @@ void DisplayListBuilder::transformFullPerspective( } // clang-format on void DisplayListBuilder::transformReset() { + checkForDeferredSave(); Push(0, 0); current_layer_->matrix.setIdentity(); } @@ -599,6 +617,10 @@ void DisplayListBuilder::transform(const SkM44* m44) { void DisplayListBuilder::clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) { + if (!rect.isFinite()) { + return; + } + checkForDeferredSave(); switch (clip_op) { case SkClipOp::kIntersect: Push(0, 1, rect, is_aa); @@ -615,6 +637,7 @@ void DisplayListBuilder::clipRRect(const SkRRect& rrect, if (rrect.isRect()) { clipRect(rrect.rect(), clip_op, is_aa); } else { + checkForDeferredSave(); switch (clip_op) { case SkClipOp::kIntersect: Push(0, 1, rrect, is_aa); @@ -646,6 +669,7 @@ void DisplayListBuilder::clipPath(const SkPath& path, return; } } + checkForDeferredSave(); switch (clip_op) { case SkClipOp::kIntersect: Push(0, 1, path, is_aa); diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h index ce785c594b7ad..759b7280ed0f1 100644 --- a/display_list/display_list_builder.h +++ b/display_list/display_list_builder.h @@ -343,6 +343,8 @@ class DisplayListBuilder final : public virtual Dispatcher, sk_sp Build(); private: + void checkForDeferredSave(); + SkAutoTMalloc storage_; size_t used_ = 0; size_t allocated_ = 0; @@ -397,6 +399,8 @@ class DisplayListBuilder final : public virtual Dispatcher, // This offset is only valid if |has_layer| is true. size_t save_layer_offset; + bool has_deferred_save_op_ = false; + bool has_layer; bool cannot_inherit_opacity; bool has_compatible_op; diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index ea8dfc0cbee44..62155636c23de 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -3,14 +3,19 @@ // found in the LICENSE file. #include +#include +#include +#include #include "flutter/display_list/display_list.h" #include "flutter/display_list/display_list_blend_mode.h" #include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/display_list_canvas_recorder.h" +#include "flutter/display_list/display_list_paint.h" #include "flutter/display_list/display_list_rtree.h" #include "flutter/display_list/display_list_test_utils.h" #include "flutter/display_list/display_list_utils.h" +#include "flutter/fml/logging.h" #include "flutter/fml/math.h" #include "flutter/testing/display_list_testing.h" #include "flutter/testing/testing.h" @@ -1644,6 +1649,550 @@ TEST(DisplayList, RTreeOfSaveLayerFilterScene) { test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1}); } +TEST(DisplayList, RemoveUnnecessarySaveRestorePairs) { + { + DisplayListBuilder builder; + builder.drawRect({10, 10, 20, 20}); + builder.save(); // This save op is unnecessary + builder.drawRect({50, 50, 60, 60}); + builder.restore(); + + DisplayListBuilder builder2; + builder2.drawRect({10, 10, 20, 20}); + builder2.drawRect({50, 50, 60, 60}); + ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build())); + } + + { + DisplayListBuilder builder; + builder.drawRect({10, 10, 20, 20}); + builder.save(); + builder.translate(1.0, 1.0); + { + builder.save(); // unnecessary + builder.drawRect({50, 50, 60, 60}); + builder.restore(); + } + + builder.restore(); + + DisplayListBuilder builder2; + builder2.drawRect({10, 10, 20, 20}); + builder2.save(); + builder2.translate(1.0, 1.0); + { builder2.drawRect({50, 50, 60, 60}); } + builder2.restore(); + ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build())); + } +} + +TEST(DisplayList, CollapseMultipleNestedSaveRestore) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.save(); + builder1.translate(10, 10); + builder1.scale(2, 2); + builder1.clipRect({10, 10, 20, 20}, SkClipOp::kIntersect, false); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.translate(10, 10); + builder2.scale(2, 2); + builder2.clipRect({10, 10, 20, 20}, SkClipOp::kIntersect, false); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, CollapseNestedSaveAndSaveLayerRestore) { + DisplayListBuilder builder1; + builder1.save(); + builder1.saveLayer(nullptr, false); + builder1.drawRect({0, 0, 100, 100}); + builder1.scale(2, 2); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.saveLayer(nullptr, false); + builder2.drawRect({0, 0, 100, 100}); + builder2.scale(2, 2); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, RemoveUnnecessarySaveRestorePairsInSetPaint) { + SkRect build_bounds = SkRect::MakeLTRB(-100, -100, 200, 200); + SkRect rect = SkRect::MakeLTRB(30, 30, 70, 70); + // clang-format off + const float alpha_matrix[] = { + 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, + }; + // clang-format on + DlMatrixColorFilter alpha_color_filter(alpha_matrix); + // Making sure hiding a problematic ColorFilter as an ImageFilter + // will generate the same behavior as setting it as a ColorFilter + + DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter); + { + DisplayListBuilder builder(build_bounds); + builder.save(); + DlPaint paint; + paint.setImageFilter(&color_filter_image_filter); + builder.drawRect(rect, paint); + builder.restore(); + sk_sp display_list1 = builder.Build(); + + DisplayListBuilder builder2(build_bounds); + DlPaint paint2; + paint2.setImageFilter(&color_filter_image_filter); + builder2.drawRect(rect, paint2); + sk_sp display_list2 = builder2.Build(); + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); + } + + { + DisplayListBuilder builder(build_bounds); + builder.save(); + builder.saveLayer(&build_bounds, true); + DlPaint paint; + paint.setImageFilter(&color_filter_image_filter); + builder.drawRect(rect, paint); + builder.restore(); + builder.restore(); + sk_sp display_list1 = builder.Build(); + + DisplayListBuilder builder2(build_bounds); + builder2.saveLayer(&build_bounds, true); + DlPaint paint2; + paint2.setImageFilter(&color_filter_image_filter); + builder2.drawRect(rect, paint2); + builder2.restore(); + sk_sp display_list2 = builder2.Build(); + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); + } +} + +TEST(DisplayList, TransformTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transform(SkM44::Translate(10, 100)); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.transform(SkM44::Translate(10, 100)); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.transform(SkM44::Translate(10, 100)); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + builder2.save(); + builder2.transform(SkM44::Translate(10, 100)); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, Transform2DTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transform2DAffine(0, 1, 12, 1, 0, 33); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.transform2DAffine(0, 1, 12, 1, 0, 33); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, TransformPerspectiveTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29, 0, 0, + 0, 12); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29, 0, 0, + 0, 12); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, ResetTransformTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transformReset(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.transformReset(); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, SkewTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.skew(10, 10); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.skew(10, 10); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, TranslateTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.translate(10, 10); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.translate(10, 10); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, ScaleTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.scale(0.5, 0.5); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.scale(0.5, 0.5); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, ClipRectTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), SkClipOp::kIntersect, + true); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.transform(SkM44()); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), SkClipOp::kIntersect, + true); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + builder2.transform(SkM44()); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, ClipRRectTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.clipRRect(kTestRRect, SkClipOp::kIntersect, true); + + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.transform(SkM44()); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.clipRRect(kTestRRect, SkClipOp::kIntersect, true); + + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + builder2.transform(SkM44()); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, ClipPathTriggersDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.clipPath(kTestPath1, SkClipOp::kIntersect, true); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.transform(SkM44()); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.clipPath(kTestPath1, SkClipOp::kIntersect, true); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + builder2.transform(SkM44()); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPTranslateDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.translate(0, 0); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPScaleDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.scale(1.0, 1.0); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPRotationDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.rotate(360); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPSkewDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.skew(0, 0); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPTransformDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transform(SkM44()); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.transform(SkM44()); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPTransform2DDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transform2DAffine(1, 0, 0, 0, 1, 0); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + +TEST(DisplayList, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { + { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 1); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); + } + + { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 1); + builder1.transformReset(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.save(); + builder2.transformReset(); + builder2.drawRect({0, 0, 100, 100}); + builder2.restore(); + builder2.drawRect({0, 0, 100, 100}); + + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); + } +} + +TEST(DisplayList, NOPClipDoesNotTriggerDeferredSave) { + DisplayListBuilder builder1; + builder1.save(); + builder1.save(); + builder1.clipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0), + SkClipOp::kIntersect, true); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + builder1.drawRect({0, 0, 100, 100}); + builder1.restore(); + auto display_list1 = builder1.Build(); + + DisplayListBuilder builder2; + builder2.drawRect({0, 0, 100, 100}); + builder2.drawRect({0, 0, 100, 100}); + auto display_list2 = builder2.Build(); + + ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); +} + TEST(DisplayList, RTreeOfClippedSaveLayerFilterScene) { DisplayListBuilder builder; // blur filter with sigma=1 expands by 30 on all sides