Skip to content
This repository has been archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] Performance improvement for Path::GetMinMaxCoveragePoints. #37827

Merged
merged 1 commit into from
Nov 30, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions impeller/geometry/path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -299,33 +299,37 @@ std::optional<std::pair<Point, Point>> Path::GetMinMaxCoveragePoints() const {

std::optional<Point> min, max;

auto clamp = [&min, &max](const std::vector<Point>& extrema) {
for (const auto& extremum : extrema) {
if (!min.has_value()) {
min = extremum;
}

if (!max.has_value()) {
max = extremum;
}
auto clamp = [&min, &max](const Point& point) {
if (min.has_value()) {
min->x = std::min(min->x, point.x);
min->y = std::min(min->y, point.y);
} else {
min = point;
}

min->x = std::min(min->x, extremum.x);
min->y = std::min(min->y, extremum.y);
max->x = std::max(max->x, extremum.x);
max->y = std::max(max->y, extremum.y);
if (max.has_value()) {
max->x = std::max(max->x, point.x);
max->y = std::max(max->y, point.y);
} else {
max = point;
}
};

for (const auto& linear : linears_) {
clamp(linear.Extrema());
clamp(linear.p1);
clamp(linear.p2);
}

for (const auto& quad : quads_) {
clamp(quad.Extrema());
for (const Point& point : quad.Extrema()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think this would be all that expensive. But finding the extrema for all components is overkill. Since the curve would lie entirely within the convex hull of its control points, perhaps we should just cycle over the control points instead? This would make coverage calculation way faster for not a lot of wastage.

cc @bdero

Copy link
Member

@bdero bdero Nov 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried that the degenerate case is a bit too extreme for min/maxing the control points (at least for cubics):

Screen Shot 2022-11-21 at 11 00 34 PM
Green is coverage as it's currently computed.

I took a look over this code and noticed other easy improvements we can make: For cubics, we find coverage by taking the analytical derivative (a quadratic curve) and then finding the up-to-2 solutions for y=0 for each dimension -- we can improve things here by keeping more stuff on the stack, but so far so good.
But for quadratics, QuadraticPathComponent::Extrema() "upgrades" the curve to cubic form and then finds the cubic extrema. Instead, we should just take the derivative of the quadratic (a line) and include the y=0 solution for each direction.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should file an issue for that. In the meantime, this patch is good to go.

clamp(point);
}
}

for (const auto& cubic : cubics_) {
clamp(cubic.Extrema());
for (const Point& point : cubic.Extrema()) {
clamp(point);
}
}

if (!min.has_value() || !max.has_value()) {
Expand Down