-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] Reduce allocations for polyline generation #47837
Conversation
VertexBufferBuilder<VS::PerVertexData> vtx_builder; | ||
auto polyline = path.CreatePolyline(scale); | ||
std::vector<Point> point_buffer; | ||
point_buffer.reserve(512); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
document magic numbers? what is this from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it up!
Shall I add a comment saying I made this up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That way we remember its not a Chesterton's Fence situation in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM with question on stroke tessellator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Allocation gainz.
impeller/tessellator/tessellator.cc
Outdated
const Path::Polyline& polyline, | ||
const BuilderCallback& callback) const { | ||
Tessellator::Result Tessellator::Tessellate(const Path& path, | ||
Scalar scale, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Tolerance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just gave it a quick look. While I'm on the road it's hard to focus.
impeller/geometry/path.h
Outdated
/// Points in the polyline, which may represent multiple contours specified | ||
/// by indices in |breaks|. | ||
std::vector<Point> points; | ||
std::vector<Point>& points; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me a bit nervous, storing a pointer as an instance method. Potentially you could instead recycle these and have a unique_ptr around them. So you pass it ownership when you create it. Then you take ownership, then you can reuse it with the next Polyline if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reworked the patch to use unique_ptr and have a callback fired in the dtor to reclaim it for code paths that need it. I think this cleans up the API a little bit, and it keeps the benchmark happy.
impeller/tessellator/tessellator.cc
Outdated
|
||
namespace impeller { | ||
|
||
FML_THREAD_LOCAL std::vector<Point> tls_point_buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLS isn't free, do we actually use the tessellator from multiple threads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAICT the underlying libtess2 TESStesselator
held by the impeller::Tessellator
is not thread safe.
I'd suggest moving the reusable point buffer into the impeller::Tessellator
and documenting that impeller::Tessellator
is not thread safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair, I was trying to be more defensive here but don't need to be. I'll make this just a regular ivar on impeller::Tessellator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an improvement. It is a bit awkward but I don't have a better suggestion. I was thinking of an explicit take for the points, but I can see where the function has it's benefits. Looks good, but I think we should tidy up the tests and put them in the proper location.
middle_handle, middle) | ||
.CreatePolyline(scale); | ||
std::vector<Point> arc_points; | ||
CubicPathComponent(start_offset, start_handle, middle_handle, middle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the reserve size be calculated here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are small and shouldn't result in lots of reallocation. I'd feel more comfortable having a benchmark showing this before trying to place a number in it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ask because I'm turning on a linter for this in #47868. I don't think it will flag this though since it can't trivially know what the reserve size is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reserving incorrect sizes can make things worse. If we don't know let's use the standard vec reallocation startegy
vtx.position = position - orientation; | ||
vtx_builder.AppendVertex(vtx); | ||
for (const auto& point : arc.CreatePolyline(scale)) { | ||
std::vector<Point> arc_points; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same deal. This shouldn't typically result in a large number of points.
impeller/geometry/path.h
Outdated
using PointBufferPointer = std::unique_ptr<std::vector<Point>>; | ||
using ReclaimPointBuffer = std::function<void(PointBufferPointer)>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like these using
statements make the code harder to follow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was getting tired of typing out std::unique_ptr<std::vector<Point>>
in a large number of places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to keep these I think you show follow the conventions (inconsistently) used elsewhere in the codebase, the "Ptr" and "Callback" suffixes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
impeller/geometry/path.h
Outdated
|
||
/// The buffer will be cleared and returned at the destruction of this | ||
/// polyline. | ||
Polyline(PointBufferPointer point_buffer, ReclaimPointBuffer reclaim); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit awkward but given c++'s lack of lifetimes I think this is an improvement and maybe the best we can do. I think you should move the polyline tests to a path_unittests.cc. Having the tests in a nonstandard location makes it hard to verify coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we want to break up geometry_unittests.cc maybe let's do it in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A followup PR sounds good to me. It's a bit easier to review this one if the tests are in the proper place but I can understand wanting to separate refactoring and functional changes.
impeller/tessellator/tessellator.cc
Outdated
point_buffer_->clear(); | ||
auto polyline = | ||
path.CreatePolyline(tolerance, std::move(point_buffer_), | ||
[&](Path::Polyline::PointBufferPointer point_buffer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lambda capture can be reduced to [this]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
impeller/tessellator/tessellator.cc
Outdated
// 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]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a method to Polyline
that returns a reference to the point at a given index
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
}); | ||
} | ||
|
||
TEST(GeometryTest, PolylineFailsWithNullptrBuffer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding these. A nullptr test for the reclaim callback would be nice too.
auto label is removed for flutter/engine/47837, due to - The status or check suite Linux linux_android_aot_engine has failed. Please fix the issues identified (or deflake) before re-applying this label. |
auto label is removed for flutter/engine/47837, due to - The status or check suite Linux mac_unopt has failed. Please fix the issues identified (or deflake) before re-applying this label. |
We calculated more points using less CPU time. |
…138266) flutter/engine@9d8a112...00db306 2023-11-10 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Bump minSdk to 19 for Android tests" (flutter/engine#47935) 2023-11-10 [email protected] Roll Dart SDK from 91c4a92a64ea to 370145bbbd4f (1 revision) (flutter/engine#47930) 2023-11-10 [email protected] Roll Skia from 2c43bf002b7f to 96ce4d6f433d (3 revisions) (flutter/engine#47931) 2023-11-10 [email protected] [Impeller] implement Canvas::DrawLine to tesselate lines directly (flutter/engine#47846) 2023-11-10 [email protected] Roll Skia from d06840545bff to 2c43bf002b7f (1 revision) (flutter/engine#47928) 2023-11-10 [email protected] Bump minSdk to 19 for Android tests (flutter/engine#47686) 2023-11-10 [email protected] [Impeller] Reduce allocations for polyline generation (flutter/engine#47837) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Before this patch, polyline generation did lots of small allocations over and over again.
Now,
Path::CreatePolyline
takes astd::vector<Point>
that the caller can reserve such that it won't likely need to be resized. The tessellator has been updated to hold a thread_local point buffer that gets reused and avoids lots of intra-frame allocation. This improves the profiles seen in flutter/flutter#138004, specifically around theFillPointsForPolyline
timeThis very significantly improves the benchmarking data in geometry_benchmarks.
The first commit in this patch slightly improves the benchmarks by reducing some allocations, but not enough of them.