Skip to content

Commit

Permalink
[Impeller] Add direct tesselation of circles for DrawCircle and Round…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
flar authored Nov 30, 2023
1 parent ab77461 commit 2420fb0
Show file tree
Hide file tree
Showing 22 changed files with 1,018 additions and 91 deletions.
2 changes: 2 additions & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@
../../../flutter/impeller/geometry/path_unittests.cc
../../../flutter/impeller/geometry/rect_unittests.cc
../../../flutter/impeller/geometry/size_unittests.cc
../../../flutter/impeller/geometry/trig_unittests.cc
../../../flutter/impeller/golden_tests/README.md
../../../flutter/impeller/golden_tests_harvester/.dart_tool
../../../flutter/impeller/golden_tests_harvester/.gitignore
Expand Down Expand Up @@ -193,6 +194,7 @@
../../../flutter/impeller/scene/importer/importer_unittests.cc
../../../flutter/impeller/scene/scene_unittests.cc
../../../flutter/impeller/shader_archive/shader_archive_unittests.cc
../../../flutter/impeller/tessellator/circle_tessellator_unittests.cc
../../../flutter/impeller/tessellator/dart/.dart_tool
../../../flutter/impeller/tessellator/dart/pubspec.lock
../../../flutter/impeller/tessellator/dart/pubspec.yaml
Expand Down
12 changes: 12 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -5101,6 +5101,8 @@ ORIGIN: ../../../flutter/impeller/entity/entity_playground.cc + ../../../flutter
ORIGIN: ../../../flutter/impeller/entity/entity_playground.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/cover_geometry.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/cover_geometry.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/ellipse_geometry.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/ellipse_geometry.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/fill_path_geometry.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/fill_path_geometry.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry/geometry.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -5206,6 +5208,8 @@ ORIGIN: ../../../flutter/impeller/geometry/sigma.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/sigma.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/size.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/size.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/trig.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/trig.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/type_traits.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/type_traits.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/vector.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -5523,6 +5527,8 @@ ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive_writer.cc + ../.
ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive_writer.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/circle_tessellator.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/circle_tessellator.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/tessellator.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/tessellator/tessellator.h + ../../../flutter/LICENSE
Expand Down Expand Up @@ -7893,6 +7899,8 @@ FILE: ../../../flutter/impeller/entity/entity_playground.cc
FILE: ../../../flutter/impeller/entity/entity_playground.h
FILE: ../../../flutter/impeller/entity/geometry/cover_geometry.cc
FILE: ../../../flutter/impeller/entity/geometry/cover_geometry.h
FILE: ../../../flutter/impeller/entity/geometry/ellipse_geometry.cc
FILE: ../../../flutter/impeller/entity/geometry/ellipse_geometry.h
FILE: ../../../flutter/impeller/entity/geometry/fill_path_geometry.cc
FILE: ../../../flutter/impeller/entity/geometry/fill_path_geometry.h
FILE: ../../../flutter/impeller/entity/geometry/geometry.cc
Expand Down Expand Up @@ -7998,6 +8006,8 @@ FILE: ../../../flutter/impeller/geometry/sigma.cc
FILE: ../../../flutter/impeller/geometry/sigma.h
FILE: ../../../flutter/impeller/geometry/size.cc
FILE: ../../../flutter/impeller/geometry/size.h
FILE: ../../../flutter/impeller/geometry/trig.cc
FILE: ../../../flutter/impeller/geometry/trig.h
FILE: ../../../flutter/impeller/geometry/type_traits.cc
FILE: ../../../flutter/impeller/geometry/type_traits.h
FILE: ../../../flutter/impeller/geometry/vector.cc
Expand Down Expand Up @@ -8316,6 +8326,8 @@ FILE: ../../../flutter/impeller/shader_archive/shader_archive_writer.cc
FILE: ../../../flutter/impeller/shader_archive/shader_archive_writer.h
FILE: ../../../flutter/impeller/tessellator/c/tessellator.cc
FILE: ../../../flutter/impeller/tessellator/c/tessellator.h
FILE: ../../../flutter/impeller/tessellator/circle_tessellator.cc
FILE: ../../../flutter/impeller/tessellator/circle_tessellator.h
FILE: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart
FILE: ../../../flutter/impeller/tessellator/tessellator.cc
FILE: ../../../flutter/impeller/tessellator/tessellator.h
Expand Down
26 changes: 26 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2060,6 +2060,32 @@ TEST_P(AiksTest, DrawLinesRenderCorrectly) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, FillCirclesRenderCorrectly) {
Canvas canvas;
canvas.Scale(GetContentScale());
Paint paint;
const int color_count = 3;
Color colors[color_count] = {
Color::Blue(),
Color::Green(),
Color::Crimson(),
};

int c_index = 0;
int radius = 600;
while (radius > 0) {
paint.color = colors[(c_index++) % color_count];
canvas.DrawCircle({10, 10}, radius, paint);
if (radius > 30) {
radius -= 10;
} else {
radius -= 2;
}
}

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
Expand Down
40 changes: 21 additions & 19 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,17 +232,6 @@ bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
}

