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

Conversation

dnfield
Copy link
Contributor

@dnfield dnfield commented Nov 8, 2023

Before this patch, polyline generation did lots of small allocations over and over again.

Now, Path::CreatePolyline takes a std::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 the FillPointsForPolyline time

This 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.

VertexBufferBuilder<VS::PerVertexData> vtx_builder;
auto polyline = path.CreatePolyline(scale);
std::vector<Point> point_buffer;
point_buffer.reserve(512);
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes please!

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

@jonahwilliams jonahwilliams left a 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.

Copy link
Member

@bdero bdero left a comment

Choose a reason for hiding this comment

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

LGTM. Allocation gainz.

const Path::Polyline& polyline,
const BuilderCallback& callback) const {
Tessellator::Result Tessellator::Tessellate(const Path& path,
Scalar scale,
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Tolerance?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

@gaaclarke gaaclarke left a 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.

/// Points in the polyline, which may represent multiple contours specified
/// by indices in |breaks|.
std::vector<Point> points;
std::vector<Point>& points;
Copy link
Member

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.

Copy link
Contributor Author

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.


namespace impeller {

FML_THREAD_LOCAL std::vector<Point> tls_point_buffer;
Copy link
Member

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?

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

@gaaclarke gaaclarke left a 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)
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor

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;
Copy link
Member

Choose a reason for hiding this comment

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

Or here?

Copy link
Contributor Author

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.

Comment on lines 100 to 101
using PointBufferPointer = std::unique_ptr<std::vector<Point>>;
using ReclaimPointBuffer = std::function<void(PointBufferPointer)>;
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


/// The buffer will be cleared and returned at the destruction of this
/// polyline.
Polyline(PointBufferPointer point_buffer, ReclaimPointBuffer reclaim);
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

point_buffer_->clear();
auto polyline =
path.CreatePolyline(tolerance, std::move(point_buffer_),
[&](Path::Polyline::PointBufferPointer point_buffer) {
Copy link
Member

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]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

// 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]) {
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@dnfield dnfield added autosubmit Merge PR when tree becomes green via auto submit App and removed autosubmit Merge PR when tree becomes green via auto submit App labels Nov 9, 2023
Copy link
Member

@gaaclarke gaaclarke left a 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) {
Copy link
Member

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.

@dnfield dnfield added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 9, 2023
@auto-submit auto-submit bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 9, 2023
Copy link
Contributor

auto-submit bot commented Nov 9, 2023

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.

@dnfield dnfield added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 10, 2023
Copy link
Contributor

auto-submit bot commented Nov 10, 2023

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.

@auto-submit auto-submit bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 10, 2023
@dnfield dnfield added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 10, 2023
@auto-submit auto-submit bot merged commit 1ca461b into flutter:main Nov 10, 2023
@dnfield dnfield deleted the alloc branch November 10, 2023 21:35
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 10, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 10, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 10, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 10, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 10, 2023
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Nov 11, 2023
…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
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
autosubmit Merge PR when tree becomes green via auto submit App e: impeller
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants