[Impeller] track path convexity and use it to simplify fill tessellation. (flutter/engine#41834)

We can do this on the GPU, but we'll need a fallback when we don't have polyline compute.

From experimentation, Skia has already computed this value when Impeller gets that path. Additionally, this will allow us to speed up tessellation of known convex shapes like ovals that we don't want to add special cases for in the API.
This commit is contained in:
Jonah Williams 2023-05-09 11:45:00 -07:00 committed by GitHub
parent af593c9777
commit d64bc16426
10 changed files with 138 additions and 7 deletions

View File

@ -251,10 +251,8 @@ void Canvas::DrawRRect(Rect rect, Scalar corner_radius, const Paint& paint) {
GetCurrentPass().AddEntity(entity);
return;
} else {
DrawPath(PathBuilder{}.AddRoundedRect(rect, corner_radius).TakePath(),
paint);
}
DrawPath(PathBuilder{}.AddRoundedRect(rect, corner_radius).TakePath(), paint);
}
void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
@ -263,7 +261,11 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
paint)) {
return;
}
DrawPath(PathBuilder{}.AddCircle(center, radius).TakePath(), paint);
auto circle_path = PathBuilder{}
.AddCircle(center, radius)
.SetConvexity(Convexity::kConvex)
.TakePath();
DrawPath(circle_path, paint);
}
void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {

View File

@ -845,6 +845,7 @@ void DlDispatcher::drawLine(const SkPoint& p0, const SkPoint& p1) {
auto path =
PathBuilder{}
.AddLine(skia_conversions::ToPoint(p0), skia_conversions::ToPoint(p1))
.SetConvexity(Convexity::kConvex)
.TakePath();
Paint paint = paint_;
paint.style = Paint::Style::kStroke;
@ -862,8 +863,10 @@ void DlDispatcher::drawOval(const SkRect& bounds) {
canvas_.DrawCircle(skia_conversions::ToPoint(bounds.center()),
bounds.width() * 0.5, paint_);
} else {
auto path =
PathBuilder{}.AddOval(skia_conversions::ToRect(bounds)).TakePath();
auto path = PathBuilder{}
.AddOval(skia_conversions::ToRect(bounds))
.SetConvexity(Convexity::kConvex)
.TakePath();
canvas_.DrawPath(path, paint_);
}
}

View File

@ -109,12 +109,15 @@ Path ToPath(const SkPath& path) {
fill_type = FillType::kNonZero;
break;
}
builder.SetConvexity(path.isConvex() ? Convexity::kConvex
: Convexity::kUnknown);
return builder.TakePath(fill_type);
}
Path ToPath(const SkRRect& rrect) {
return PathBuilder{}
.AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect))
.SetConvexity(Convexity::kConvex)
.TakePath();
}

View File

@ -2572,5 +2572,41 @@ TEST_P(EntityTest, TiledTextureContentsIsOpaque) {
ASSERT_FALSE(contents.IsOpaque());
}
TEST_P(EntityTest, TessellateConvex) {
{
// Sanity check simple rectangle.
auto [pts, indices] =
TessellateConvex(PathBuilder{}
.AddRect(Rect::MakeLTRB(0, 0, 10, 10))
.TakePath()
.CreatePolyline(1.0));
std::vector<Point> expected = {
{0, 0}, {10, 0}, {10, 10}, {0, 10}, //
};
std::vector<uint16_t> expected_indices = {0, 1, 2, 0, 2, 3};
ASSERT_EQ(pts, expected);
ASSERT_EQ(indices, expected_indices);
}
{
auto [pts, indices] =
TessellateConvex(PathBuilder{}
.AddRect(Rect::MakeLTRB(0, 0, 10, 10))
.AddRect(Rect::MakeLTRB(20, 20, 30, 30))
.TakePath()
.CreatePolyline(1.0));
std::vector<Point> expected = {
{0, 0}, {10, 0}, {10, 10}, {0, 10}, //
{20, 20}, {30, 20}, {30, 30}, {20, 30} //
};
std::vector<uint16_t> expected_indices = {0, 1, 2, 0, 2, 3,
0, 6, 7, 0, 7, 8};
ASSERT_EQ(pts, expected);
ASSERT_EQ(indices, expected_indices);
}
}
} // namespace testing
} // namespace impeller

View File