void Canvas::DrawLine(const Point& p0, const Point& p1, const Paint& paint) {
if (paint.stroke_cap == Cap::kRound) {
auto path = PathBuilder{}
.AddLine((p0), (p1))
.SetConvexity(Convexity::kConvex)
.TakePath();
Paint stroke_paint = paint;
stroke_paint.style = Paint::Style::kStroke;
DrawPath(path, stroke_paint);
return;
}

Entity entity;
entity.SetTransform(GetCurrentTransform());
entity.SetClipDepth(GetClipDepth());
Expand Down Expand Up @@ -298,20 +287,33 @@ void Canvas::DrawRRect(Rect rect, Point corner_radii, const Paint& paint) {
}

void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
if (paint.style == Paint::Style::kStroke) {
auto circle_path =
PathBuilder{}
.AddCircle(center, radius)
.SetConvexity(Convexity::kConvex)
.SetBounds(Rect::MakeLTRB(center.x - radius, center.y - radius,
center.x + radius, center.y + radius))
.TakePath();
DrawPath(circle_path, paint);
return;
}

Size half_size(radius, radius);
if (AttemptDrawBlurredRRect(
Rect::MakeOriginSize(center - half_size, half_size * 2), radius,
paint)) {
return;
}
auto circle_path =
PathBuilder{}
.AddCircle(center, radius)
.SetConvexity(Convexity::kConvex)
.SetBounds(Rect::MakeLTRB(center.x - radius, center.y - radius,
center.x + radius, center.y + radius))
.TakePath();
DrawPath(circle_path, paint);

Entity entity;
entity.SetTransform(GetCurrentTransform());
entity.SetClipDepth(GetClipDepth());
entity.SetBlendMode(paint.blend_mode);
entity.SetContents(paint.WithFilters(
paint.CreateContentsForGeometry(Geometry::MakeCircle(center, radius))));

GetCurrentPass().AddEntity(entity);
}

void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ impeller_component("entity") {
"entity_pass_target.h",
"geometry/cover_geometry.cc",
"geometry/cover_geometry.h",
"geometry/ellipse_geometry.cc",
"geometry/ellipse_geometry.h",
"geometry/fill_path_geometry.cc",
"geometry/fill_path_geometry.h",
"geometry/geometry.cc",
Expand Down
130 changes: 130 additions & 0 deletions impeller/entity/geometry/ellipse_geometry.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/entity/geometry/ellipse_geometry.h"

#include "flutter/impeller/tessellator/circle_tessellator.h"

namespace impeller {

EllipseGeometry::EllipseGeometry(Point center, Scalar radius)
: center_(center), radius_(radius) {
FML_DCHECK(radius >= 0);
}

GeometryResult EllipseGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto& host_buffer = pass.GetTransientsBuffer();
using VT = SolidFillVertexShader::PerVertexData;

Scalar radius = radius_;
const Point& center = center_;
std::shared_ptr<Tessellator> tessellator = renderer.GetTessellator();
CircleTessellator circle_tessellator(tessellator, entity.GetTransform(),
radius_);
size_t count = circle_tessellator.GetCircleVertexCount();
auto vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &center, radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateCircleTriangleStrip(
[&vertices](const Point& p) { //
*vertices++ = {
.position = p,
};
},
center, radius);
});

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = vertex_buffer,
.vertex_count = count,
.index_type = IndexType::kNone,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransform(),
.prevent_overdraw = false,
};
}

// |Geometry|
GeometryResult EllipseGeometry::GetPositionUVBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto& host_buffer = pass.GetTransientsBuffer();
using VT = TextureFillVertexShader::PerVertexData;
auto uv_transform =
texture_coverage.GetNormalizingTransform() * effect_transform;

