diff --git a/engine/src/flutter/impeller/aiks/canvas.cc b/engine/src/flutter/impeller/aiks/canvas.cc index 662ea009cd6..8ccf32f4806 100644 --- a/engine/src/flutter/impeller/aiks/canvas.cc +++ b/engine/src/flutter/impeller/aiks/canvas.cc @@ -251,10 +251,8 @@ void Canvas::DrawRRect(Rect rect, Scalar corner_radius, const Paint& paint) { GetCurrentPass().AddEntity(entity); return; - } else { - DrawPath(PathBuilder{}.AddRoundedRect(rect, corner_radius).TakePath(), - paint); } + DrawPath(PathBuilder{}.AddRoundedRect(rect, corner_radius).TakePath(), paint); } void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) { @@ -263,7 +261,11 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) { paint)) { return; } - DrawPath(PathBuilder{}.AddCircle(center, radius).TakePath(), paint); + auto circle_path = PathBuilder{} + .AddCircle(center, radius) + .SetConvexity(Convexity::kConvex) + .TakePath(); + DrawPath(circle_path, paint); } void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) { diff --git a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc index 88eeff25409..210f64b7f9e 100644 --- a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc +++ b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc @@ -845,6 +845,7 @@ void DlDispatcher::drawLine(const SkPoint& p0, const SkPoint& p1) { auto path = PathBuilder{} .AddLine(skia_conversions::ToPoint(p0), skia_conversions::ToPoint(p1)) + .SetConvexity(Convexity::kConvex) .TakePath(); Paint paint = paint_; paint.style = Paint::Style::kStroke; @@ -862,8 +863,10 @@ void DlDispatcher::drawOval(const SkRect& bounds) { canvas_.DrawCircle(skia_conversions::ToPoint(bounds.center()), bounds.width() * 0.5, paint_); } else { - auto path = - PathBuilder{}.AddOval(skia_conversions::ToRect(bounds)).TakePath(); + auto path = PathBuilder{} + .AddOval(skia_conversions::ToRect(bounds)) + .SetConvexity(Convexity::kConvex) + .TakePath(); canvas_.DrawPath(path, paint_); } } diff --git a/engine/src/flutter/impeller/display_list/skia_conversions.cc b/engine/src/flutter/impeller/display_list/skia_conversions.cc index 7b54fef8a55..16c8978bbc6 100644 --- a/engine/src/flutter/impeller/display_list/skia_conversions.cc +++ b/engine/src/flutter/impeller/display_list/skia_conversions.cc @@ -109,12 +109,15 @@ Path ToPath(const SkPath& path) { fill_type = FillType::kNonZero; break; } + builder.SetConvexity(path.isConvex() ? Convexity::kConvex + : Convexity::kUnknown); return builder.TakePath(fill_type); } Path ToPath(const SkRRect& rrect) { return PathBuilder{} .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) + .SetConvexity(Convexity::kConvex) .TakePath(); } diff --git a/engine/src/flutter/impeller/entity/entity_unittests.cc b/engine/src/flutter/impeller/entity/entity_unittests.cc index 77acc57dd91..1f0376e8461 100644 --- a/engine/src/flutter/impeller/entity/entity_unittests.cc +++ b/engine/src/flutter/impeller/entity/entity_unittests.cc @@ -2572,5 +2572,41 @@ TEST_P(EntityTest, TiledTextureContentsIsOpaque) { ASSERT_FALSE(contents.IsOpaque()); } +TEST_P(EntityTest, TessellateConvex) { + { + // Sanity check simple rectangle. + auto [pts, indices] = + TessellateConvex(PathBuilder{} + .AddRect(Rect::MakeLTRB(0, 0, 10, 10)) + .TakePath() + .CreatePolyline(1.0)); + + std::vector expected = { + {0, 0}, {10, 0}, {10, 10}, {0, 10}, // + }; + std::vector expected_indices = {0, 1, 2, 0, 2, 3}; + ASSERT_EQ(pts, expected); + ASSERT_EQ(indices, expected_indices); + } + + { + auto [pts, indices] = + TessellateConvex(PathBuilder{} + .AddRect(Rect::MakeLTRB(0, 0, 10, 10)) + .AddRect(Rect::MakeLTRB(20, 20, 30, 30)) + .TakePath() + .CreatePolyline(1.0)); + + std::vector expected = { + {0, 0}, {10, 0}, {10, 10}, {0, 10}, // + {20, 20}, {30, 20}, {30, 30}, {20, 30} // + }; + std::vector expected_indices = {0, 1, 2, 0, 2, 3, + 0, 6, 7, 0, 7, 8}; + ASSERT_EQ(pts, expected); + ASSERT_EQ(indices, expected_indices); + } +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/geometry.cc b/engine/src/flutter/impeller/entity/geometry.cc index 612d9cb3a1a..8e568c3f2a9 100644 --- a/engine/src/flutter/impeller/entity/geometry.cc +++ b/engine/src/flutter/impeller/entity/geometry.cc @@ -16,6 +16,37 @@ namespace impeller { +/// Given a convex polyline, create a triangle fan structure. +std::pair, std::vector> TessellateConvex( + Path::Polyline polyline) { + std::vector output; + std::vector index; + + for (auto j = 0u; j < polyline.contours.size(); j++) { + auto [start, end] = polyline.GetContourPointBounds(j); + auto center = polyline.points[start]; + + // Some polygons will not self close and an additional triangle + // must be inserted, others will self close and we need to avoid + // inserting an extra triangle. + if (polyline.points[end - 1] == polyline.points[start]) { + end--; + } + output.emplace_back(center); + output.emplace_back(polyline.points[start + 1]); + + for (auto i = start + 2; i < end; i++) { + const auto& point_b = polyline.points[i]; + output.emplace_back(point_b); + + index.emplace_back(0); + index.emplace_back(i - 1); + index.emplace_back(i); + } + } + return std::make_pair(output, index); +} + Geometry::Geometry() = default; Geometry::~Geometry() = default; @@ -103,8 +134,30 @@ GeometryResult FillPathGeometry::GetPositionBuffer( const ContentContext& renderer, const Entity& entity, RenderPass& pass) { - VertexBuffer vertex_buffer; auto& host_buffer = pass.GetTransientsBuffer(); + VertexBuffer vertex_buffer; + + if (path_.GetFillType() == FillType::kNonZero && // + path_.IsConvex()) { + auto [points, indicies] = TessellateConvex( + path_.CreatePolyline(entity.GetTransformation().GetMaxBasisLength())); + + vertex_buffer.vertex_buffer = host_buffer.Emplace( + points.data(), points.size() * sizeof(Point), alignof(Point)); + vertex_buffer.index_buffer = host_buffer.Emplace( + indicies.data(), indicies.size() * sizeof(uint16_t), alignof(uint16_t)); + vertex_buffer.index_count = indicies.size(); + vertex_buffer.index_type = IndexType::k16bit; + + return GeometryResult{ + .type = PrimitiveType::kTriangle, + .vertex_buffer = vertex_buffer, + .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(), + .prevent_overdraw = false, + }; + } + auto tesselation_result = renderer.GetTessellator()->Tessellate( path_.GetFillType(), path_.CreatePolyline(entity.GetTransformation().GetMaxBasisLength()), diff --git a/engine/src/flutter/impeller/entity/geometry.h b/engine/src/flutter/impeller/entity/geometry.h index bcf9190766a..53fd80ba969 100644 --- a/engine/src/flutter/impeller/entity/geometry.h +++ b/engine/src/flutter/impeller/entity/geometry.h @@ -33,6 +33,11 @@ enum GeometryVertexType { kUV, }; +/// @brief Given a polyline created from a convex filled path, perform a +/// tessellation. +std::pair, std::vector> TessellateConvex( + Path::Polyline polyline); + class Geometry { public: Geometry(); diff --git a/engine/src/flutter/impeller/geometry/path.cc b/engine/src/flutter/impeller/geometry/path.cc index 5121e8e045c..1f677294f89 100644 --- a/engine/src/flutter/impeller/geometry/path.cc +++ b/engine/src/flutter/impeller/geometry/path.cc @@ -53,6 +53,14 @@ FillType Path::GetFillType() const { return fill_; } +bool Path::IsConvex() const { + return convexity_ == Convexity::kConvex; +} + +void Path::SetConvexity(Convexity value) { + convexity_ = value; +} + Path& Path::AddLinearComponent(Point p1, Point p2) { linears_.emplace_back(p1, p2); components_.emplace_back(ComponentType::kLinear, linears_.size() - 1); diff --git a/engine/src/flutter/impeller/geometry/path.h b/engine/src/flutter/impeller/geometry/path.h index 21ad2fdbb31..78a6e5f6647 100644 --- a/engine/src/flutter/impeller/geometry/path.h +++ b/engine/src/flutter/impeller/geometry/path.h @@ -34,6 +34,11 @@ enum class FillType { kAbsGeqTwo, }; +enum class Convexity { + kUnknown, + kConvex, +}; + //------------------------------------------------------------------------------ /// @brief Paths are lightweight objects that describe a collection of /// linear, quadratic, or cubic segments. These segments may be @@ -94,6 +99,8 @@ class Path { FillType GetFillType() const; + bool IsConvex() const; + Path& AddLinearComponent(Point p1, Point p2); Path& AddQuadraticComponent(Point p1, Point cp, Point p2); @@ -148,6 +155,10 @@ class Path { std::optional> GetMinMaxCoveragePoints() const; private: + friend class PathBuilder; + + void SetConvexity(Convexity value); + struct ComponentIndexPair { ComponentType type = ComponentType::kLinear; size_t index = 0; @@ -159,6 +170,7 @@ class Path { }; FillType fill_ = FillType::kNonZero; + Convexity convexity_ = Convexity::kUnknown; std::vector components_; std::vector linears_; std::vector quads_; diff --git a/engine/src/flutter/impeller/geometry/path_builder.cc b/engine/src/flutter/impeller/geometry/path_builder.cc index ef3424885db..93855dadd48 100644 --- a/engine/src/flutter/impeller/geometry/path_builder.cc +++ b/engine/src/flutter/impeller/geometry/path_builder.cc @@ -21,6 +21,7 @@ Path PathBuilder::CopyPath(FillType fill) const { Path PathBuilder::TakePath(FillType fill) { auto path = prototype_; path.SetFillType(fill); + path.SetConvexity(convexity_); return path; } @@ -103,6 +104,11 @@ PathBuilder& PathBuilder::SmoothQuadraticCurveTo(Point point, bool relative) { return *this; } +PathBuilder& PathBuilder::SetConvexity(Convexity value) { + convexity_ = value; + return *this; +} + PathBuilder& PathBuilder::CubicCurveTo(Point controlPoint1, Point controlPoint2, Point point, diff --git a/engine/src/flutter/impeller/geometry/path_builder.h b/engine/src/flutter/impeller/geometry/path_builder.h index de25ca311e7..56ac9f4dc3f 100644 --- a/engine/src/flutter/impeller/geometry/path_builder.h +++ b/engine/src/flutter/impeller/geometry/path_builder.h @@ -31,6 +31,8 @@ class PathBuilder { const Path& GetCurrentPath() const; + PathBuilder& SetConvexity(Convexity value); + PathBuilder& MoveTo(Point point, bool relative = false); PathBuilder& Close(); @@ -116,6 +118,7 @@ class PathBuilder { Point subpath_start_; Point current_; Path prototype_; + Convexity convexity_; Point ReflectedQuadraticControlPoint1() const;