diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 605d139cef36f..c68cdbaff43b1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -3118,14 +3118,14 @@ ORIGIN: ../../../flutter/impeller/entity/entity_pass_target.cc + ../../../flutte ORIGIN: ../../../flutter/impeller/entity/entity_pass_target.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_playground.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/entity_playground.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/geometry/circle_geometry.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/geometry/circle_geometry.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/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 ORIGIN: ../../../flutter/impeller/entity/geometry/geometry.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/entity/geometry/point_field_geometry.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/entity/geometry/point_field_geometry.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/rect_geometry.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/rect_geometry.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.cc + ../../../flutter/LICENSE @@ -5875,14 +5875,14 @@ FILE: ../../../flutter/impeller/entity/entity_pass_target.cc FILE: ../../../flutter/impeller/entity/entity_pass_target.h FILE: ../../../flutter/impeller/entity/entity_playground.cc FILE: ../../../flutter/impeller/entity/entity_playground.h +FILE: ../../../flutter/impeller/entity/geometry/circle_geometry.cc +FILE: ../../../flutter/impeller/entity/geometry/circle_geometry.h FILE: ../../../flutter/impeller/entity/geometry/cover_geometry.cc FILE: ../../../flutter/impeller/entity/geometry/cover_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 FILE: ../../../flutter/impeller/entity/geometry/geometry.h -FILE: ../../../flutter/impeller/entity/geometry/point_field_geometry.cc -FILE: ../../../flutter/impeller/entity/geometry/point_field_geometry.h FILE: ../../../flutter/impeller/entity/geometry/rect_geometry.cc FILE: ../../../flutter/impeller/entity/geometry/rect_geometry.h FILE: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.cc diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index a0823f912de98..f79d60f8238d2 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -277,6 +277,18 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) { paint)) { return; } + if (paint.style == Paint::Style::kFill) { + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetClipDepth(GetClipDepth()); + entity.SetBlendMode(paint.blend_mode); + entity.SetContents(paint.WithFilters(paint.CreateContentsForGeometry( + Geometry::MakeCircle({center}, radius, true)))); + + GetCurrentPass().AddEntity(entity); + return; + } + auto circle_path = PathBuilder{} .AddCircle(center, radius) @@ -434,8 +446,8 @@ void Canvas::DrawPoints(std::vector points, entity.SetClipDepth(GetClipDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters(paint.CreateContentsForGeometry( - Geometry::MakePointField(std::move(points), radius, - /*round=*/point_style == PointStyle::kRound)))); + Geometry::MakeCircle(std::move(points), radius, + /*round=*/point_style == PointStyle::kRound)))); GetCurrentPass().AddEntity(entity); } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 2460a19178513..33b778e905559 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -188,14 +188,14 @@ impeller_component("entity") { "entity_pass_delegate.h", "entity_pass_target.cc", "entity_pass_target.h", + "geometry/circle_geometry.cc", + "geometry/circle_geometry.h", "geometry/cover_geometry.cc", "geometry/cover_geometry.h", "geometry/fill_path_geometry.cc", "geometry/fill_path_geometry.h", "geometry/geometry.cc", "geometry/geometry.h", - "geometry/point_field_geometry.cc", - "geometry/point_field_geometry.h", "geometry/rect_geometry.cc", "geometry/rect_geometry.h", "geometry/stroke_path_geometry.cc", diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index ea564dfa8e029..6e2429c5f20dd 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -30,8 +30,8 @@ #include "impeller/entity/entity_pass.h" #include "impeller/entity/entity_pass_delegate.h" #include "impeller/entity/entity_playground.h" +#include "impeller/entity/geometry/circle_geometry.h" #include "impeller/entity/geometry/geometry.h" -#include "impeller/entity/geometry/point_field_geometry.h" #include "impeller/entity/geometry/stroke_path_geometry.h" #include "impeller/geometry/color.h" #include "impeller/geometry/geometry_asserts.h" @@ -2390,27 +2390,27 @@ TEST_P(EntityTest, TessellateConvex) { } } -TEST_P(EntityTest, PointFieldGeometryDivisions) { +TEST_P(EntityTest, CircleGeometryDivisions) { // Square always gives 4 divisions. - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(24.0, false), 4u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(2.0, false), 4u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(200.0, false), 4u); - - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(0.5, true), 4u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(1.5, true), 8u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(5.5, true), 24u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(12.5, true), 34u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(22.3, true), 22u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(40.5, true), 40u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(100.0, true), 100u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(24.0, false), 4u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(2.0, false), 4u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(200.0, false), 4u); + + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(0.5, true), 4u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(1.5, true), 8u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(5.5, true), 24u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(12.5, true), 34u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(22.3, true), 22u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(40.5, true), 40u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(100.0, true), 100u); // Caps at 140. - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(1000.0, true), 140u); - ASSERT_EQ(PointFieldGeometry::ComputeCircleDivisions(20000.0, true), 140u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(1000.0, true), 140u); + ASSERT_EQ(CircleGeometry::ComputeCircleDivisions(20000.0, true), 140u); } -TEST_P(EntityTest, PointFieldGeometryCoverage) { +TEST_P(EntityTest, CircleGeometryCoverage) { std::vector points = {{10, 20}, {100, 200}}; - auto geometry = Geometry::MakePointField(points, 5.0, false); + auto geometry = Geometry::MakeCircle(points, 5.0, false); ASSERT_EQ(*geometry->GetCoverage(Matrix()), Rect::MakeLTRB(5, 15, 105, 205)); ASSERT_EQ(*geometry->GetCoverage(Matrix::MakeTranslation({30, 0, 0})), Rect::MakeLTRB(35, 15, 135, 205)); diff --git a/impeller/entity/geometry/point_field_geometry.cc b/impeller/entity/geometry/circle_geometry.cc similarity index 71% rename from impeller/entity/geometry/point_field_geometry.cc rename to impeller/entity/geometry/circle_geometry.cc index 9821bad91ad7b..ac6f603da6ae1 100644 --- a/impeller/entity/geometry/point_field_geometry.cc +++ b/impeller/entity/geometry/circle_geometry.cc @@ -2,54 +2,54 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/entity/geometry/point_field_geometry.h" +#include "impeller/entity/geometry/circle_geometry.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/compute_command.h" namespace impeller { -PointFieldGeometry::PointFieldGeometry(std::vector points, - Scalar radius, - bool round) +// The minimum number of points before compute is used to fill out the geometry. +// This number was arbitrarily chosen. +static constexpr size_t kMinComputeSize = 32; + +CircleGeometry::CircleGeometry(std::vector points, + Scalar radius, + bool round) : points_(std::move(points)), radius_(radius), round_(round) {} -PointFieldGeometry::~PointFieldGeometry() = default; +CircleGeometry::~CircleGeometry() = default; -GeometryResult PointFieldGeometry::GetPositionBuffer( - const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) { - if (renderer.GetDeviceCapabilities().SupportsCompute()) { +GeometryResult CircleGeometry::GetPositionBuffer(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) { + if (points_.size() > kMinComputeSize && + renderer.GetDeviceCapabilities().SupportsCompute()) { return GetPositionBufferGPU(renderer, entity, pass); } - auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass); - if (!vtx_builder.has_value()) { - return {}; - } - auto& host_buffer = pass.GetTransientsBuffer(); return { .type = PrimitiveType::kTriangle, - .vertex_buffer = vtx_builder->CreateVertexBuffer(host_buffer), + .vertex_buffer = GetPositionBufferCPU(renderer, entity, pass), .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(), .prevent_overdraw = false, }; } -GeometryResult PointFieldGeometry::GetPositionUVBuffer( +GeometryResult CircleGeometry::GetPositionUVBuffer( Rect texture_coverage, Matrix effect_transform, const ContentContext& renderer, const Entity& entity, RenderPass& pass) { - if (renderer.GetDeviceCapabilities().SupportsCompute()) { + if (points_.size() > kMinComputeSize && + renderer.GetDeviceCapabilities().SupportsCompute()) { return GetPositionBufferGPU(renderer, entity, pass, texture_coverage, effect_transform); } - auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass); + auto vtx_builder = GetPositionUVBufferCPU(renderer, entity, pass); if (!vtx_builder.has_value()) { return {}; } @@ -66,10 +66,74 @@ GeometryResult PointFieldGeometry::GetPositionUVBuffer( }; } +VertexBuffer CircleGeometry::GetPositionBufferCPU( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) { + if (radius_ < 0.0) { + return {}; + } + auto determinant = entity.GetTransformation().GetDeterminant(); + if (determinant == 0) { + return {}; + } + + Scalar min_size = 1.0f / sqrt(std::abs(determinant)); + Scalar radius = std::max(radius_, min_size); + + auto vertices_per_geom = ComputeCircleDivisions( + entity.GetTransformation().GetMaxBasisLength() * radius, round_); + auto points_per_circle = 3 + (vertices_per_geom - 3) * 3; + auto total = points_per_circle * points_.size(); + auto radian_start = round_ ? 0.0f : 0.785398f; + auto radian_step = k2Pi / vertices_per_geom; + + auto& host_buffer = pass.GetTransientsBuffer(); + + /// Precompute all relative points and angles for a fixed geometry size. + auto elapsed_angle = radian_start; + std::vector angle_table(vertices_per_geom); + for (auto i = 0u; i < vertices_per_geom; i++) { + angle_table[i] = Point(cos(elapsed_angle), sin(elapsed_angle)) * radius; + elapsed_angle += radian_step; + } + + auto vertex_buffer = host_buffer.Emplace( + total * sizeof(Point), alignof(Point), [&](uint8_t* buffer) { + Point* points = reinterpret_cast(buffer); + auto offset = 0u; + for (auto i = 0u; i < points_.size(); i++) { + auto center = points_[i]; + + auto origin = center + angle_table[0]; + points[offset++] = origin; + + auto pt1 = center + angle_table[1]; + points[offset++] = pt1; + + auto pt2 = center + angle_table[2]; + points[offset++] = pt2; + + for (auto j = 0u; j < vertices_per_geom - 3; j++) { + points[offset++] = origin; + points[offset++] = pt2; + pt2 = center + angle_table[j + 3]; + points[offset++] = pt2; + } + } + }); + + return VertexBuffer{ + .vertex_buffer = vertex_buffer, + .vertex_count = total, + .index_type = IndexType::kNone, + }; +} + std::optional> -PointFieldGeometry::GetPositionBufferCPU(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) { +CircleGeometry::GetPositionUVBufferCPU(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) { if (radius_ < 0.0) { return std::nullopt; } @@ -122,7 +186,7 @@ PointFieldGeometry::GetPositionBufferCPU(const ContentContext& renderer, return vtx_builder; } -GeometryResult PointFieldGeometry::GetPositionBufferGPU( +GeometryResult CircleGeometry::GetPositionBufferGPU( const ContentContext& renderer, const Entity& entity, RenderPass& pass, @@ -241,8 +305,8 @@ GeometryResult PointFieldGeometry::GetPositionBufferGPU( /// @brief Compute the number of vertices to divide each circle into. /// /// @return the number of vertices. -size_t PointFieldGeometry::ComputeCircleDivisions(Scalar scaled_radius, - bool round) { +size_t CircleGeometry::ComputeCircleDivisions(Scalar scaled_radius, + bool round) { if (!round) { return 4; } @@ -265,13 +329,12 @@ size_t PointFieldGeometry::ComputeCircleDivisions(Scalar scaled_radius, } // |Geometry| -GeometryVertexType PointFieldGeometry::GetVertexType() const { +GeometryVertexType CircleGeometry::GetVertexType() const { return GeometryVertexType::kPosition; } // |Geometry| -std::optional PointFieldGeometry::GetCoverage( - const Matrix& transform) const { +std::optional CircleGeometry::GetCoverage(const Matrix& transform) const { if (points_.size() > 0) { // Doesn't use MakePointBounds as this isn't resilient to points that // all lie along the same axis. diff --git a/impeller/entity/geometry/point_field_geometry.h b/impeller/entity/geometry/circle_geometry.h similarity index 71% rename from impeller/entity/geometry/point_field_geometry.h rename to impeller/entity/geometry/circle_geometry.h index 30ccdaa01a369..3264b5e0f50e4 100644 --- a/impeller/entity/geometry/point_field_geometry.h +++ b/impeller/entity/geometry/circle_geometry.h @@ -8,11 +8,11 @@ namespace impeller { -class PointFieldGeometry : public Geometry { +class CircleGeometry : public Geometry { public: - PointFieldGeometry(std::vector points, Scalar radius, bool round); + CircleGeometry(std::vector points, Scalar radius, bool round); - ~PointFieldGeometry(); + ~CircleGeometry(); static size_t ComputeCircleDivisions(Scalar scaled_radius, bool round); @@ -42,18 +42,22 @@ class PointFieldGeometry : public Geometry { std::optional texture_coverage = std::nullopt, std::optional effect_transform = std::nullopt); + VertexBuffer GetPositionBufferCPU(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass); + std::optional> - GetPositionBufferCPU(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass); + GetPositionUVBufferCPU(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass); std::vector points_; Scalar radius_; bool round_; - PointFieldGeometry(const PointFieldGeometry&) = delete; + CircleGeometry(const CircleGeometry&) = delete; - PointFieldGeometry& operator=(const PointFieldGeometry&) = delete; + CircleGeometry& operator=(const CircleGeometry&) = delete; }; } // namespace impeller diff --git a/impeller/entity/geometry/geometry.cc b/impeller/entity/geometry/geometry.cc index 29d880a86c86b..0c3047a5b5f79 100644 --- a/impeller/entity/geometry/geometry.cc +++ b/impeller/entity/geometry/geometry.cc @@ -6,9 +6,9 @@ #include +#include "impeller/entity/geometry/circle_geometry.h" #include "impeller/entity/geometry/cover_geometry.h" #include "impeller/entity/geometry/fill_path_geometry.h" -#include "impeller/entity/geometry/point_field_geometry.h" #include "impeller/entity/geometry/rect_geometry.h" #include "impeller/entity/geometry/stroke_path_geometry.h" #include "impeller/geometry/rect.h" @@ -117,10 +117,10 @@ std::unique_ptr Geometry::MakeFillPath( return std::make_unique(path, inner_rect); } -std::unique_ptr Geometry::MakePointField(std::vector points, - Scalar radius, - bool round) { - return std::make_unique(std::move(points), radius, round); +std::unique_ptr Geometry::MakeCircle(std::vector points, + Scalar radius, + bool round) { + return std::make_unique(std::move(points), radius, round); } std::unique_ptr Geometry::MakeStrokePath(const Path& path, diff --git a/impeller/entity/geometry/geometry.h b/impeller/entity/geometry/geometry.h index c0743626953ae..e0609df755f13 100644 --- a/impeller/entity/geometry/geometry.h +++ b/impeller/entity/geometry/geometry.h @@ -72,9 +72,9 @@ class Geometry { static std::unique_ptr MakeRect(Rect rect); - static std::unique_ptr MakePointField(std::vector points, - Scalar radius, - bool round); + static std::unique_ptr MakeCircle(std::vector points, + Scalar radius, + bool round); virtual GeometryResult GetPositionBuffer(const ContentContext& renderer, const Entity& entity,