Scalar radius = radius_;
const Point& center = center_;
std::shared_ptr<Tessellator> tessellator = renderer.GetTessellator();
CircleTessellator circle_tessellator(tessellator, entity.GetTransform(),
radius_);
size_t count = circle_tessellator.GetCircleVertexCount();
auto vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &uv_transform, &center, radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateCircleTriangleStrip(
[&vertices, &uv_transform](const Point& p) { //
*vertices++ = {
.position = p,
.texture_coords = uv_transform * p,
};
},
center, radius);
});

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = vertex_buffer,
.vertex_count = count,
.index_type = IndexType::kNone,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransform(),
.prevent_overdraw = false,
};
}

GeometryVertexType EllipseGeometry::GetVertexType() const {
return GeometryVertexType::kPosition;
}

std::optional<Rect> EllipseGeometry::GetCoverage(
const Matrix& transform) const {
Point corners[4]{
{center_.x, center_.y - radius_},
{center_.x + radius_, center_.y},
{center_.x, center_.y + radius_},
{center_.x - radius_, center_.y},
};

for (int i = 0; i < 4; i++) {
corners[i] = transform * corners[i];
}
return Rect::MakePointBounds(std::begin(corners), std::end(corners));
}

bool EllipseGeometry::CoversArea(const Matrix& transform,
const Rect& rect) const {
return false;
}

bool EllipseGeometry::IsAxisAlignedRect() const {
return false;
}

} // namespace impeller
68 changes: 68 additions & 0 deletions impeller/entity/geometry/ellipse_geometry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#pragma once

#include "impeller/entity/geometry/geometry.h"

namespace impeller {

class EllipseGeometry final : public Geometry {
public:
explicit EllipseGeometry(Point center, Scalar radius);

~EllipseGeometry() = default;

// |Geometry|
bool CoversArea(const Matrix& transform, const Rect& rect) const override;

// |Geometry|
bool IsAxisAlignedRect() const override;

private:
// Computes the 4 corners of a rectangle that defines the line and
// possibly extended endpoints which will be rendered under the given
// transform, and returns true if such a rectangle is defined.
//
// The coordinates will be generated in the original coordinate system
// of the line end points and the transform will only be used to determine
// the minimum line width.
//
// For kButt and kSquare end caps the ends should always be exteded as
// per that decoration, but for kRound caps the ends might be extended
// if the goal is to get a conservative bounds and might not be extended
// if the calling code is planning to draw the round caps on the ends.
//
// @return true if the transform and width were not degenerate
bool ComputeCorners(Point corners[4],
const Matrix& transform,
bool extend_endpoints) const;

// |Geometry|
GeometryResult GetPositionBuffer(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const override;

// |Geometry|
GeometryVertexType GetVertexType() const override;

// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;

// |Geometry|
GeometryResult GetPositionUVBuffer(Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const override;

Point center_;
Scalar radius_;

EllipseGeometry(const EllipseGeometry&) = delete;

EllipseGeometry& operator=(const EllipseGeometry&) = delete;
};

} // namespace impeller
5 changes: 5 additions & 0 deletions impeller/entity/geometry/geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <optional>

#include "impeller/entity/geometry/cover_geometry.h"
#include "impeller/entity/geometry/ellipse_geometry.h"
#include "impeller/entity/geometry/fill_path_geometry.h"
#include "impeller/entity/geometry/line_geometry.h"
#include "impeller/entity/geometry/point_field_geometry.h"
Expand Down Expand Up @@ -118,6 +119,10 @@ std::shared_ptr<Geometry> Geometry::MakeLine(Point p0,
return std::make_shared<LineGeometry>(p0, p1, width, cap);
}

std::shared_ptr<Geometry> Geometry::MakeCircle(Point center, Scalar radius) {
return std::make_shared<EllipseGeometry>(center, radius);
}

bool Geometry::CoversArea(const Matrix& transform, const Rect& rect) const {
return false;
}
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/geometry/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class Geometry {
Scalar width,
Cap cap);

static std::shared_ptr<Geometry> MakeCircle(Point center, Scalar radius);

static std::shared_ptr<Geometry> MakePointField(std::vector<Point> points,
Scalar radius,
bool round);
Expand Down
Loading

0 comments on commit 2420fb0

Please sign in to comment.