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

Commit 46ff024

Browse files
[Impeller] Reland: remove most temporary allocation during polyline generation. (#52180)
Part of flutter/flutter#143077 Only allocate into reused arenas instead of allocating a new vector of data. Fixes flutter/flutter#133348 Also moves tessellation logic into the c/tessellator and out of the impeller tessellator. This was necessary to fix a compilation error. introduced by including host_buffer -> allocator -> fml mapping -> window.h include which has a function definition that conflicts with the c tessellator definition. Fixes missing points in case a filled path is not explicitly closed.
1 parent 6abfa56 commit 46ff024

14 files changed

+513
-429
lines changed

impeller/core/range.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
#include <cstddef>
99

10-
#include "flutter/fml/macros.h"
11-
1210
namespace impeller {
1311

1412
struct Range {

impeller/entity/geometry/fill_path_geometry.cc

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,8 @@ GeometryResult FillPathGeometry::GetPositionBuffer(
3636
};
3737
}
3838

39-
VertexBuffer vertex_buffer;
40-
41-
auto points = renderer.GetTessellator()->TessellateConvex(
42-
path_, entity.GetTransform().GetMaxBasisLength());
43-
44-
vertex_buffer.vertex_buffer = host_buffer.Emplace(
45-
points.data(), points.size() * sizeof(Point), alignof(Point));
46-
vertex_buffer.index_buffer = {}, vertex_buffer.vertex_count = points.size();
47-
vertex_buffer.index_type = IndexType::kNone;
39+
VertexBuffer vertex_buffer = renderer.GetTessellator()->TessellateConvex(
40+
path_, host_buffer, entity.GetTransform().GetMaxBasisLength());
4841

4942
return GeometryResult{
5043
.type = PrimitiveType::kTriangleStrip,
@@ -61,8 +54,6 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer(
6154
const ContentContext& renderer,
6255
const Entity& entity,
6356
RenderPass& pass) const {
64-
using VS = TextureFillVertexShader;
65-
6657
const auto& bounding_box = path_.GetBoundingBox();
6758
if (bounding_box.has_value() && bounding_box->IsEmpty()) {
6859
return GeometryResult{
@@ -80,22 +71,13 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer(
8071
auto uv_transform =
8172
texture_coverage.GetNormalizingTransform() * effect_transform;
8273

83-
auto points = renderer.GetTessellator()->TessellateConvex(
84-
path_, entity.GetTransform().GetMaxBasisLength());
85-
86-
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
87-
vertex_builder.Reserve(points.size());
88-
for (auto i = 0u; i < points.size(); i++) {
89-
VS::PerVertexData data;
90-
data.position = points[i];
91-
data.texture_coords = uv_transform * points[i];
92-
vertex_builder.AppendVertex(data);
93-
}
74+
VertexBuffer vertex_buffer = renderer.GetTessellator()->TessellateConvex(
75+
path_, renderer.GetTransientsBuffer(),
76+
entity.GetTransform().GetMaxBasisLength(), uv_transform);
9477

9578
return GeometryResult{
9679
.type = PrimitiveType::kTriangleStrip,
97-
.vertex_buffer =
98-
vertex_builder.CreateVertexBuffer(renderer.GetTransientsBuffer()),
80+
.vertex_buffer = vertex_buffer,
9981
.transform = entity.GetShaderTransform(pass),
10082
.mode = GetResultMode(),
10183
};

impeller/geometry/geometry_benchmarks.cc

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -59,39 +59,22 @@ template <class... Args>
5959
static void BM_Polyline(benchmark::State& state, Args&&... args) {
6060
auto args_tuple = std::make_tuple(std::move(args)...);
6161
auto path = std::get<Path>(args_tuple);
62-
bool tessellate = std::get<bool>(args_tuple);
6362

6463
size_t point_count = 0u;
6564
size_t single_point_count = 0u;
6665
auto points = std::make_unique<std::vector<Point>>();
6766
points->reserve(2048);
6867
while (state.KeepRunning()) {
69-
if (tessellate) {
70-
tess.Tessellate(path, 1.0f,
71-
[&point_count, &single_point_count](
72-
const float* vertices, size_t vertices_count,
73-
const uint16_t* indices, size_t indices_count) {
74-
if (indices_count > 0) {
75-
single_point_count = indices_count;
76-
point_count += indices_count;
77-
} else {
78-
single_point_count = vertices_count;
79-
point_count += vertices_count;
80-
}
81-
return true;
82-
});
83-
} else {
84-
auto polyline = path.CreatePolyline(
85-
// Clang-tidy doesn't know that the points get moved back before
86-
// getting moved again in this loop.
87-
// NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
88-
1.0f, std::move(points),
89-
[&points](Path::Polyline::PointBufferPtr reclaimed) {
90-
points = std::move(reclaimed);
91-
});
92-
single_point_count = polyline.points->size();
93-
point_count += single_point_count;
94-
}
68+
auto polyline = path.CreatePolyline(
69+
// Clang-tidy doesn't know that the points get moved back before
70+
// getting moved again in this loop.
71+
// NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
72+
1.0f, std::move(points),
73+
[&points](Path::Polyline::PointBufferPtr reclaimed) {
74+
points = std::move(reclaimed);
75+
});
76+
single_point_count = polyline.points->size();
77+
point_count += single_point_count;
9578
}
9679
state.counters["SinglePointCount"] = single_point_count;
9780
state.counters["TotalPointCount"] = point_count;
@@ -155,11 +138,13 @@ static void BM_Convex(benchmark::State& state, Args&&... args) {
155138
size_t point_count = 0u;
156139
size_t single_point_count = 0u;
157140
auto points = std::make_unique<std::vector<Point>>();
141+
auto indices = std::make_unique<std::vector<uint16_t>>();
158142
points->reserve(2048);
143+
indices->reserve(2048);
159144
while (state.KeepRunning()) {
160-
auto points = tess.TessellateConvex(path, 1.0f);
161-
single_point_count = points.size();
162-
point_count += points.size();
145+
tess.TessellateConvexInternal(path, *points, *indices, 1.0f);
146+
single_point_count = indices->size();
147+
point_count += indices->size();
163148
}
164149
state.counters["SinglePointCount"] = single_point_count;
165150
state.counters["TotalPointCount"] = point_count;
@@ -182,27 +167,17 @@ static void BM_Convex(benchmark::State& state, Args&&... args) {
182167
MAKE_STROKE_BENCHMARK_CAPTURE_CAPS_JOINS(path, _uvNoTx, UVMode::kUVRect)
183168

184169
BENCHMARK_CAPTURE(BM_Polyline, cubic_polyline, CreateCubic(true), false);
185-
BENCHMARK_CAPTURE(BM_Polyline, cubic_polyline_tess, CreateCubic(true), true);
186170
BENCHMARK_CAPTURE(BM_Polyline,
187171
unclosed_cubic_polyline,
188172
CreateCubic(false),
189173
false);
190-
BENCHMARK_CAPTURE(BM_Polyline,
191-
unclosed_cubic_polyline_tess,
192-
CreateCubic(false),
193-
true);
194174
MAKE_STROKE_BENCHMARK_CAPTURE_UVS(Cubic);
195175

196176
BENCHMARK_CAPTURE(BM_Polyline, quad_polyline, CreateQuadratic(true), false);
197-
BENCHMARK_CAPTURE(BM_Polyline, quad_polyline_tess, CreateQuadratic(true), true);
198177
BENCHMARK_CAPTURE(BM_Polyline,
199178
unclosed_quad_polyline,
200179
CreateQuadratic(false),
201180
false);
202-
BENCHMARK_CAPTURE(BM_Polyline,
203-
unclosed_quad_polyline_tess,
204-
CreateQuadratic(false),
205-
true);
206181
MAKE_STROKE_BENCHMARK_CAPTURE_UVS(Quadratic);
207182

208183
BENCHMARK_CAPTURE(BM_Convex, rrect_convex, CreateRRect(), true);

impeller/geometry/path.cc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,54 @@ std::optional<Rect> Path::GetTransformedBoundingBox(
349349
return bounds->TransformBounds(transform);
350350
}
351351

352+
void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
353+
auto& path_components = data_->components;
354+
auto& path_points = data_->points;
355+
356+
for (size_t component_i = 0; component_i < path_components.size();
357+
component_i++) {
358+
const auto& path_component = path_components[component_i];
359+
switch (path_component.type) {
360+
case ComponentType::kLinear: {
361+
const LinearPathComponent* linear =
362+
reinterpret_cast<const LinearPathComponent*>(
363+
&path_points[path_component.index]);
364+
writer.Write(linear->p2);
365+
break;
366+
}
367+
case ComponentType::kQuadratic: {
368+
const QuadraticPathComponent* quad =
369+
reinterpret_cast<const QuadraticPathComponent*>(
370+
&path_points[path_component.index]);
371+
quad->ToLinearPathComponents(scale, writer);
372+
break;
373+
}
374+
case ComponentType::kCubic: {
375+
const CubicPathComponent* cubic =
376+
reinterpret_cast<const CubicPathComponent*>(
377+
&path_points[path_component.index]);
378+
cubic->ToLinearPathComponents(scale, writer);
379+
break;
380+
}
381+
case ComponentType::kContour:
382+
if (component_i == path_components.size() - 1) {
383+
// If the last component is a contour, that means it's an empty
384+
// contour, so skip it.
385+
continue;
386+
}
387+
writer.EndContour();
388+
389+
// Insert contour start.
390+
const auto& next_component = path_components[component_i + 1];
391+
// It doesn't matter what type this is as all structs are laid out
392+
// with p1 as the first member.
393+
const LinearPathComponent* linear =
394+
reinterpret_cast<const LinearPathComponent*>(
395+
&path_points[next_component.index]);
396+
writer.Write(linear->p1);
397+
break;
398+
}
399+
}
400+
}
401+
352402
} // namespace impeller

impeller/geometry/path.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <vector>
1212

1313
#include "impeller/geometry/path_component.h"
14+
#include "impeller/geometry/rect.h"
1415

1516
namespace impeller {
1617

@@ -168,6 +169,13 @@ class Path {
168169
std::make_unique<std::vector<Point>>(),
169170
Polyline::ReclaimPointBufferCallback reclaim = nullptr) const;
170171

172+
/// Generate a polyline into the temporary storage held by the [writer].
173+
///
174+
/// It is suitable to use the max basis length of the matrix used to transform
175+
/// the path. If the provided scale is 0, curves will revert to straight
176+
/// lines.
177+
void WritePolyline(Scalar scale, VertexWriter& writer) const;
178+
171179
std::optional<Rect> GetBoundingBox() const;
172180

173181
std::optional<Rect> GetTransformedBoundingBox(const Matrix& transform) const;

impeller/geometry/path_component.cc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,67 @@
1010

1111
namespace impeller {
1212

13+
VertexWriter::VertexWriter(std::vector<Point>& points,
14+
std::vector<uint16_t>& indices,
15+
std::optional<Matrix> uv_transform)
16+
: points_(points), indices_(indices), uv_transform_(uv_transform) {}
17+
18+
void VertexWriter::EndContour() {
19+
if (points_.size() == 0u || contour_start_ == points_.size() - 1) {
20+
// Empty or first contour.
21+
return;
22+
}
23+
24+
auto start = contour_start_;
25+
auto end = points_.size() - 1;
26+
// All filled paths are drawn as if they are closed, but if
27+
// there is an explicit close then a lineTo to the origin
28+
// is inserted. This point isn't strictly necesary to
29+
// correctly render the shape and can be dropped.
30+
if (points_[end] == points_[start]) {
31+
end--;
32+
}
33+
34+
if (contour_start_ > 0) {
35+
// Triangle strip break.
36+
indices_.emplace_back(indices_.back());
37+
indices_.emplace_back(start);
38+
indices_.emplace_back(start);
39+
40+
// If the contour has an odd number of points, insert an extra point when
41+
// bridging to the next contour to preserve the correct triangle winding
42+
// order.
43+
if (previous_contour_odd_points_) {
44+
indices_.emplace_back(start);
45+
}
46+
} else {
47+
indices_.emplace_back(start);
48+
}
49+
50+
size_t a = start + 1;
51+
size_t b = end;
52+
while (a < b) {
53+
indices_.emplace_back(a);
54+
indices_.emplace_back(b);
55+
a++;
56+
b--;
57+
}
58+
if (a == b) {
59+
indices_.emplace_back(a);
60+
previous_contour_odd_points_ = false;
61+
} else {
62+
previous_contour_odd_points_ = true;
63+
}
64+
contour_start_ = points_.size();
65+
}
66+
67+
void VertexWriter::Write(Point point) {
68+
points_.emplace_back(point);
69+
if (uv_transform_.has_value()) {
70+
points_.emplace_back(*uv_transform_ * point);
71+
}
72+
}
73+
1374
/*
1475
* Based on: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Specific_cases
1576
*/
@@ -119,6 +180,16 @@ void QuadraticPathComponent::ToLinearPathComponents(
119180
proc(p2);
120181
}
121182

183+
void QuadraticPathComponent::ToLinearPathComponents(
184+
Scalar scale,
185+
VertexWriter& writer) const {
186+
Scalar line_count = std::ceilf(ComputeQuadradicSubdivisions(scale, *this));
187+
for (size_t i = 1; i < line_count; i += 1) {
188+
writer.Write(Solve(i / line_count));
189+
}
190+
writer.Write(p2);
191+
}
192+
122193
std::vector<Point> QuadraticPathComponent::Extrema() const {
123194
CubicPathComponent elevated(*this);
124195
return elevated.Extrema();
@@ -189,6 +260,15 @@ void CubicPathComponent::ToLinearPathComponents(Scalar scale,
189260
proc(p2);
190261
}
191262

263+
void CubicPathComponent::ToLinearPathComponents(Scalar scale,
264+
VertexWriter& writer) const {
265+
Scalar line_count = std::ceilf(ComputeCubicSubdivisions(scale, *this));
266+
for (size_t i = 1; i < line_count; i++) {
267+
writer.Write(Solve(i / line_count));
268+
}
269+
writer.Write(p2);
270+
}
271+
192272
static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) {
193273
return (a > (b - epsilon)) && (a < (b + epsilon));
194274
}

impeller/geometry/path_component.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,34 @@
1010
#include <variant>
1111
#include <vector>
1212

13+
#include "impeller/geometry/matrix.h"
1314
#include "impeller/geometry/point.h"
14-
#include "impeller/geometry/rect.h"
1515
#include "impeller/geometry/scalar.h"
1616

1717
namespace impeller {
1818

19+
/// @brief An interface for generating a multi contour polyline as a triangle
20+
/// strip.
21+
class VertexWriter {
22+
public:
23+
explicit VertexWriter(std::vector<Point>& points,
24+
std::vector<uint16_t>& indices,
25+
std::optional<Matrix> uv_transform);
26+
27+
~VertexWriter() = default;
28+
29+
void EndContour();
30+
31+
void Write(Point point);
32+
33+
private:
34+
bool previous_contour_odd_points_ = false;
35+
size_t contour_start_ = 0u;
36+
std::vector<Point>& points_;
37+
std::vector<uint16_t>& indices_;
38+
std::optional<Matrix> uv_transform_;
39+
};
40+
1941
struct LinearPathComponent {
2042
Point p1;
2143
Point p2;
@@ -64,6 +86,8 @@ struct QuadraticPathComponent {
6486

6587
void ToLinearPathComponents(Scalar scale_factor, const PointProc& proc) const;
6688

89+
void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
90+
6791
std::vector<Point> Extrema() const;
6892

6993
bool operator==(const QuadraticPathComponent& other) const {
@@ -109,6 +133,8 @@ struct CubicPathComponent {
109133

110134
void ToLinearPathComponents(Scalar scale, const PointProc& proc) const;
111135

136+
void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
137+
112138
CubicPathComponent Subsegment(Scalar t0, Scalar t1) const;
113139

114140
bool operator==(const CubicPathComponent& other) const {

0 commit comments

Comments
 (0)