Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upChange how point_queue and active_edges are sorted for performance #335
Conversation
ad27978
to
96ac2c9
These curves are eliminated prior to the tiling phase in |
|
Wow, this is a ~30% win on I think I'd prefer to keep around |
| let index = self.stops.binary_search_by(|other| { | ||
| other.offset.partial_cmp(&stop.offset).unwrap() | ||
| }).unwrap_or_else(convert::identity); | ||
| self.stops.insert(index, stop); |
This comment has been minimized.
This comment has been minimized.
pcwalton
May 11, 2020
Collaborator
Wouldn't it be faster to keep the old sorted vector code since insert has to be O(n) anyway?
This comment has been minimized.
This comment has been minimized.
Veedrac
May 11, 2020
Author
Contributor
This is almost the same code SortedVector used, just inlined.
I did change unwrap_or(Less) to just unwrap(), and am sorting only on offset, which now that I think about it might not be valid. What's the expected behaviour for gradients with two points at the same offset?
This comment has been minimized.
This comment has been minimized.
pcwalton
May 11, 2020
Collaborator
Here's what the spec says:
If multiple stops are added at the same offset on a gradient, then they must be placed in the order added, with the first one closest to the start of the gradient, and each subsequent one infinitesimally further along towards the end point (in effect causing all but the first and last stop added at each point to be ignored).
This comment has been minimized.
This comment has been minimized.
Veedrac
May 11, 2020
Author
Contributor
Ah, then both versions are actually wrong, since binary_search can return ‘any one of the matches’ to insert at. I'll add a fix.
point_queue is only sorted by y coordinate to coordinate work to one strip at a time. This is more efficiently handled by punting later work into a next_point_queue, and never explicitly sorting the data. active_edges is only sorted by x coordinate for the tile-by-tile work in process_old_active_edges. It is much more efficient to sort once inside that function.
Zero-width gradient sections should be ‘ignored’. The visible endpoints should be precisely the first and last colors added at that point. https://html.spec.whatwg.org/multipage/canvas.html#interpolation
|
Since ordering turns out to be rather subtle for gradients, it seems better to have the logic inline, rather than the responsibility of |
|
Agreed. Thanks for the PR! |
Veedrac commentedMay 10, 2020
•
edited
My understanding is that every path, which is a set of connected sequences of Bezier curves, is individually tiled. The tiling procedure descends from appropriate nodes, splitting off line segments from its Beziers piece by piece, and splitting those line segments if they cross between a 16-tall strip. Once a strip has been allocated its line segments, it is partitioned into 16x16 tiles.
I believe
point_queueis only sorted byycoordinate to coordinate work to one strip at a time. This is more efficiently handled by punting later work into anext_point_queue, and never explicitly sorting the data. This does mean you might punt future work several times before it is actually handled, but this is not an issue in examples I tested, and can be solved if need be.Similarly I believe
active_edgesis only sorted byxcoordinate for the tile-by-tile work inprocess_old_active_edges. It is much more efficient to sort once inside that function.SortedVectoris then only used in one place to sort gradients, so I removed it and inlined the functionality.Speaking speculatively, it looks like it would be more efficient, both in CPU time and in the number of line segments generated, to partition the Bezier curves directly into the strips (you'd only need to solve a cubic), and then directly use a Bezier flattening algorithm on each segment. This has a few advantages: you partition exactly at tile boundaries, you don't need to redo partitioning work each time you bite a piece of the curve off, and you can use better partitioning algorithms (like this?).
One thing I don't understand is how the code handles Bezier curves that extend above the strip containing their endpoints. The code only seems to check the
ycoordinates of their endpoints.