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

Commit 2420fb0

Browse files
authored
[Impeller] Add direct tesselation of circles for DrawCircle and Round end caps (#48103)
Note: The GPU tesselation for drawPoints is disabled in this test PR, but will be re-enabled when I can run some benchmarks to see where a good cutoff exists. Tessellation for circular or quarter circular paths is now handled by a single tessellator class that provides services for DrawCircle, DrawPoints (round caps), and DrawLines (round caps). Creating a general path is avoided along with the associated overhead of recognizing and using a generic tessellation step.
1 parent ab77461 commit 2420fb0

22 files changed

+1018
-91
lines changed

ci/licenses_golden/excluded_files

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
../../../flutter/impeller/geometry/path_unittests.cc
157157
../../../flutter/impeller/geometry/rect_unittests.cc
158158
../../../flutter/impeller/geometry/size_unittests.cc
159+
../../../flutter/impeller/geometry/trig_unittests.cc
159160
../../../flutter/impeller/golden_tests/README.md
160161
../../../flutter/impeller/golden_tests_harvester/.dart_tool
161162
../../../flutter/impeller/golden_tests_harvester/.gitignore
@@ -193,6 +194,7 @@
193194
../../../flutter/impeller/scene/importer/importer_unittests.cc
194195
../../../flutter/impeller/scene/scene_unittests.cc
195196
../../../flutter/impeller/shader_archive/shader_archive_unittests.cc
197+
../../../flutter/impeller/tessellator/circle_tessellator_unittests.cc
196198
../../../flutter/impeller/tessellator/dart/.dart_tool
197199
../../../flutter/impeller/tessellator/dart/pubspec.lock
198200
../../../flutter/impeller/tessellator/dart/pubspec.yaml

ci/licenses_golden/licenses_flutter

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5101,6 +5101,8 @@ ORIGIN: ../../../flutter/impeller/entity/entity_playground.cc + ../../../flutter
51015101
ORIGIN: ../../../flutter/impeller/entity/entity_playground.h + ../../../flutter/LICENSE
51025102
ORIGIN: ../../../flutter/impeller/entity/geometry/cover_geometry.cc + ../../../flutter/LICENSE
51035103
ORIGIN: ../../../flutter/impeller/entity/geometry/cover_geometry.h + ../../../flutter/LICENSE
5104+
ORIGIN: ../../../flutter/impeller/entity/geometry/ellipse_geometry.cc + ../../../flutter/LICENSE
5105+
ORIGIN: ../../../flutter/impeller/entity/geometry/ellipse_geometry.h + ../../../flutter/LICENSE
51045106
ORIGIN: ../../../flutter/impeller/entity/geometry/fill_path_geometry.cc + ../../../flutter/LICENSE
51055107
ORIGIN: ../../../flutter/impeller/entity/geometry/fill_path_geometry.h + ../../../flutter/LICENSE
51065108
ORIGIN: ../../../flutter/impeller/entity/geometry/geometry.cc + ../../../flutter/LICENSE
@@ -5206,6 +5208,8 @@ ORIGIN: ../../../flutter/impeller/geometry/sigma.cc + ../../../flutter/LICENSE
52065208
ORIGIN: ../../../flutter/impeller/geometry/sigma.h + ../../../flutter/LICENSE
52075209
ORIGIN: ../../../flutter/impeller/geometry/size.cc + ../../../flutter/LICENSE
52085210
ORIGIN: ../../../flutter/impeller/geometry/size.h + ../../../flutter/LICENSE
5211+
ORIGIN: ../../../flutter/impeller/geometry/trig.cc + ../../../flutter/LICENSE
5212+
ORIGIN: ../../../flutter/impeller/geometry/trig.h + ../../../flutter/LICENSE
52095213
ORIGIN: ../../../flutter/impeller/geometry/type_traits.cc + ../../../flutter/LICENSE
52105214
ORIGIN: ../../../flutter/impeller/geometry/type_traits.h + ../../../flutter/LICENSE
52115215
ORIGIN: ../../../flutter/impeller/geometry/vector.cc + ../../../flutter/LICENSE
@@ -5523,6 +5527,8 @@ ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive_writer.cc + ../.
55235527
ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive_writer.h + ../../../flutter/LICENSE
55245528
ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.cc + ../../../flutter/LICENSE
55255529
ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.h + ../../../flutter/LICENSE
5530+
ORIGIN: ../../../flutter/impeller/tessellator/circle_tessellator.cc + ../../../flutter/LICENSE
5531+
ORIGIN: ../../../flutter/impeller/tessellator/circle_tessellator.h + ../../../flutter/LICENSE
55265532
ORIGIN: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart + ../../../flutter/LICENSE
55275533
ORIGIN: ../../../flutter/impeller/tessellator/tessellator.cc + ../../../flutter/LICENSE
55285534
ORIGIN: ../../../flutter/impeller/tessellator/tessellator.h + ../../../flutter/LICENSE
@@ -7893,6 +7899,8 @@ FILE: ../../../flutter/impeller/entity/entity_playground.cc
78937899
FILE: ../../../flutter/impeller/entity/entity_playground.h
78947900
FILE: ../../../flutter/impeller/entity/geometry/cover_geometry.cc
78957901
FILE: ../../../flutter/impeller/entity/geometry/cover_geometry.h
7902+
FILE: ../../../flutter/impeller/entity/geometry/ellipse_geometry.cc
7903+
FILE: ../../../flutter/impeller/entity/geometry/ellipse_geometry.h
78967904
FILE: ../../../flutter/impeller/entity/geometry/fill_path_geometry.cc
78977905
FILE: ../../../flutter/impeller/entity/geometry/fill_path_geometry.h
78987906
FILE: ../../../flutter/impeller/entity/geometry/geometry.cc
@@ -7998,6 +8006,8 @@ FILE: ../../../flutter/impeller/geometry/sigma.cc
79988006
FILE: ../../../flutter/impeller/geometry/sigma.h
79998007
FILE: ../../../flutter/impeller/geometry/size.cc
80008008
FILE: ../../../flutter/impeller/geometry/size.h
8009+
FILE: ../../../flutter/impeller/geometry/trig.cc
8010+
FILE: ../../../flutter/impeller/geometry/trig.h
80018011
FILE: ../../../flutter/impeller/geometry/type_traits.cc
80028012
FILE: ../../../flutter/impeller/geometry/type_traits.h
80038013
FILE: ../../../flutter/impeller/geometry/vector.cc
@@ -8316,6 +8326,8 @@ FILE: ../../../flutter/impeller/shader_archive/shader_archive_writer.cc
83168326
FILE: ../../../flutter/impeller/shader_archive/shader_archive_writer.h
83178327
FILE: ../../../flutter/impeller/tessellator/c/tessellator.cc
83188328
FILE: ../../../flutter/impeller/tessellator/c/tessellator.h
8329+
FILE: ../../../flutter/impeller/tessellator/circle_tessellator.cc
8330+
FILE: ../../../flutter/impeller/tessellator/circle_tessellator.h
83198331
FILE: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart
83208332
FILE: ../../../flutter/impeller/tessellator/tessellator.cc
83218333
FILE: ../../../flutter/impeller/tessellator/tessellator.h

impeller/aiks/aiks_unittests.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,32 @@ TEST_P(AiksTest, DrawLinesRenderCorrectly) {
20602060
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
20612061
}
20622062

2063+
TEST_P(AiksTest, FillCirclesRenderCorrectly) {
2064+
Canvas canvas;
2065+
canvas.Scale(GetContentScale());
2066+
Paint paint;
2067+
const int color_count = 3;
2068+
Color colors[color_count] = {
2069+
Color::Blue(),
2070+
Color::Green(),
2071+
Color::Crimson(),
2072+
};
2073+
2074+
int c_index = 0;
2075+
int radius = 600;
2076+
while (radius > 0) {
2077+
paint.color = colors[(c_index++) % color_count];
2078+
canvas.DrawCircle({10, 10}, radius, paint);
2079+
if (radius > 30) {
2080+
radius -= 10;
2081+
} else {
2082+
radius -= 2;
2083+
}
2084+
}
2085+
2086+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2087+
}
2088+
20632089
TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
20642090
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
20652091
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {

impeller/aiks/canvas.cc

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -232,17 +232,6 @@ bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
232232
}
233233

234234
void Canvas::DrawLine(const Point& p0, const Point& p1, const Paint& paint) {
235-
if (paint.stroke_cap == Cap::kRound) {
236-
auto path = PathBuilder{}
237-
.AddLine((p0), (p1))
238-
.SetConvexity(Convexity::kConvex)
239-
.TakePath();
240-
Paint stroke_paint = paint;
241-
stroke_paint.style = Paint::Style::kStroke;
242-
DrawPath(path, stroke_paint);
243-
return;
244-
}
245-
246235
Entity entity;
247236
entity.SetTransform(GetCurrentTransform());
248237
entity.SetClipDepth(GetClipDepth());
@@ -298,20 +287,33 @@ void Canvas::DrawRRect(Rect rect, Point corner_radii, const Paint& paint) {
298287
}
299288

300289
void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
290+
if (paint.style == Paint::Style::kStroke) {
291+
auto circle_path =
292+
PathBuilder{}
293+
.AddCircle(center, radius)
294+
.SetConvexity(Convexity::kConvex)
295+
.SetBounds(Rect::MakeLTRB(center.x - radius, center.y - radius,
296+
center.x + radius, center.y + radius))
297+
.TakePath();
298+
DrawPath(circle_path, paint);
299+
return;
300+
}
301+
301302
Size half_size(radius, radius);
302303
if (AttemptDrawBlurredRRect(
303304
Rect::MakeOriginSize(center - half_size, half_size * 2), radius,
304305
paint)) {
305306
return;
306307
}
307-
auto circle_path =
308-
PathBuilder{}
309-
.AddCircle(center, radius)
310-
.SetConvexity(Convexity::kConvex)
311-
.SetBounds(Rect::MakeLTRB(center.x - radius, center.y - radius,
312-
center.x + radius, center.y + radius))
313-
.TakePath();
314-
DrawPath(circle_path, paint);
308+
309+
Entity entity;
310+
entity.SetTransform(GetCurrentTransform());
311+
entity.SetClipDepth(GetClipDepth());
312+
entity.SetBlendMode(paint.blend_mode);
313+
entity.SetContents(paint.WithFilters(
314+
paint.CreateContentsForGeometry(Geometry::MakeCircle(center, radius))));
315+
316+
GetCurrentPass().AddEntity(entity);
315317
}
316318

317319
void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {

impeller/entity/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ impeller_component("entity") {
193193
"entity_pass_target.h",
194194
"geometry/cover_geometry.cc",
195195
"geometry/cover_geometry.h",
196+
"geometry/ellipse_geometry.cc",
197+
"geometry/ellipse_geometry.h",
196198
"geometry/fill_path_geometry.cc",
197199
"geometry/fill_path_geometry.h",
198200
"geometry/geometry.cc",
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/entity/geometry/ellipse_geometry.h"
6+
7+
#include "flutter/impeller/tessellator/circle_tessellator.h"
8+
9+
namespace impeller {
10+
11+
EllipseGeometry::EllipseGeometry(Point center, Scalar radius)
12+
: center_(center), radius_(radius) {
13+
FML_DCHECK(radius >= 0);
14+
}
15+
16+
GeometryResult EllipseGeometry::GetPositionBuffer(
17+
const ContentContext& renderer,
18+
const Entity& entity,
19+
RenderPass& pass) const {
20+
auto& host_buffer = pass.GetTransientsBuffer();
21+
using VT = SolidFillVertexShader::PerVertexData;
22+
23+
Scalar radius = radius_;
24+
const Point& center = center_;
25+
std::shared_ptr<Tessellator> tessellator = renderer.GetTessellator();
26+
CircleTessellator circle_tessellator(tessellator, entity.GetTransform(),
27+
radius_);
28+
size_t count = circle_tessellator.GetCircleVertexCount();
29+
auto vertex_buffer = host_buffer.Emplace(
30+
count * sizeof(VT), alignof(VT),
31+
[&circle_tessellator, &center, radius](uint8_t* buffer) {
32+
auto vertices = reinterpret_cast<VT*>(buffer);
33+
circle_tessellator.GenerateCircleTriangleStrip(
34+
[&vertices](const Point& p) { //
35+
*vertices++ = {
36+
.position = p,
37+
};
38+
},
39+
center, radius);
40+
});
41+
42+
return GeometryResult{
43+
.type = PrimitiveType::kTriangleStrip,
44+
.vertex_buffer =
45+
{
46+
.vertex_buffer = vertex_buffer,
47+
.vertex_count = count,
48+
.index_type = IndexType::kNone,
49+
},
50+
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
51+
entity.GetTransform(),
52+
.prevent_overdraw = false,
53+
};
54+
}
55+
56+
// |Geometry|
57+
GeometryResult EllipseGeometry::GetPositionUVBuffer(
58+
Rect texture_coverage,
59+
Matrix effect_transform,
60+
const ContentContext& renderer,
61+
const Entity& entity,
62+
RenderPass& pass) const {
63+
auto& host_buffer = pass.GetTransientsBuffer();
64+
using VT = TextureFillVertexShader::PerVertexData;
65+
auto uv_transform =
66+
texture_coverage.GetNormalizingTransform() * effect_transform;
67+
68+
Scalar radius = radius_;
69+
const Point& center = center_;
70+
std::shared_ptr<Tessellator> tessellator = renderer.GetTessellator();
71+
CircleTessellator circle_tessellator(tessellator, entity.GetTransform(),
72+
radius_);
73+
size_t count = circle_tessellator.GetCircleVertexCount();
74+
auto vertex_buffer = host_buffer.Emplace(
75+
count * sizeof(VT), alignof(VT),
76+
[&circle_tessellator, &uv_transform, &center, radius](uint8_t* buffer) {
77+
auto vertices = reinterpret_cast<VT*>(buffer);
78+
circle_tessellator.GenerateCircleTriangleStrip(
79+
[&vertices, &uv_transform](const Point& p) { //
80+
*vertices++ = {
81+
.position = p,
82+
.texture_coords = uv_transform * p,
83+
};
84+
},
85+
center, radius);
86+
});
87+
88+
return GeometryResult{
89+
.type = PrimitiveType::kTriangleStrip,
90+
.vertex_buffer =
91+
{
92+
.vertex_buffer = vertex_buffer,
93+
.vertex_count = count,
94+
.index_type = IndexType::kNone,
95+
},
96+
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
97+
entity.GetTransform(),
98+
.prevent_overdraw = false,
99+
};
100+
}
101+
102+
GeometryVertexType EllipseGeometry::GetVertexType() const {
103+
return GeometryVertexType::kPosition;
104+
}
105+
106+
std::optional<Rect> EllipseGeometry::GetCoverage(
107+
const Matrix& transform) const {
108+
Point corners[4]{
109+
{center_.x, center_.y - radius_},
110+
{center_.x + radius_, center_.y},
111+
{center_.x, center_.y + radius_},
112+
{center_.x - radius_, center_.y},
113+
};
114+
115+
for (int i = 0; i < 4; i++) {
116+
corners[i] = transform * corners[i];
117+
}
118+
return Rect::MakePointBounds(std::begin(corners), std::end(corners));
119+
}
120+
121+
bool EllipseGeometry::CoversArea(const Matrix& transform,
122+
const Rect& rect) const {
123+
return false;
124+
}
125+
126+
bool EllipseGeometry::IsAxisAlignedRect() const {
127+
return false;
128+
}
129+
130+
} // namespace impeller
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include "impeller/entity/geometry/geometry.h"
8+
9+
namespace impeller {
10+
11+
class EllipseGeometry final : public Geometry {
12+
public:
13+
explicit EllipseGeometry(Point center, Scalar radius);
14+
15+
~EllipseGeometry() = default;
16+
17+
// |Geometry|
18+
bool CoversArea(const Matrix& transform, const Rect& rect) const override;
19+
20+
// |Geometry|
21+
bool IsAxisAlignedRect() const override;
22+
23+
private:
24+
// Computes the 4 corners of a rectangle that defines the line and
25+
// possibly extended endpoints which will be rendered under the given
26+
// transform, and returns true if such a rectangle is defined.
27+
//
28+
// The coordinates will be generated in the original coordinate system
29+
// of the line end points and the transform will only be used to determine
30+
// the minimum line width.
31+
//
32+
// For kButt and kSquare end caps the ends should always be exteded as
33+
// per that decoration, but for kRound caps the ends might be extended
34+
// if the goal is to get a conservative bounds and might not be extended
35+
// if the calling code is planning to draw the round caps on the ends.
36+
//
37+
// @return true if the transform and width were not degenerate
38+
bool ComputeCorners(Point corners[4],
39+
const Matrix& transform,
40+
bool extend_endpoints) const;
41+
42+
// |Geometry|
43+
GeometryResult GetPositionBuffer(const ContentContext& renderer,
44+
const Entity& entity,
45+
RenderPass& pass) const override;
46+
47+
// |Geometry|
48+
GeometryVertexType GetVertexType() const override;
49+
50+
// |Geometry|
51+
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
52+
53+
// |Geometry|
54+
GeometryResult GetPositionUVBuffer(Rect texture_coverage,
55+
Matrix effect_transform,
56+
const ContentContext& renderer,
57+
const Entity& entity,
58+
RenderPass& pass) const override;
59+
60+
Point center_;
61+
Scalar radius_;
62+
63+
EllipseGeometry(const EllipseGeometry&) = delete;
64+
65+
EllipseGeometry& operator=(const EllipseGeometry&) = delete;
66+
};
67+
68+
} // namespace impeller

impeller/entity/geometry/geometry.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <optional>
99

1010
#include "impeller/entity/geometry/cover_geometry.h"
11+
#include "impeller/entity/geometry/ellipse_geometry.h"
1112
#include "impeller/entity/geometry/fill_path_geometry.h"
1213
#include "impeller/entity/geometry/line_geometry.h"
1314
#include "impeller/entity/geometry/point_field_geometry.h"
@@ -118,6 +119,10 @@ std::shared_ptr<Geometry> Geometry::MakeLine(Point p0,
118119
return std::make_shared<LineGeometry>(p0, p1, width, cap);
119120
}
120121

122+
std::shared_ptr<Geometry> Geometry::MakeCircle(Point center, Scalar radius) {
123+
return std::make_shared<EllipseGeometry>(center, radius);
124+
}
125+
121126
bool Geometry::CoversArea(const Matrix& transform, const Rect& rect) const {
122127
return false;
123128
}

impeller/entity/geometry/geometry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class Geometry {
6868
Scalar width,
6969
Cap cap);
7070

71+
static std::shared_ptr<Geometry> MakeCircle(Point center, Scalar radius);
72+
7173
static std::shared_ptr<Geometry> MakePointField(std::vector<Point> points,
7274
Scalar radius,
7375
bool round);

0 commit comments

Comments
 (0)