diff --git a/impeller/entity/geometry/geometry.cc b/impeller/entity/geometry/geometry.cc index 36267c51c2331..426f257949b11 100644 --- a/impeller/entity/geometry/geometry.cc +++ b/impeller/entity/geometry/geometry.cc @@ -17,25 +17,27 @@ namespace impeller { /// Given a convex polyline, create a triangle fan structure. std::pair, std::vector> TessellateConvex( - Path::Polyline polyline) { + const Path::Polyline& polyline) { std::vector output; std::vector indices; - for (auto j = 0u; j < polyline.contours.size(); j++) { + const auto& points = polyline.points(); + + for (auto j = 0u; j < polyline.contours().size(); j++) { auto [start, end] = polyline.GetContourPointBounds(j); - auto center = polyline.points[start]; + auto center = 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]) { + if (points[end - 1] == points[start]) { end--; } output.emplace_back(center); - output.emplace_back(polyline.points[start + 1]); + output.emplace_back(points[start + 1]); for (auto i = start + 2; i < end; i++) { - const auto& point_b = polyline.points[i]; + const auto& point_b = points[i]; output.emplace_back(point_b); indices.emplace_back(0); diff --git a/impeller/entity/geometry/geometry.h b/impeller/entity/geometry/geometry.h index 6a8861d120e6e..e988d8884bb3f 100644 --- a/impeller/entity/geometry/geometry.h +++ b/impeller/entity/geometry/geometry.h @@ -49,7 +49,7 @@ GeometryResult ComputeUVGeometryForRect(Rect source_rect, /// @brief Given a polyline created from a convex filled path, perform a /// tessellation. std::pair, std::vector> TessellateConvex( - Path::Polyline polyline); + const Path::Polyline& polyline); class Geometry { public: diff --git a/impeller/entity/geometry/stroke_path_geometry.cc b/impeller/entity/geometry/stroke_path_geometry.cc index 43648a548e29e..55cda6c442426 100644 --- a/impeller/entity/geometry/stroke_path_geometry.cc +++ b/impeller/entity/geometry/stroke_path_geometry.cc @@ -237,29 +237,29 @@ StrokePathGeometry::CreateSolidStrokeVertices( auto polyline = path.CreatePolyline(scale); VS::PerVertexData vtx; + const auto& contours = polyline.contours(); + const auto& points = polyline.points(); // Offset state. Point offset; Point previous_offset; // Used for computing joins. - auto compute_offset = [&polyline, &offset, &previous_offset, + auto compute_offset = [&points, &offset, &previous_offset, &stroke_width](size_t point_i) { previous_offset = offset; - Point direction = - (polyline.points[point_i] - polyline.points[point_i - 1]).Normalize(); + Point direction = (points[point_i] - points[point_i - 1]).Normalize(); offset = Vector2{-direction.y, direction.x} * stroke_width * 0.5; }; - for (size_t contour_i = 0; contour_i < polyline.contours.size(); - contour_i++) { - auto contour = polyline.contours[contour_i]; + for (size_t contour_i = 0; contour_i < contours.size(); contour_i++) { + auto contour = contours[contour_i]; size_t contour_start_point_i, contour_end_point_i; std::tie(contour_start_point_i, contour_end_point_i) = polyline.GetContourPointBounds(contour_i); switch (contour_end_point_i - contour_start_point_i) { case 1: { - Point p = polyline.points[contour_start_point_i]; + Point p = points[contour_start_point_i]; cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, scale, false); cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, scale, false); continue; @@ -282,14 +282,14 @@ StrokePathGeometry::CreateSolidStrokeVertices( // vertices at the start of the new contour (thus connecting the two // contours with two zero volume triangles, which will be discarded by // the rasterizer). - vtx.position = polyline.points[contour_start_point_i - 1]; + vtx.position = points[contour_start_point_i - 1]; // Append two vertices when "picking up" the pen so that the triangle // drawn when moving to the beginning of the new contour will have zero // volume. vtx_builder.AppendVertex(vtx); vtx_builder.AppendVertex(vtx); - vtx.position = polyline.points[contour_start_point_i]; + vtx.position = points[contour_start_point_i]; // Append two vertices at the beginning of the new contour, which // appends two triangles of zero area. vtx_builder.AppendVertex(vtx); @@ -297,45 +297,45 @@ StrokePathGeometry::CreateSolidStrokeVertices( } // Generate start cap. - if (!polyline.contours[contour_i].is_closed) { + if (!contours[contour_i].is_closed) { auto cap_offset = Vector2(-contour.start_direction.y, contour.start_direction.x) * stroke_width * 0.5; // Counterclockwise normal - cap_proc(vtx_builder, polyline.points[contour_start_point_i], cap_offset, - scale, true); + cap_proc(vtx_builder, points[contour_start_point_i], cap_offset, scale, + true); } // Generate contour geometry. for (size_t point_i = contour_start_point_i + 1; point_i < contour_end_point_i; point_i++) { // Generate line rect. - vtx.position = polyline.points[point_i - 1] + offset; + vtx.position = points[point_i - 1] + offset; vtx_builder.AppendVertex(vtx); - vtx.position = polyline.points[point_i - 1] - offset; + vtx.position = points[point_i - 1] - offset; vtx_builder.AppendVertex(vtx); - vtx.position = polyline.points[point_i] + offset; + vtx.position = points[point_i] + offset; vtx_builder.AppendVertex(vtx); - vtx.position = polyline.points[point_i] - offset; + vtx.position = points[point_i] - offset; vtx_builder.AppendVertex(vtx); if (point_i < contour_end_point_i - 1) { compute_offset(point_i + 1); // Generate join from the current line to the next line. - join_proc(vtx_builder, polyline.points[point_i], previous_offset, - offset, scaled_miter_limit, scale); + join_proc(vtx_builder, points[point_i], previous_offset, offset, + scaled_miter_limit, scale); } } // Generate end cap or join. - if (!polyline.contours[contour_i].is_closed) { + if (!contours[contour_i].is_closed) { auto cap_offset = Vector2(-contour.end_direction.y, contour.end_direction.x) * stroke_width * 0.5; // Clockwise normal - cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], - cap_offset, scale, false); + cap_proc(vtx_builder, points[contour_end_point_i - 1], cap_offset, scale, + false); } else { - join_proc(vtx_builder, polyline.points[contour_start_point_i], offset, + join_proc(vtx_builder, points[contour_start_point_i], offset, contour_first_offset, scaled_miter_limit, scale); } } diff --git a/impeller/geometry/geometry_benchmarks.cc b/impeller/geometry/geometry_benchmarks.cc index d3c1f9ea525c5..2e381a3581a7a 100644 --- a/impeller/geometry/geometry_benchmarks.cc +++ b/impeller/geometry/geometry_benchmarks.cc @@ -30,7 +30,7 @@ static void BM_Polyline(benchmark::State& state, Args&&... args) { size_t single_point_count = 0u; while (state.KeepRunning()) { auto polyline = path.CreatePolyline(1.0f); - single_point_count = polyline.points.size(); + single_point_count = polyline.points().size(); point_count += single_point_count; if (tessellate) { tess.Tessellate( diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 2b9875bf67bd0..e5a5b147340d3 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -600,8 +600,8 @@ TEST(GeometryTest, EmptyPath) { ASSERT_POINT_NEAR(c.destination, Point()); Path::Polyline polyline = path.CreatePolyline(1.0f); - ASSERT_TRUE(polyline.points.empty()); - ASSERT_TRUE(polyline.contours.empty()); + ASSERT_TRUE(polyline.points().empty()); + ASSERT_TRUE(polyline.contours().empty()); } TEST(GeometryTest, SimplePath) { @@ -2016,13 +2016,13 @@ TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) { auto polyline = path.CreatePolyline(1.0f); - ASSERT_EQ(polyline.contours.size(), 2u); - ASSERT_EQ(polyline.points.size(), 5u); - ASSERT_EQ(polyline.points[0].x, 10); - ASSERT_EQ(polyline.points[1].x, 20); - ASSERT_EQ(polyline.points[2].x, 30); - ASSERT_EQ(polyline.points[3].x, 40); - ASSERT_EQ(polyline.points[4].x, 50); + ASSERT_EQ(polyline.contours().size(), 2u); + ASSERT_EQ(polyline.points().size(), 5u); + ASSERT_EQ(polyline.points()[0].x, 10); + ASSERT_EQ(polyline.points()[1].x, 20); + ASSERT_EQ(polyline.points()[2].x, 30); + ASSERT_EQ(polyline.points()[3].x, 40); + ASSERT_EQ(polyline.points()[4].x, 50); } TEST(GeometryTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { @@ -2101,12 +2101,12 @@ TEST(GeometryTest, PathCreatePolylineGeneratesCorrectContourData) { .Close() .TakePath() .CreatePolyline(1.0f); - ASSERT_EQ(polyline.points.size(), 6u); - ASSERT_EQ(polyline.contours.size(), 2u); - ASSERT_EQ(polyline.contours[0].is_closed, false); - ASSERT_EQ(polyline.contours[0].start_index, 0u); - ASSERT_EQ(polyline.contours[1].is_closed, true); - ASSERT_EQ(polyline.contours[1].start_index, 2u); + ASSERT_EQ(polyline.points().size(), 6u); + ASSERT_EQ(polyline.contours().size(), 2u); + ASSERT_EQ(polyline.contours()[0].is_closed, false); + ASSERT_EQ(polyline.contours()[0].start_index, 0u); + ASSERT_EQ(polyline.contours()[1].is_closed, true); + ASSERT_EQ(polyline.contours()[1].start_index, 2u); } TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) { @@ -2132,15 +2132,15 @@ TEST(GeometryTest, PathAddRectPolylineHasCorrectContourData) { .AddRect(Rect::MakeLTRB(50, 60, 70, 80)) .TakePath() .CreatePolyline(1.0f); - ASSERT_EQ(polyline.contours.size(), 1u); - ASSERT_TRUE(polyline.contours[0].is_closed); - ASSERT_EQ(polyline.contours[0].start_index, 0u); - ASSERT_EQ(polyline.points.size(), 5u); - ASSERT_EQ(polyline.points[0], Point(50, 60)); - ASSERT_EQ(polyline.points[1], Point(70, 60)); - ASSERT_EQ(polyline.points[2], Point(70, 80)); - ASSERT_EQ(polyline.points[3], Point(50, 80)); - ASSERT_EQ(polyline.points[4], Point(50, 60)); + ASSERT_EQ(polyline.contours().size(), 1u); + ASSERT_TRUE(polyline.contours()[0].is_closed); + ASSERT_EQ(polyline.contours()[0].start_index, 0u); + ASSERT_EQ(polyline.points().size(), 5u); + ASSERT_EQ(polyline.points()[0], Point(50, 60)); + ASSERT_EQ(polyline.points()[1], Point(70, 60)); + ASSERT_EQ(polyline.points()[2], Point(70, 80)); + ASSERT_EQ(polyline.points()[3], Point(50, 80)); + ASSERT_EQ(polyline.points()[4], Point(50, 60)); } TEST(GeometryTest, PathPolylineDuplicatesAreRemovedForSameContour) { @@ -2157,19 +2157,19 @@ TEST(GeometryTest, PathPolylineDuplicatesAreRemovedForSameContour) { .LineTo({0, 100}) // Insert duplicate at end of contour. .TakePath() .CreatePolyline(1.0f); - ASSERT_EQ(polyline.contours.size(), 2u); - ASSERT_EQ(polyline.contours[0].start_index, 0u); - ASSERT_TRUE(polyline.contours[0].is_closed); - ASSERT_EQ(polyline.contours[1].start_index, 4u); - ASSERT_FALSE(polyline.contours[1].is_closed); - ASSERT_EQ(polyline.points.size(), 7u); - ASSERT_EQ(polyline.points[0], Point(50, 50)); - ASSERT_EQ(polyline.points[1], Point(100, 50)); - ASSERT_EQ(polyline.points[2], Point(100, 100)); - ASSERT_EQ(polyline.points[3], Point(50, 50)); - ASSERT_EQ(polyline.points[4], Point(50, 50)); - ASSERT_EQ(polyline.points[5], Point(0, 50)); - ASSERT_EQ(polyline.points[6], Point(0, 100)); + ASSERT_EQ(polyline.contours().size(), 2u); + ASSERT_EQ(polyline.contours()[0].start_index, 0u); + ASSERT_TRUE(polyline.contours()[0].is_closed); + ASSERT_EQ(polyline.contours()[1].start_index, 4u); + ASSERT_FALSE(polyline.contours()[1].is_closed); + ASSERT_EQ(polyline.points().size(), 7u); + ASSERT_EQ(polyline.points()[0], Point(50, 50)); + ASSERT_EQ(polyline.points()[1], Point(100, 50)); + ASSERT_EQ(polyline.points()[2], Point(100, 100)); + ASSERT_EQ(polyline.points()[3], Point(50, 50)); + ASSERT_EQ(polyline.points()[4], Point(50, 50)); + ASSERT_EQ(polyline.points()[5], Point(0, 50)); + ASSERT_EQ(polyline.points()[6], Point(0, 100)); } TEST(GeometryTest, MatrixPrinting) { diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 1f677294f89df..14fa0a12b64a4 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -11,13 +11,13 @@ namespace impeller { -Path::Path() { +Path::Path() : state_(std::make_shared()) { AddContourComponent({}); }; Path::~Path() = default; -std::tuple Path::Polyline::GetContourPointBounds( +std::tuple Path::Polyline::State::GetContourPointBounds( size_t contour_index) const { if (contour_index >= contours.size()) { return {points.size(), points.size()}; @@ -29,7 +29,7 @@ std::tuple Path::Polyline::GetContourPointBounds( return std::make_tuple(start_index, end_index); } -size_t Path::GetComponentCount(std::optional type) const { +size_t Path::State::GetComponentCount(std::optional type) const { if (type.has_value()) { switch (type.value()) { case ComponentType::kLinear: @@ -45,41 +45,41 @@ size_t Path::GetComponentCount(std::optional type) const { return components_.size(); } -void Path::SetFillType(FillType fill) { +void Path::State::SetFillType(FillType fill) { fill_ = fill; } -FillType Path::GetFillType() const { +FillType Path::State::GetFillType() const { return fill_; } -bool Path::IsConvex() const { +bool Path::State::IsConvex() const { return convexity_ == Convexity::kConvex; } -void Path::SetConvexity(Convexity value) { +void Path::State::SetConvexity(Convexity value) { convexity_ = value; } -Path& Path::AddLinearComponent(Point p1, Point p2) { +void Path::State::AddLinearComponent(Point p1, Point p2) { linears_.emplace_back(p1, p2); components_.emplace_back(ComponentType::kLinear, linears_.size() - 1); - return *this; + InvalidateCache(); } -Path& Path::AddQuadraticComponent(Point p1, Point cp, Point p2) { +void Path::State::AddQuadraticComponent(Point p1, Point cp, Point p2) { quads_.emplace_back(p1, cp, p2); components_.emplace_back(ComponentType::kQuadratic, quads_.size() - 1); - return *this; + InvalidateCache(); } -Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { +void Path::State::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { cubics_.emplace_back(p1, cp1, cp2, p2); components_.emplace_back(ComponentType::kCubic, cubics_.size() - 1); - return *this; + InvalidateCache(); } -Path& Path::AddContourComponent(Point destination, bool is_closed) { +void Path::State::AddContourComponent(Point destination, bool is_closed) { if (components_.size() > 0 && components_.back().type == ComponentType::kContour) { // Never insert contiguous contours. @@ -88,14 +88,15 @@ Path& Path::AddContourComponent(Point destination, bool is_closed) { contours_.emplace_back(ContourComponent(destination, is_closed)); components_.emplace_back(ComponentType::kContour, contours_.size() - 1); } - return *this; + InvalidateCache(); } -void Path::SetContourClosed(bool is_closed) { +void Path::State::SetContourClosed(bool is_closed) { contours_.back().is_closed = is_closed; + InvalidateCache(); } -void Path::EnumerateComponents( +void Path::State::EnumerateComponents( const Applier& linear_applier, const Applier& quad_applier, const Applier& cubic_applier, @@ -128,8 +129,8 @@ void Path::EnumerateComponents( } } -bool Path::GetLinearComponentAtIndex(size_t index, - LinearPathComponent& linear) const { +bool Path::State::GetLinearComponentAtIndex(size_t index, + LinearPathComponent& linear) const { if (index >= components_.size()) { return false; } @@ -142,7 +143,7 @@ bool Path::GetLinearComponentAtIndex(size_t index, return true; } -bool Path::GetQuadraticComponentAtIndex( +bool Path::State::GetQuadraticComponentAtIndex( size_t index, QuadraticPathComponent& quadratic) const { if (index >= components_.size()) { @@ -157,8 +158,8 @@ bool Path::GetQuadraticComponentAtIndex( return true; } -bool Path::GetCubicComponentAtIndex(size_t index, - CubicPathComponent& cubic) const { +bool Path::State::GetCubicComponentAtIndex(size_t index, + CubicPathComponent& cubic) const { if (index >= components_.size()) { return false; } @@ -171,8 +172,8 @@ bool Path::GetCubicComponentAtIndex(size_t index, return true; } -bool Path::GetContourComponentAtIndex(size_t index, - ContourComponent& move) const { +bool Path::State::GetContourComponentAtIndex(size_t index, + ContourComponent& move) const { if (index >= components_.size()) { return false; } @@ -185,8 +186,9 @@ bool Path::GetContourComponentAtIndex(size_t index, return true; } -bool Path::UpdateLinearComponentAtIndex(size_t index, - const LinearPathComponent& linear) { +bool Path::State::UpdateLinearComponentAtIndex( + size_t index, + const LinearPathComponent& linear) { if (index >= components_.size()) { return false; } @@ -196,10 +198,11 @@ bool Path::UpdateLinearComponentAtIndex(size_t index, } linears_[components_[index].index] = linear; + InvalidateCache(); return true; } -bool Path::UpdateQuadraticComponentAtIndex( +bool Path::State::UpdateQuadraticComponentAtIndex( size_t index, const QuadraticPathComponent& quadratic) { if (index >= components_.size()) { @@ -211,11 +214,12 @@ bool Path::UpdateQuadraticComponentAtIndex( } quads_[components_[index].index] = quadratic; + InvalidateCache(); return true; } -bool Path::UpdateCubicComponentAtIndex(size_t index, - CubicPathComponent& cubic) { +bool Path::State::UpdateCubicComponentAtIndex(size_t index, + CubicPathComponent& cubic) { if (index >= components_.size()) { return false; } @@ -225,11 +229,12 @@ bool Path::UpdateCubicComponentAtIndex(size_t index, } cubics_[components_[index].index] = cubic; + InvalidateCache(); return true; } -bool Path::UpdateContourComponentAtIndex(size_t index, - const ContourComponent& move) { +bool Path::State::UpdateContourComponentAtIndex(size_t index, + const ContourComponent& move) { if (index >= components_.size()) { return false; } @@ -239,29 +244,35 @@ bool Path::UpdateContourComponentAtIndex(size_t index, } contours_[components_[index].index] = move; + InvalidateCache(); return true; } -Path::Polyline Path::CreatePolyline(Scalar scale) const { - Polyline polyline; +Path::Polyline Path::State::CreatePolyline(Scalar scale) const { + if (cached_polyline_ && cached_polyline_->state_->scale >= scale) { + return *cached_polyline_; + } + + std::vector points; + std::vector contours; std::optional previous_contour_point; - auto collect_points = [&polyline, &previous_contour_point]( - const std::vector& collection) { - if (collection.empty()) { - return; - } + auto collect_points = + [&points, &previous_contour_point](const std::vector& collection) { + if (collection.empty()) { + return; + } - for (const auto& point : collection) { - if (previous_contour_point.has_value() && - previous_contour_point.value() == point) { - // Skip over duplicate points in the same contour. - continue; - } - previous_contour_point = point; - polyline.points.push_back(point); - } - }; + for (const auto& point : collection) { + if (previous_contour_point.has_value() && + previous_contour_point.value() == point) { + // Skip over duplicate points in the same contour. + continue; + } + previous_contour_point = point; + points.push_back(point); + } + }; auto get_path_component = [this](size_t component_i) -> PathComponentVariant { if (component_i >= components_.size()) { @@ -298,11 +309,11 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { }; std::optional previous_path_component_index; - auto end_contour = [&polyline, &previous_path_component_index, + auto end_contour = [&contours, &previous_path_component_index, &get_path_component]() { // Whenever a contour has ended, extract the exact end direction from the // last component. - if (polyline.contours.empty()) { + if (contours.empty()) { return; } @@ -310,7 +321,7 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { return; } - auto& contour = polyline.contours.back(); + auto& contour = contours.back(); contour.end_direction = Vector2(0, 1); size_t previous_index = previous_path_component_index.value(); @@ -357,30 +368,40 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { Vector2 start_direction = compute_contour_start_direction(component_i); const auto& contour = contours_[component.index]; - polyline.contours.push_back({.start_index = polyline.points.size(), - .is_closed = contour.is_closed, - .start_direction = start_direction}); + contours.push_back({.start_index = points.size(), + .is_closed = contour.is_closed, + .start_direction = start_direction}); previous_contour_point = std::nullopt; collect_points({contour.destination}); break; } end_contour(); } + Polyline polyline(std::move(points), std::move(contours), scale); + cached_polyline_ = polyline; return polyline; } -std::optional Path::GetBoundingBox() const { +std::optional Path::State::GetBoundingBox() const { + if (cached_bounding_box_) { + return *cached_bounding_box_; + } + auto min_max = GetMinMaxCoveragePoints(); if (!min_max.has_value()) { + cached_bounding_box_ = + std::make_optional>(std::nullopt); return std::nullopt; } auto min = min_max->first; auto max = min_max->second; const auto difference = max - min; - return Rect{min.x, min.y, difference.x, difference.y}; + Rect result{min.x, min.y, difference.x, difference.y}; + cached_bounding_box_ = result; + return result; } -std::optional Path::GetTransformedBoundingBox( +std::optional Path::State::GetTransformedBoundingBox( const Matrix& transform) const { auto bounds = GetBoundingBox(); if (!bounds.has_value()) { @@ -389,7 +410,8 @@ std::optional Path::GetTransformedBoundingBox( return bounds->TransformBounds(transform); } -std::optional> Path::GetMinMaxCoveragePoints() const { +std::optional> Path::State::GetMinMaxCoveragePoints() + const { if (linears_.empty() && quads_.empty() && cubics_.empty()) { return std::nullopt; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 78a6e5f6647e2..381819f8da39e 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -51,6 +50,9 @@ enum class Convexity { /// Creating paths that describe complex shapes is usually done by a /// path builder. /// +/// Paths are internally reference counted. A copied path will +/// share underlying storage with the original path. +/// To make an independent copy use Path::Clone(). class Path { public: enum class ComponentType { @@ -75,41 +77,94 @@ class Path { /// One or more contours represented as a series of points and indices in /// the point vector representing the start of a new contour. - struct Polyline { - /// Points in the polyline, which may represent multiple contours specified - /// by indices in |breaks|. - std::vector points; - std::vector contours; - - /// Convenience method to compute the start (inclusive) and end (exclusive) - /// point of the given contour index. + class Polyline { + public: + Polyline(std::vector&& points, + std::vector&& countours, + Scalar scale) + : state_(std::make_shared()) { + state_->points = std::move(points); + state_->contours = std::move(countours); + state_->scale = scale; + } + /// Points in the polyline, which may represent multiple contours + /// specified by indices in |breaks|. + const std::vector& points() const { return state_->points; } + const std::vector& contours() const { + return state_->contours; + } + /// Convenience method to compute the start (inclusive) and end + /// (exclusive) point of the given contour index. /// /// The contour_index parameter is clamped to contours.size(). std::tuple GetContourPointBounds( - size_t contour_index) const; + size_t contour_index) const { + return state_->GetContourPointBounds(contour_index); + } + + private: + friend class Tessellator; + friend class Path; + + struct State { + std::vector points; + std::vector contours; + std::tuple GetContourPointBounds( + size_t contour_index) const; + Scalar scale; + + struct TesselatorData { + std::vector vertices; + std::vector indices; + }; + std::unordered_map tesselator_cache_; + }; + std::shared_ptr state_; }; Path(); ~Path(); - size_t GetComponentCount(std::optional type = {}) const; + /// Creates a clone of this path. Modifications of cloned paths do not affect + /// the original. + Path Clone() const { + Path copy; + *copy.state_ = *state_; + return copy; + } - void SetFillType(FillType fill); + size_t GetComponentCount(std::optional type = {}) const { + return state_->GetComponentCount(type); + } - FillType GetFillType() const; + void SetFillType(FillType fill) { state_->SetFillType(fill); } - bool IsConvex() const; + FillType GetFillType() const { return state_->GetFillType(); } - Path& AddLinearComponent(Point p1, Point p2); + bool IsConvex() const { return state_->IsConvex(); } - Path& AddQuadraticComponent(Point p1, Point cp, Point p2); + Path& AddLinearComponent(Point p1, Point p2) { + state_->AddLinearComponent(p1, p2); + return *this; + } - Path& AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2); + Path& AddQuadraticComponent(Point p1, Point cp, Point p2) { + state_->AddQuadraticComponent(p1, cp, p2); + return *this; + } - Path& AddContourComponent(Point destination, bool is_closed = false); + Path& AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { + state_->AddCubicComponent(p1, cp1, cp2, p2); + return *this; + } - void SetContourClosed(bool is_closed); + Path& AddContourComponent(Point destination, bool is_closed = false) { + state_->AddContourComponent(destination, is_closed); + return *this; + } + + void SetContourClosed(bool is_closed) { state_->SetContourClosed(is_closed); } template using Applier = std::function; @@ -117,65 +172,166 @@ class Path { const Applier& linear_applier, const Applier& quad_applier, const Applier& cubic_applier, - const Applier& contour_applier) const; + const Applier& contour_applier) const { + state_->EnumerateComponents(linear_applier, quad_applier, cubic_applier, + contour_applier); + } bool GetLinearComponentAtIndex(size_t index, - LinearPathComponent& linear) const; + LinearPathComponent& linear) const { + return state_->GetLinearComponentAtIndex(index, linear); + } bool GetQuadraticComponentAtIndex(size_t index, - QuadraticPathComponent& quadratic) const; + QuadraticPathComponent& quadratic) const { + return state_->GetQuadraticComponentAtIndex(index, quadratic); + } - bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const; + bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const { + return state_->GetCubicComponentAtIndex(index, cubic); + } bool GetContourComponentAtIndex(size_t index, - ContourComponent& contour) const; + ContourComponent& contour) const { + return state_->GetContourComponentAtIndex(index, contour); + } bool UpdateLinearComponentAtIndex(size_t index, - const LinearPathComponent& linear); + const LinearPathComponent& linear) { + return state_->UpdateLinearComponentAtIndex(index, linear); + } - bool UpdateQuadraticComponentAtIndex(size_t index, - const QuadraticPathComponent& quadratic); + bool UpdateQuadraticComponentAtIndex( + size_t index, + const QuadraticPathComponent& quadratic) { + return state_->UpdateQuadraticComponentAtIndex(index, quadratic); + } - bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); + bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) { + return state_->UpdateCubicComponentAtIndex(index, cubic); + } bool UpdateContourComponentAtIndex(size_t index, - const ContourComponent& contour); + const ContourComponent& contour) { + return state_->UpdateContourComponentAtIndex(index, contour); + } /// Callers must provide the scale factor for how this path will be /// transformed. /// /// It is suitable to use the max basis length of the matrix used to transform /// the path. If the provided scale is 0, curves will revert to lines. - Polyline CreatePolyline(Scalar scale) const; + Polyline CreatePolyline(Scalar scale) const { + return state_->CreatePolyline(scale); + } - std::optional GetBoundingBox() const; + std::optional GetBoundingBox() const { + return state_->GetBoundingBox(); + } - std::optional GetTransformedBoundingBox(const Matrix& transform) const; + std::optional GetTransformedBoundingBox(const Matrix& transform) const { + return state_->GetTransformedBoundingBox(transform); + } - std::optional> GetMinMaxCoveragePoints() const; + std::optional> GetMinMaxCoveragePoints() const { + return state_->GetMinMaxCoveragePoints(); + } private: friend class PathBuilder; - void SetConvexity(Convexity value); + void SetConvexity(Convexity value) { state_->SetConvexity(value); } + + struct State { + struct ComponentIndexPair { + ComponentType type = ComponentType::kLinear; + size_t index = 0; + + ComponentIndexPair() {} + + ComponentIndexPair(ComponentType a_type, size_t a_index) + : type(a_type), index(a_index) {} + }; + + size_t GetComponentCount(std::optional type = {}) const; + + void SetFillType(FillType fill); + + FillType GetFillType() const; + + bool IsConvex() const; + + void AddLinearComponent(Point p1, Point p2); + + void AddQuadraticComponent(Point p1, Point cp, Point p2); + + void AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2); + + void AddContourComponent(Point destination, bool is_closed = false); + + void SetContourClosed(bool is_closed); + + template + using Applier = std::function; + void EnumerateComponents( + const Applier& linear_applier, + const Applier& quad_applier, + const Applier& cubic_applier, + const Applier& contour_applier) const; + + bool GetLinearComponentAtIndex(size_t index, + LinearPathComponent& linear) const; + + bool GetQuadraticComponentAtIndex(size_t index, + QuadraticPathComponent& quadratic) const; + + bool GetCubicComponentAtIndex(size_t index, + CubicPathComponent& cubic) const; + + bool GetContourComponentAtIndex(size_t index, + ContourComponent& contour) const; + + bool UpdateLinearComponentAtIndex(size_t index, + const LinearPathComponent& linear); + + bool UpdateQuadraticComponentAtIndex( + size_t index, + const QuadraticPathComponent& quadratic); + + bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); + + bool UpdateContourComponentAtIndex(size_t index, + const ContourComponent& contour); + + Polyline CreatePolyline(Scalar scale) const; + + std::optional GetBoundingBox() const; + + std::optional GetTransformedBoundingBox( + const Matrix& transform) const; + + std::optional> GetMinMaxCoveragePoints() const; + + void SetConvexity(Convexity value); - struct ComponentIndexPair { - ComponentType type = ComponentType::kLinear; - size_t index = 0; + FillType fill_ = FillType::kNonZero; + Convexity convexity_ = Convexity::kUnknown; + std::vector components_; + std::vector linears_; + std::vector quads_; + std::vector cubics_; + std::vector contours_; - ComponentIndexPair() {} + void InvalidateCache() { + cached_bounding_box_.reset(); + cached_polyline_.reset(); + } - ComponentIndexPair(ComponentType a_type, size_t a_index) - : type(a_type), index(a_index) {} + mutable std::optional> cached_bounding_box_; + mutable std::optional cached_polyline_; }; - FillType fill_ = FillType::kNonZero; - Convexity convexity_ = Convexity::kUnknown; - std::vector components_; - std::vector linears_; - std::vector quads_; - std::vector cubics_; - std::vector contours_; + std::shared_ptr state_; }; } // namespace impeller diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 93855dadd488d..4c1636ac642c4 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -13,13 +13,14 @@ PathBuilder::PathBuilder() = default; PathBuilder::~PathBuilder() = default; Path PathBuilder::CopyPath(FillType fill) const { - auto path = prototype_; + auto path = prototype_.Clone(); path.SetFillType(fill); return path; } Path PathBuilder::TakePath(FillType fill) { auto path = prototype_; + prototype_ = Path(); path.SetFillType(fill); path.SetConvexity(convexity_); return path; diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 4e2c3c535f41e..0b9254bc511da 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -56,11 +56,39 @@ Tessellator::Result Tessellator::Tessellate( FillType fill_type, const Path::Polyline& polyline, const BuilderCallback& callback) const { + auto& cache = polyline.state_->tesselator_cache_; + auto it = cache.find(fill_type); + if (it != cache.end()) { + auto result = + callback(it->second.vertices.data(), it->second.vertices.size(), + it->second.indices.data(), it->second.indices.size()); + if (result) { + return Result::kSuccess; + } else { + return Result::kInputError; + } + } else { + return DoTessellate( + fill_type, polyline, + [&](const float* vertices, size_t vertices_size, + const uint16_t* indices, size_t indices_size) { + Path::Polyline::State::TesselatorData data; + data.vertices.assign(vertices, vertices + vertices_size); + data.indices.assign(indices, indices + indices_size); + cache[fill_type] = std::move(data); + return callback(vertices, vertices_size, indices, indices_size); + }); + } +} + +Tessellator::Result Tessellator::DoTessellate( + FillType fill_type, + const Path::Polyline& polyline, + const BuilderCallback& callback) const { if (!callback) { return Result::kInputError; } - - if (polyline.points.empty()) { + if (polyline.points().empty()) { return Result::kInputError; } @@ -76,7 +104,7 @@ Tessellator::Result Tessellator::Tessellate( /// Feed contour information to the tessellator. /// static_assert(sizeof(Point) == 2 * sizeof(float)); - for (size_t contour_i = 0; contour_i < polyline.contours.size(); + for (size_t contour_i = 0; contour_i < polyline.contours().size(); contour_i++) { size_t start_point_index, end_point_index; std::tie(start_point_index, end_point_index) = @@ -84,9 +112,9 @@ Tessellator::Result Tessellator::Tessellate( ::tessAddContour(tessellator, // the C tessellator kVertexSize, // - polyline.points.data() + start_point_index, // - sizeof(Point), // - end_point_index - start_point_index // + polyline.points().data() + start_point_index, // + sizeof(Point), // + end_point_index - start_point_index // ); } diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index ff93b7091a3f6..115ad428d94f7 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -64,6 +64,10 @@ class Tessellator { const BuilderCallback& callback) const; private: + Tessellator::Result DoTessellate(FillType fill_type, + const Path::Polyline& polyline, + const BuilderCallback& callback) const; + CTessellator c_tessellator_; FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); diff --git a/impeller/tessellator/tessellator_unittests.cc b/impeller/tessellator/tessellator_unittests.cc index 0b943d1391254..e2d09668c6d41 100644 --- a/impeller/tessellator/tessellator_unittests.cc +++ b/impeller/tessellator/tessellator_unittests.cc @@ -20,7 +20,7 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { [](const float* vertices, size_t vertices_size, const uint16_t* indices, size_t indices_size) { return true; }); - ASSERT_EQ(polyline.points.size(), 0u); + ASSERT_EQ(polyline.points().size(), 0u); ASSERT_EQ(result, Tessellator::Result::kInputError); } @@ -33,7 +33,7 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { FillType::kPositive, polyline, [](const float* vertices, size_t vertices_size, const uint16_t* indices, size_t indices_size) { return true; }); - ASSERT_EQ(polyline.points.size(), 1u); + ASSERT_EQ(polyline.points().size(), 1u); ASSERT_EQ(result, Tessellator::Result::kSuccess); } @@ -47,7 +47,7 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { [](const float* vertices, size_t vertices_size, const uint16_t* indices, size_t indices_size) { return true; }); - ASSERT_EQ(polyline.points.size(), 2u); + ASSERT_EQ(polyline.points().size(), 2u); ASSERT_EQ(result, Tessellator::Result::kSuccess); } @@ -65,7 +65,7 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { [](const float* vertices, size_t vertices_size, const uint16_t* indices, size_t indices_size) { return true; }); - ASSERT_EQ(polyline.points.size(), 2000u); + ASSERT_EQ(polyline.points().size(), 2000u); ASSERT_EQ(result, Tessellator::Result::kSuccess); } @@ -79,7 +79,7 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { [](const float* vertices, size_t vertices_size, const uint16_t* indices, size_t indices_size) { return false; }); - ASSERT_EQ(polyline.points.size(), 2u); + ASSERT_EQ(polyline.points().size(), 2u); ASSERT_EQ(result, Tessellator::Result::kInputError); } }