@ -16,6 +16,37 @@
namespace impeller {
/// Given a convex polyline, create a triangle fan structure.
std::pair<std::vector<Point>, std::vector<uint16_t>> TessellateConvex(
Path::Polyline polyline) {
std::vector<Point> output;
std::vector<uint16_t> index;
for (auto j = 0u; j < polyline.contours.size(); j++) {
auto [start, end] = polyline.GetContourPointBounds(j);
auto center = polyline.points[start];
// 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]) {
end--;
}
output.emplace_back(center);
output.emplace_back(polyline.points[start + 1]);
for (auto i = start + 2; i < end; i++) {
const auto& point_b = polyline.points[i];
output.emplace_back(point_b);
index.emplace_back(0);
index.emplace_back(i - 1);
index.emplace_back(i);
}
}
return std::make_pair(output, index);
}
Geometry::Geometry() = default;
Geometry::~Geometry() = default;
@ -103,8 +134,30 @@ GeometryResult FillPathGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
VertexBuffer vertex_buffer;
auto& host_buffer = pass.GetTransientsBuffer();
VertexBuffer vertex_buffer;
if (path_.GetFillType() == FillType::kNonZero && //
path_.IsConvex()) {
auto [points, indicies] = TessellateConvex(
path_.CreatePolyline(entity.GetTransformation().GetMaxBasisLength()));
vertex_buffer.vertex_buffer = host_buffer.Emplace(
points.data(), points.size() * sizeof(Point), alignof(Point));
vertex_buffer.index_buffer = host_buffer.Emplace(
indicies.data(), indicies.size() * sizeof(uint16_t), alignof(uint16_t));
vertex_buffer.index_count = indicies.size();
vertex_buffer.index_type = IndexType::k16bit;
return GeometryResult{
.type = PrimitiveType::kTriangle,
.vertex_buffer = vertex_buffer,
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}
auto tesselation_result = renderer.GetTessellator()->Tessellate(
path_.GetFillType(),
path_.CreatePolyline(entity.GetTransformation().GetMaxBasisLength()),

View File

@ -33,6 +33,11 @@ enum GeometryVertexType {
kUV,
};
/// @brief Given a polyline created from a convex filled path, perform a
/// tessellation.
std::pair<std::vector<Point>, std::vector<uint16_t>> TessellateConvex(
Path::Polyline polyline);
class Geometry {
public:
Geometry();

View File

@ -53,6 +53,14 @@ FillType Path::GetFillType() const {
return fill_;
}
bool Path::IsConvex() const {
return convexity_ == Convexity::kConvex;
}
void Path::SetConvexity(Convexity value) {
convexity_ = value;
}
Path& Path::AddLinearComponent(Point p1, Point p2) {
linears_.emplace_back(p1, p2);
components_.emplace_back(ComponentType::kLinear, linears_.size() - 1);

View File

@ -34,6 +34,11 @@ enum class FillType {
kAbsGeqTwo,
};
enum class Convexity {
kUnknown,
kConvex,
};
//------------------------------------------------------------------------------
/// @brief Paths are lightweight objects that describe a collection of
/// linear, quadratic, or cubic segments. These segments may be
@ -94,6 +99,8 @@ class Path {
FillType GetFillType() const;
bool IsConvex() const;
Path& AddLinearComponent(Point p1, Point p2);
Path& AddQuadraticComponent(Point p1, Point cp, Point p2);
@ -148,6 +155,10 @@ class Path {
std::optional<std::pair<Point, Point>> GetMinMaxCoveragePoints() const;
private:
friend class PathBuilder;
void SetConvexity(Convexity value);
struct ComponentIndexPair {
ComponentType type = ComponentType::kLinear;
size_t index = 0;
@ -159,6 +170,7 @@ class Path {
};
FillType fill_ = FillType::kNonZero;
Convexity convexity_ = Convexity::kUnknown;
std::vector<ComponentIndexPair> components_;
std::vector<LinearPathComponent> linears_;
std::vector<QuadraticPathComponent> quads_;

View File

@ -21,6 +21,7 @@ Path PathBuilder::CopyPath(FillType fill) const {
Path PathBuilder::TakePath(FillType fill) {
auto path = prototype_;
path.SetFillType(fill);
path.SetConvexity(convexity_);
return path;
}
@ -103,6 +104,11 @@ PathBuilder& PathBuilder::SmoothQuadraticCurveTo(Point point, bool relative) {
return *this;
}
PathBuilder& PathBuilder::SetConvexity(Convexity value) {
convexity_ = value;
return *this;
}
PathBuilder& PathBuilder::CubicCurveTo(Point controlPoint1,
Point controlPoint2,
Point point,

View File

@ -31,6 +31,8 @@ class PathBuilder {
const Path& GetCurrentPath() const;
PathBuilder& SetConvexity(Convexity value);
PathBuilder& MoveTo(Point point, bool relative = false);
PathBuilder& Close();
@ -116,6 +118,7 @@ class PathBuilder {
Point subpath_start_;
Point current_;
Path prototype_;
Convexity convexity_;
Point ReflectedQuadraticControlPoint1() const;