| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/display_list/display_list.h" | |
| #include "flutter/display_list/display_list_builder.h" | |
| #include "flutter/display_list/display_list_complexity.h" | |
| #include "flutter/display_list/display_list_complexity_gl.h" | |
| #include "flutter/display_list/display_list_complexity_metal.h" | |
| #include "flutter/display_list/display_list_sampling_options.h" | |
| #include "flutter/display_list/display_list_test_utils.h" | |
| #include "flutter/testing/testing.h" | |
| #include "third_party/skia/include/core/SkColor.h" | |
| namespace flutter { | |
| namespace testing { | |
| namespace { | |
| std::vector<DisplayListComplexityCalculator*> Calculators() { | |
| return {DisplayListMetalComplexityCalculator::GetInstance(), | |
| DisplayListGLComplexityCalculator::GetInstance(), | |
| DisplayListNaiveComplexityCalculator::GetInstance()}; | |
| } | |
| std::vector<DisplayListComplexityCalculator*> AccumulatorCalculators() { | |
| return {DisplayListMetalComplexityCalculator::GetInstance(), | |
| DisplayListGLComplexityCalculator::GetInstance()}; | |
| } | |
| std::vector<SkPoint> GetTestPoints() { | |
| std::vector<SkPoint> points; | |
| points.push_back(SkPoint::Make(0, 0)); | |
| points.push_back(SkPoint::Make(10, 0)); | |
| points.push_back(SkPoint::Make(10, 10)); | |
| points.push_back(SkPoint::Make(20, 10)); | |
| points.push_back(SkPoint::Make(20, 20)); | |
| return points; | |
| } | |
| } // namespace | |
| TEST(DisplayListComplexity, EmptyDisplayList) { | |
| auto display_list = GetSampleDisplayList(0); | |
| auto calculators = Calculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_EQ(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DisplayListCeiling) { | |
| auto display_list = GetSampleDisplayList(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| calculator->SetComplexityCeiling(10u); | |
| ASSERT_EQ(calculator->Compute(display_list.get()), 10u); | |
| calculator->SetComplexityCeiling(std::numeric_limits<unsigned int>::max()); | |
| } | |
| } | |
| TEST(DisplayListComplexity, NestedDisplayList) { | |
| auto display_list = GetSampleNestedDisplayList(); | |
| auto calculators = Calculators(); | |
| for (auto calculator : calculators) { | |
| // There's only one draw call in the "outer" DisplayList, which calls | |
| // drawDisplayList with the "inner" DisplayList. To ensure we are | |
| // recursing correctly into the inner DisplayList, check that we aren't | |
| // returning 0 (if the function is a no-op) or 1 (as the op_count is 1) | |
| ASSERT_GT(calculator->Compute(display_list.get()), 1u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, AntiAliasing) { | |
| DisplayListBuilder builder_no_aa; | |
| builder_no_aa.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100)); | |
| auto display_list_no_aa = builder_no_aa.Build(); | |
| DisplayListBuilder builder_aa; | |
| builder_aa.setAntiAlias(true); | |
| builder_aa.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100)); | |
| auto display_list_aa = builder_aa.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list_no_aa.get()), | |
| calculator->Compute(display_list_aa.get())); | |
| } | |
| } | |
| TEST(DisplayListComplexity, StrokeWidth) { | |
| DisplayListBuilder builder_stroke_0; | |
| builder_stroke_0.setStrokeWidth(0.0f); | |
| builder_stroke_0.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100)); | |
| auto display_list_stroke_0 = builder_stroke_0.Build(); | |
| DisplayListBuilder builder_stroke_1; | |
| builder_stroke_1.setStrokeWidth(1.0f); | |
| builder_stroke_1.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100)); | |
| auto display_list_stroke_1 = builder_stroke_1.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list_stroke_0.get()), | |
| calculator->Compute(display_list_stroke_1.get())); | |
| } | |
| } | |
| TEST(DisplayListComplexity, Style) { | |
| DisplayListBuilder builder_filled; | |
| builder_filled.setStyle(DlDrawStyle::kFill); | |
| builder_filled.drawRect(SkRect::MakeXYWH(10, 10, 80, 80)); | |
| auto display_list_filled = builder_filled.Build(); | |
| DisplayListBuilder builder_stroked; | |
| builder_stroked.setStyle(DlDrawStyle::kStroke); | |
| builder_stroked.drawRect(SkRect::MakeXYWH(10, 10, 80, 80)); | |
| auto display_list_stroked = builder_stroked.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list_filled.get()), | |
| calculator->Compute(display_list_stroked.get())); | |
| } | |
| } | |
| TEST(DisplayListComplexity, SaveLayers) { | |
| DisplayListBuilder builder; | |
| builder.saveLayer(nullptr, true); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawPath) { | |
| DisplayListBuilder builder_line; | |
| SkPath line_path; | |
| line_path.moveTo(SkPoint::Make(0, 0)); | |
| line_path.lineTo(SkPoint::Make(10, 10)); | |
| line_path.close(); | |
| builder_line.drawPath(line_path); | |
| auto display_list_line = builder_line.Build(); | |
| DisplayListBuilder builder_quad; | |
| SkPath quad_path; | |
| quad_path.moveTo(SkPoint::Make(0, 0)); | |
| quad_path.quadTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20)); | |
| quad_path.close(); | |
| builder_quad.drawPath(quad_path); | |
| auto display_list_quad = builder_quad.Build(); | |
| DisplayListBuilder builder_conic; | |
| SkPath conic_path; | |
| conic_path.moveTo(SkPoint::Make(0, 0)); | |
| conic_path.conicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), 1.5f); | |
| conic_path.close(); | |
| builder_conic.drawPath(conic_path); | |
| auto display_list_conic = builder_conic.Build(); | |
| DisplayListBuilder builder_cubic; | |
| SkPath cubic_path; | |
| cubic_path.moveTo(SkPoint::Make(0, 0)); | |
| cubic_path.cubicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), | |
| SkPoint::Make(20, 20)); | |
| builder_cubic.drawPath(cubic_path); | |
| auto display_list_cubic = builder_cubic.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list_line.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_quad.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_conic.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_cubic.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawShadow) { | |
| DisplayListBuilder builder_line; | |
| SkPath line_path; | |
| line_path.moveTo(SkPoint::Make(0, 0)); | |
| line_path.lineTo(SkPoint::Make(10, 10)); | |
| line_path.close(); | |
| builder_line.drawShadow(line_path, SK_ColorRED, 10.0f, false, 1.0f); | |
| auto display_list_line = builder_line.Build(); | |
| DisplayListBuilder builder_quad; | |
| SkPath quad_path; | |
| quad_path.moveTo(SkPoint::Make(0, 0)); | |
| quad_path.quadTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20)); | |
| quad_path.close(); | |
| builder_quad.drawShadow(quad_path, SK_ColorRED, 10.0f, false, 1.0f); | |
| auto display_list_quad = builder_quad.Build(); | |
| DisplayListBuilder builder_conic; | |
| SkPath conic_path; | |
| conic_path.moveTo(SkPoint::Make(0, 0)); | |
| conic_path.conicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), 1.5f); | |
| conic_path.close(); | |
| builder_conic.drawShadow(conic_path, SK_ColorRED, 10.0f, false, 1.0f); | |
| auto display_list_conic = builder_conic.Build(); | |
| DisplayListBuilder builder_cubic; | |
| SkPath cubic_path; | |
| cubic_path.moveTo(SkPoint::Make(0, 0)); | |
| cubic_path.cubicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), | |
| SkPoint::Make(20, 20)); | |
| builder_cubic.drawShadow(cubic_path, SK_ColorRED, 10.0f, false, 1.0f); | |
| auto display_list_cubic = builder_cubic.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list_line.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_quad.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_conic.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_cubic.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawOval) { | |
| DisplayListBuilder builder; | |
| builder.drawOval(SkRect::MakeXYWH(10, 10, 100, 80)); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawCircle) { | |
| DisplayListBuilder builder; | |
| builder.drawCircle(SkPoint::Make(50, 50), 10.0f); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawRRect) { | |
| DisplayListBuilder builder; | |
| builder.drawRRect( | |
| SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 80), 2.0f, 3.0f)); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawDRRect) { | |
| DisplayListBuilder builder; | |
| SkRRect outer = | |
| SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 80), 2.0f, 3.0f); | |
| SkRRect inner = | |
| SkRRect::MakeRectXY(SkRect::MakeXYWH(15, 15, 70, 70), 1.5f, 1.5f); | |
| builder.drawDRRect(outer, inner); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawArc) { | |
| DisplayListBuilder builder; | |
| builder.drawArc(SkRect::MakeXYWH(10, 10, 100, 80), 0.0f, 10.0f, true); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawVertices) { | |
| auto points = GetTestPoints(); | |
| auto vertices = DlVertices::Make(DlVertexMode::kTriangles, points.size(), | |
| points.data(), nullptr, nullptr); | |
| DisplayListBuilder builder; | |
| builder.drawVertices(vertices, DlBlendMode::kSrc); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawTextBlob) { | |
| auto text_blob = SkTextBlob::MakeFromString( | |
| "The quick brown fox jumps over the lazy dog.", SkFont()); | |
| DisplayListBuilder builder; | |
| builder.drawTextBlob(text_blob, 0.0f, 0.0f); | |
| auto display_list = builder.Build(); | |
| DisplayListBuilder builder_multiple; | |
| builder_multiple.drawTextBlob(text_blob, 0.0f, 0.0f); | |
| builder_multiple.drawTextBlob(text_blob, 0.0f, 0.0f); | |
| auto display_list_multiple = builder_multiple.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| ASSERT_GT(calculator->Compute(display_list_multiple.get()), | |
| calculator->Compute(display_list.get())); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawPoints) { | |
| auto points = GetTestPoints(); | |
| DisplayListBuilder builder_lines; | |
| builder_lines.drawPoints(SkCanvas::kLines_PointMode, points.size(), | |
| points.data()); | |
| auto display_list_lines = builder_lines.Build(); | |
| DisplayListBuilder builder_points; | |
| builder_points.drawPoints(SkCanvas::kPoints_PointMode, points.size(), | |
| points.data()); | |
| auto display_list_points = builder_points.Build(); | |
| DisplayListBuilder builder_polygon; | |
| builder_polygon.drawPoints(SkCanvas::kPolygon_PointMode, points.size(), | |
| points.data()); | |
| auto display_list_polygon = builder_polygon.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list_lines.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_points.get()), 0u); | |
| ASSERT_NE(calculator->Compute(display_list_polygon.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawImage) { | |
| SkImageInfo info = | |
| SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType, | |
| SkAlphaType::kPremul_SkAlphaType); | |
| SkBitmap bitmap; | |
| bitmap.allocPixels(info, 0); | |
| auto image = SkImage::MakeFromBitmap(bitmap); | |
| DisplayListBuilder builder; | |
| builder.drawImage(DlImage::Make(image), SkPoint::Make(0, 0), | |
| DlImageSampling::kNearestNeighbor, false); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawImageNine) { | |
| SkImageInfo info = | |
| SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType, | |
| SkAlphaType::kPremul_SkAlphaType); | |
| SkBitmap bitmap; | |
| bitmap.allocPixels(info, 0); | |
| auto image = SkImage::MakeFromBitmap(bitmap); | |
| SkIRect center = SkIRect::MakeXYWH(5, 5, 20, 20); | |
| SkRect dest = SkRect::MakeXYWH(0, 0, 50, 50); | |
| DisplayListBuilder builder; | |
| builder.drawImageNine(DlImage::Make(image), center, dest, | |
| DlFilterMode::kNearest, true); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawImageRect) { | |
| SkImageInfo info = | |
| SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType, | |
| SkAlphaType::kPremul_SkAlphaType); | |
| SkBitmap bitmap; | |
| bitmap.allocPixels(info, 0); | |
| auto image = SkImage::MakeFromBitmap(bitmap); | |
| SkRect src = SkRect::MakeXYWH(0, 0, 50, 50); | |
| SkRect dest = SkRect::MakeXYWH(0, 0, 50, 50); | |
| DisplayListBuilder builder; | |
| builder.drawImageRect(DlImage::Make(image), src, dest, | |
| DlImageSampling::kNearestNeighbor, true); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| TEST(DisplayListComplexity, DrawAtlas) { | |
| SkImageInfo info = | |
| SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType, | |
| SkAlphaType::kPremul_SkAlphaType); | |
| SkBitmap bitmap; | |
| bitmap.allocPixels(info, 0); | |
| auto image = SkImage::MakeFromBitmap(bitmap); | |
| std::vector<SkRect> rects; | |
| std::vector<SkRSXform> xforms; | |
| for (int i = 0; i < 10; i++) { | |
| rects.push_back(SkRect::MakeXYWH(0, 0, 10, 10)); | |
| xforms.push_back(SkRSXform::Make(0, 0, 0, 0)); | |
| } | |
| DisplayListBuilder builder; | |
| builder.drawAtlas(DlImage::Make(image), xforms.data(), rects.data(), nullptr, | |
| 10, DlBlendMode::kSrc, DlImageSampling::kNearestNeighbor, | |
| nullptr, true); | |
| auto display_list = builder.Build(); | |
| auto calculators = AccumulatorCalculators(); | |
| for (auto calculator : calculators) { | |
| ASSERT_NE(calculator->Compute(display_list.get()), 0u); | |
| } | |
| } | |
| } // namespace testing | |
| } // namespace flutter |