From 031fc72b86ded7a8fd5f494d1794f42b8d529230 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sat, 2 Nov 2024 12:35:58 -0700 Subject: [PATCH 1/5] [Impeller] geometry changes to support line/point style. --- impeller/geometry/geometry_benchmarks.cc | 2 - impeller/geometry/path.cc | 4 + impeller/geometry/path.h | 4 + impeller/geometry/path_builder.cc | 11 ++ impeller/geometry/path_builder.h | 1 + impeller/geometry/path_component.cc | 8 +- impeller/geometry/path_component.h | 4 +- impeller/geometry/path_unittests.cc | 136 +++++++++++++++++++++++ impeller/tessellator/tessellator.cc | 18 +-- impeller/tessellator/tessellator.h | 18 +-- 10 files changed, 185 insertions(+), 21 deletions(-) diff --git a/impeller/geometry/geometry_benchmarks.cc b/impeller/geometry/geometry_benchmarks.cc index 1df17cffd69ba..cfc7b8356a24e 100644 --- a/impeller/geometry/geometry_benchmarks.cc +++ b/impeller/geometry/geometry_benchmarks.cc @@ -4,8 +4,6 @@ #include "flutter/benchmarking/benchmarking.h" -#include "flutter/impeller/entity/solid_fill.vert.h" - #include "impeller/entity/geometry/stroke_path_geometry.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 7762ada248305..f5ea8fb3b94b0 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -59,6 +59,10 @@ bool Path::IsEmpty() const { data_->components[0] == ComponentType::kContour); } +bool Path::IsSingleContour() const { + return data_->single_countour; +} + /// Determine required storage for points and indices. std::pair Path::CountStorage(Scalar scale) const { size_t points = 0; diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index edad3c9a44175..d81b3cb0ec6f8 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -153,6 +153,9 @@ class Path { bool IsEmpty() const; + /// @brief Whether the line contains a single contour. + bool IsSingleContour() const; + bool GetLinearComponentAtIndex(size_t index, LinearPathComponent& linear) const; @@ -219,6 +222,7 @@ class Path { FillType fill = FillType::kNonZero; Convexity convexity = Convexity::kUnknown; + bool single_countour = true; std::optional bounds; std::vector points; std::vector components; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 6d49fca61d4b0..fb787f145dac8 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -18,13 +18,22 @@ PathBuilder::~PathBuilder() = default; Path PathBuilder::CopyPath(FillType fill) { prototype_.fill = fill; + prototype_.single_countour = + current_contour_location_ == 0u || + (contour_count_ == 2 && + prototype_.components.back() == Path::ComponentType::kContour); return Path(prototype_); } Path PathBuilder::TakePath(FillType fill) { prototype_.fill = fill; UpdateBounds(); + prototype_.single_countour = + current_contour_location_ == 0u || + (contour_count_ == 2 && + prototype_.components.back() == Path::ComponentType::kContour); current_contour_location_ = 0u; + contour_count_ = 1; return Path(std::move(prototype_)); } @@ -276,6 +285,7 @@ void PathBuilder::AddContourComponent(const Point& destination, points.push_back(destination); points.push_back(closed); components.push_back(Path::ComponentType::kContour); + contour_count_ += 1; } prototype_.bounds.reset(); } @@ -450,6 +460,7 @@ PathBuilder& PathBuilder::AddPath(const Path& path) { for (auto component : path.data_->components) { if (component == Path::ComponentType::kContour) { current_contour_location_ = source_offset; + contour_count_ += 1; } source_offset += Path::VerbToOffset(component); } diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index abd899d2368c4..0184687cb611e 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -111,6 +111,7 @@ class PathBuilder { Point subpath_start_; Point current_; size_t current_contour_location_ = 0u; + size_t contour_count_ = 0u; Path::Data prototype_; PathBuilder& AddRoundedRectTopLeft(Rect rect, RoundingRadii radii); diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index c6ba536b2219a..a83833f61c1d1 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -80,11 +80,13 @@ void StripVertexWriter::Write(Point point) { /////////// GLESVertexWriter /////////// GLESVertexWriter::GLESVertexWriter(std::vector& points, - std::vector& indices) - : points_(points), indices_(indices) {} + std::vector& indices, + bool line_strip) + : points_(points), indices_(indices), line_strip_(line_strip) {} void GLESVertexWriter::EndContour() { - if (points_.size() == 0u || contour_start_ == points_.size() - 1) { + if (points_.size() == 0u || contour_start_ == points_.size() - 1 || + line_strip_) { // Empty or first contour. return; } diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 156b3de04d533..c86750723b69a 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -71,7 +71,8 @@ class StripVertexWriter : public VertexWriter { class GLESVertexWriter : public VertexWriter { public: explicit GLESVertexWriter(std::vector& points, - std::vector& indices); + std::vector& indices, + bool line_strip = false); ~GLESVertexWriter() = default; @@ -84,6 +85,7 @@ class GLESVertexWriter : public VertexWriter { size_t contour_start_ = 0u; std::vector& points_; std::vector& indices_; + const bool line_strip_; }; struct LinearPathComponent { diff --git a/impeller/geometry/path_unittests.cc b/impeller/geometry/path_unittests.cc index 51c29756f5bd6..ef802cbe7c0eb 100644 --- a/impeller/geometry/path_unittests.cc +++ b/impeller/geometry/path_unittests.cc @@ -50,6 +50,142 @@ TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) { ASSERT_EQ(polyline.GetPoint(4).x, 50); } +TEST(PathTest, PathSingleContour) { + // Closed shapes. + { + Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath(); + EXPECT_TRUE(path.IsSingleContour()); + } + + { + Path path = + PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath(); + + EXPECT_TRUE(path.IsSingleContour()); + } + + { + Path path = + PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath(); + + EXPECT_TRUE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddRoundRect(RoundRect::MakeRectRadius( + Rect::MakeXYWH(100, 100, 100, 100), 10)) + .TakePath(); + + EXPECT_TRUE(path.IsSingleContour()); + } + + // Open shapes. + { + Point p(100, 100); + Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath(); + + EXPECT_TRUE(path.IsSingleContour()); + } + + { + Path path = + PathBuilder{} + .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100}) + .TakePath(); + + EXPECT_TRUE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100}) + .TakePath(); + + EXPECT_TRUE(path.IsSingleContour()); + } +} + +TEST(PathTest, PathSingleContourDoubleShapes) { + // Closed shapes. + { + Path path = PathBuilder{} + .AddCircle({100, 100}, 50) + .AddCircle({100, 100}, 50) + .TakePath(); + EXPECT_FALSE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddOval(Rect::MakeXYWH(100, 100, 100, 100)) + .AddOval(Rect::MakeXYWH(100, 100, 100, 100)) + .TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddRect(Rect::MakeXYWH(100, 100, 100, 100)) + .AddRect(Rect::MakeXYWH(100, 100, 100, 100)) + .TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddRoundRect(RoundRect::MakeRectRadius( + Rect::MakeXYWH(100, 100, 100, 100), 10)) + .AddRoundRect(RoundRect::MakeRectRadius( + Rect::MakeXYWH(100, 100, 100, 100), 10)) + .TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddRoundRect(RoundRect::MakeRectXY( + Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20))) + .AddRoundRect(RoundRect::MakeRectXY( + Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20))) + .TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } + + // Open shapes. + { + Point p(100, 100); + Path path = + PathBuilder{}.AddLine(p, {200, 100}).AddLine(p, {200, 100}).TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } + + { + Path path = + PathBuilder{} + .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100}) + .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100}) + .TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } + + { + Path path = PathBuilder{} + .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100}) + .Close() + .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100}) + .TakePath(); + + EXPECT_FALSE(path.IsSingleContour()); + } +} + TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { // Closed shapes. { diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 6a2fc8260bb9e..8f2c4f756afe4 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -35,8 +35,9 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, HostBuffer& host_buffer, Scalar tolerance, bool supports_primitive_restart, - bool supports_triangle_fan) { - if (supports_primitive_restart) { + bool supports_triangle_fan, + bool line_strip) { + if (supports_primitive_restart && !line_strip) { // Primitive Restart. const auto [point_count, contour_count] = path.CountStorage(tolerance); BufferView point_buffer = host_buffer.Emplace( @@ -80,7 +81,8 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, FML_DCHECK(point_buffer_); FML_DCHECK(index_buffer_); - TessellateConvexInternal(path, *point_buffer_, *index_buffer_, tolerance); + TessellateConvexInternal(path, *point_buffer_, *index_buffer_, tolerance, + line_strip); if (point_buffer_->empty()) { return VertexBuffer{ @@ -102,19 +104,21 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, return VertexBuffer{ .vertex_buffer = std::move(vertex_buffer), .index_buffer = std::move(index_buffer), - .vertex_count = index_buffer_->size(), - .index_type = IndexType::k16bit, + .vertex_count = + line_strip ? point_buffer_->size() : index_buffer_->size(), + .index_type = line_strip ? IndexType::kNone : IndexType::k16bit, }; } void Tessellator::TessellateConvexInternal(const Path& path, std::vector& point_buffer, std::vector& index_buffer, - Scalar tolerance) { + Scalar tolerance, + bool line_strip) { point_buffer.clear(); index_buffer.clear(); - GLESVertexWriter writer(point_buffer, index_buffer); + GLESVertexWriter writer(point_buffer, index_buffer, line_strip); path.WritePolyline(tolerance, writer); } diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index 18fb013b2b1c9..6b9480b34cc0d 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -175,21 +175,22 @@ class Tessellator { /// @brief Given a convex path, create a triangle fan structure. /// /// @param[in] path The path to tessellate. + /// @param[in] host_buffer The host buffer for allocation of vertices/index + /// data. /// @param[in] tolerance The tolerance value for conversion of the path to /// a polyline. This value is often derived from the - /// Matrix::GetMaxBasisLength of the CTM applied to the - /// path for rendering. - /// - /// @return A point vector containing the vertices in triangle strip format. + /// Matrix::GetMaxBasisLengthXY of the CTM applied to + /// the path for rendering. + /// @param[in] line_strip if true, generates line strip geometry instead of a + /// filled convex hull. Defaults to false. /// - /// @param[in] host_buffer The host buffer for allocation of vertices/index - /// data. /// @return A vertex buffer containing all data from the provided curve. VertexBuffer TessellateConvex(const Path& path, HostBuffer& host_buffer, Scalar tolerance, bool supports_primitive_restart = false, - bool supports_triangle_fan = false); + bool supports_triangle_fan = false, + bool line_strip = false); /// Visible for testing. /// @@ -198,7 +199,8 @@ class Tessellator { static void TessellateConvexInternal(const Path& path, std::vector& point_buffer, std::vector& index_buffer, - Scalar tolerance); + Scalar tolerance, + bool line_strip = false); //---------------------------------------------------------------------------- /// @brief Create a temporary polyline. Only one per-process can exist at From f37cfbf12c6a6347ae813cb99f9d9ffb0ba81193 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sat, 2 Nov 2024 12:39:08 -0700 Subject: [PATCH 2/5] add missing flushes to tessellator. --- impeller/tessellator/tessellator.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 8f2c4f756afe4..9cab724451f52 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -54,6 +54,8 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, index_buffer.GetBuffer()->OnGetContents() + index_buffer.GetRange().offset)); path.WritePolyline(tolerance, writer); + point_buffer.GetBuffer()->Flush(point_buffer.GetRange()); + index_buffer.GetBuffer()->Flush(index_buffer.GetRange()); return VertexBuffer{ .vertex_buffer = std::move(point_buffer), @@ -69,6 +71,8 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, index_buffer.GetBuffer()->OnGetContents() + index_buffer.GetRange().offset)); path.WritePolyline(tolerance, writer); + point_buffer.GetBuffer()->Flush(point_buffer.GetRange()); + index_buffer.GetBuffer()->Flush(index_buffer.GetRange()); return VertexBuffer{ .vertex_buffer = std::move(point_buffer), From 34ba6ac7722015f5cd2e7a53758478025a415ac7 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 11:05:43 -0800 Subject: [PATCH 3/5] line strip tessellator. --- impeller/geometry/path_component.cc | 19 ++++++++++++++----- impeller/geometry/path_component.h | 22 +++++++++++++++++++--- impeller/tessellator/tessellator.cc | 24 +++++++++++++----------- impeller/tessellator/tessellator.h | 29 +++++++++++++++++++++++------ 4 files changed, 69 insertions(+), 25 deletions(-) diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index a83833f61c1d1..7d41d1aa12790 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -77,16 +77,25 @@ void StripVertexWriter::Write(Point point) { point_buffer_[count_++] = point; } +/////////// LineStripVertexWriter //////// + +LineStripVertexWriter::LineStripVertexWriter(std::vector& points) : points_(points) {} + +void LineStripVertexWriter::EndContour() {} + +void LineStripVertexWriter::Write(Point point) { + +} + + /////////// GLESVertexWriter /////////// GLESVertexWriter::GLESVertexWriter(std::vector& points, - std::vector& indices, - bool line_strip) - : points_(points), indices_(indices), line_strip_(line_strip) {} + std::vector& indices) + : points_(points), indices_(indices) {} void GLESVertexWriter::EndContour() { - if (points_.size() == 0u || contour_start_ == points_.size() - 1 || - line_strip_) { + if (points_.size() == 0u || contour_start_ == points_.size() - 1) { // Empty or first contour. return; } diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index c86750723b69a..3dc46ff731cab 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -67,12 +67,29 @@ class StripVertexWriter : public VertexWriter { uint16_t* index_buffer_ = nullptr; }; +/// @brief A vertex writer that generates a line strip topology. +class LineStripVertexWriter : public VertexWriter { + public: + explicit LineStripVertexWriter(std::vector& points); + + ~LineStripVertexWriter() = default; + + void EndContour() override; + + void Write(Point point) override; + + private: + size_t offset_ = 0u; + std::vector& points_; + std::vector overflow_; +}; + + /// @brief A vertex writer that has no hardware requirements. class GLESVertexWriter : public VertexWriter { public: explicit GLESVertexWriter(std::vector& points, - std::vector& indices, - bool line_strip = false); + std::vector& indices); ~GLESVertexWriter() = default; @@ -85,7 +102,6 @@ class GLESVertexWriter : public VertexWriter { size_t contour_start_ = 0u; std::vector& points_; std::vector& indices_; - const bool line_strip_; }; struct LinearPathComponent { diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 9cab724451f52..62efa08823898 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -35,9 +35,8 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, HostBuffer& host_buffer, Scalar tolerance, bool supports_primitive_restart, - bool supports_triangle_fan, - bool line_strip) { - if (supports_primitive_restart && !line_strip) { + bool supports_triangle_fan) { + if (supports_primitive_restart) { // Primitive Restart. const auto [point_count, contour_count] = path.CountStorage(tolerance); BufferView point_buffer = host_buffer.Emplace( @@ -85,8 +84,7 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, FML_DCHECK(point_buffer_); FML_DCHECK(index_buffer_); - TessellateConvexInternal(path, *point_buffer_, *index_buffer_, tolerance, - line_strip); + TessellateConvexInternal(path, *point_buffer_, *index_buffer_, tolerance); if (point_buffer_->empty()) { return VertexBuffer{ @@ -108,21 +106,25 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, return VertexBuffer{ .vertex_buffer = std::move(vertex_buffer), .index_buffer = std::move(index_buffer), - .vertex_count = - line_strip ? point_buffer_->size() : index_buffer_->size(), - .index_type = line_strip ? IndexType::kNone : IndexType::k16bit, + .vertex_count = index_buffer_->size(), + .index_type = IndexType::k16bit, }; } +VertexBuffer Tessellator::GenerateLineStrip(const Path& path, + HostBuffer& host_buffer, + Scalar tolerance) { + +} + void Tessellator::TessellateConvexInternal(const Path& path, std::vector& point_buffer, std::vector& index_buffer, - Scalar tolerance, - bool line_strip) { + Scalar tolerance) { point_buffer.clear(); index_buffer.clear(); - GLESVertexWriter writer(point_buffer, index_buffer, line_strip); + GLESVertexWriter writer(point_buffer, index_buffer); path.WritePolyline(tolerance, writer); } diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index 6b9480b34cc0d..43dc01d6552f5 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -181,16 +181,34 @@ class Tessellator { /// a polyline. This value is often derived from the /// Matrix::GetMaxBasisLengthXY of the CTM applied to /// the path for rendering. - /// @param[in] line_strip if true, generates line strip geometry instead of a - /// filled convex hull. Defaults to false. /// /// @return A vertex buffer containing all data from the provided curve. VertexBuffer TessellateConvex(const Path& path, HostBuffer& host_buffer, Scalar tolerance, bool supports_primitive_restart = false, - bool supports_triangle_fan = false, - bool line_strip = false); + bool supports_triangle_fan = false); + + //---------------------------------------------------------------------------- + /// @brief Given a path, create a line strip primitive structure. + /// + /// A line strip is a series of vertices that draws a line + /// rendered at a specified width (in our case, always 1.0 + /// physical pixel) that is tessellated by the rasterizer. See + /// also PrimitiveType::kLineStrip. + /// + /// @param[in] path The path to tessellate. + /// @param[in] host_buffer The host buffer for allocation of vertices/index + /// data. + /// @param[in] tolerance The tolerance value for conversion of the path to + /// a polyline. This value is often derived from the + /// Matrix::GetMaxBasisLengthXY of the CTM applied to + /// the path for rendering. + /// + /// @return A vertex buffer containing all data from the provided curve. + VertexBuffer GenerateLineStrip(const Path& path, + HostBuffer& host_buffer, + Scalar tolerance); /// Visible for testing. /// @@ -199,8 +217,7 @@ class Tessellator { static void TessellateConvexInternal(const Path& path, std::vector& point_buffer, std::vector& index_buffer, - Scalar tolerance, - bool line_strip = false); + Scalar tolerance); //---------------------------------------------------------------------------- /// @brief Create a temporary polyline. Only one per-process can exist at From 397f7ad5c447ff8e48585f0352be6da4f5eb350b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 11:45:52 -0800 Subject: [PATCH 4/5] line strip primitive generator. --- impeller/entity/entity_unittests.cc | 54 +++++++++++++++++++++++++++++ impeller/geometry/path_component.cc | 15 +++++++- impeller/geometry/path_component.h | 5 ++- impeller/tessellator/tessellator.cc | 39 +++++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 1b1a9a903913c..331f9b51ac973 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -14,6 +14,7 @@ #include "gtest/gtest.h" #include "impeller/core/device_buffer.h" #include "impeller/core/formats.h" +#include "impeller/core/host_buffer.h" #include "impeller/core/texture_descriptor.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/conical_gradient_contents.h" @@ -2424,6 +2425,59 @@ TEST_P(EntityTest, GiantStrokePathAllocation) { EXPECT_NEAR(point.y, expected[4].y, 0.1); } +TEST_P(EntityTest, GiantLineStripPathAllocation) { + PathBuilder builder{}; + for (int i = 0; i < 10000; i++) { + builder.LineTo(Point(i, i)); + } + Path path = builder.TakePath(); + + ContentContext content_context(GetContext(), /*typographer_context=*/nullptr); + Entity entity; + + auto host_buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(), + GetContext()->GetIdleWaiter()); + auto tessellator = Tessellator(); + + auto vertex_buffer = tessellator.GenerateLineStrip(path, *host_buffer, 1.0); + + // Validate the buffer data overflowed the small buffer + EXPECT_GT(vertex_buffer.vertex_count, kPointArenaSize); + + // Validate that there are no uninitialized points near the gap. + Point* written_data = reinterpret_cast( + (vertex_buffer.vertex_buffer.GetBuffer()->OnGetContents() + + vertex_buffer.vertex_buffer.GetRange().offset)); + + std::vector expected = { + Point(4093, 4093), // + Point(4094, 4094), // + Point(4095, 4095), // + Point(4096, 4096), // + Point(4097, 4097) // + }; + + Point point = written_data[kPointArenaSize - 2]; + EXPECT_NEAR(point.x, expected[0].x, 0.1); + EXPECT_NEAR(point.y, expected[0].y, 0.1); + + point = written_data[kPointArenaSize - 1]; + EXPECT_NEAR(point.x, expected[1].x, 0.1); + EXPECT_NEAR(point.y, expected[1].y, 0.1); + + point = written_data[kPointArenaSize]; + EXPECT_NEAR(point.x, expected[2].x, 0.1); + EXPECT_NEAR(point.y, expected[2].y, 0.1); + + point = written_data[kPointArenaSize + 1]; + EXPECT_NEAR(point.x, expected[3].x, 0.1); + EXPECT_NEAR(point.y, expected[3].y, 0.1); + + point = written_data[kPointArenaSize + 2]; + EXPECT_NEAR(point.x, expected[4].x, 0.1); + EXPECT_NEAR(point.y, expected[4].y, 0.1); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 7d41d1aa12790..a302bceea3cee 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -5,6 +5,7 @@ #include "path_component.h" #include +#include #include "impeller/geometry/scalar.h" #include "impeller/geometry/wangs_formula.h" @@ -79,14 +80,26 @@ void StripVertexWriter::Write(Point point) { /////////// LineStripVertexWriter //////// -LineStripVertexWriter::LineStripVertexWriter(std::vector& points) : points_(points) {} +LineStripVertexWriter::LineStripVertexWriter(std::vector& points) + : points_(points) {} void LineStripVertexWriter::EndContour() {} void LineStripVertexWriter::Write(Point point) { + if (offset_ >= points_.size()) { + overflow_.push_back(point); + } else { + points_[offset_++] = point; + } +} +const std::vector& LineStripVertexWriter::GetOveriszedBuffer() const { + return overflow_; } +std::pair LineStripVertexWriter::GetVertexCount() const { + return std::make_pair(offset_, overflow_.size()); +} /////////// GLESVertexWriter /////////// diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 3dc46ff731cab..62c3e73769f34 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -78,13 +78,16 @@ class LineStripVertexWriter : public VertexWriter { void Write(Point point) override; + std::pair GetVertexCount() const; + + const std::vector& GetOveriszedBuffer() const; + private: size_t offset_ = 0u; std::vector& points_; std::vector overflow_; }; - /// @brief A vertex writer that has no hardware requirements. class GLESVertexWriter : public VertexWriter { public: diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index a1354b8101326..2122e460a4519 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -4,6 +4,7 @@ #include "impeller/tessellator/tessellator.h" #include +#include #include "impeller/core/device_buffer.h" #include "impeller/geometry/path_component.h" @@ -119,7 +120,45 @@ VertexBuffer Tessellator::TessellateConvex(const Path& path, VertexBuffer Tessellator::GenerateLineStrip(const Path& path, HostBuffer& host_buffer, Scalar tolerance) { + LineStripVertexWriter writer(stroke_points_); + path.WritePolyline(tolerance, writer); + + const auto [arena_length, oversized_length] = writer.GetVertexCount(); + if (oversized_length == 0) { + return VertexBuffer{ + .vertex_buffer = + host_buffer.Emplace(stroke_points_.data(), + arena_length * sizeof(Point), alignof(Point)), + .index_buffer = {}, + .vertex_count = arena_length, + .index_type = IndexType::kNone, + }; + } + const std::vector& oversized_data = writer.GetOveriszedBuffer(); + BufferView buffer_view = host_buffer.Emplace( + /*buffer=*/nullptr, // + (arena_length + oversized_length) * sizeof(Point), // + alignof(Point) // + ); + memcpy(buffer_view.GetBuffer()->OnGetContents() + + buffer_view.GetRange().offset, // + stroke_points_.data(), // + arena_length * sizeof(Point) // + ); + memcpy(buffer_view.GetBuffer()->OnGetContents() + + buffer_view.GetRange().offset + arena_length * sizeof(Point), // + oversized_data.data(), // + oversized_data.size() * sizeof(Point) // + ); + buffer_view.GetBuffer()->Flush(buffer_view.GetRange()); + + return VertexBuffer{ + .vertex_buffer = buffer_view, + .index_buffer = {}, + .vertex_count = arena_length + oversized_length, + .index_type = IndexType::kNone, + }; } void Tessellator::TessellateConvexInternal(const Path& path, From 879f9d08279ffda31c9d208dada0505027b15c99 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 11 Nov 2024 12:00:03 -0800 Subject: [PATCH 5/5] typo --- impeller/entity/geometry/stroke_path_geometry.cc | 4 ++-- impeller/geometry/path_component.cc | 2 +- impeller/geometry/path_component.h | 2 +- impeller/tessellator/tessellator.cc | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/impeller/entity/geometry/stroke_path_geometry.cc b/impeller/entity/geometry/stroke_path_geometry.cc index ec6178b6036ff..a4fc27edbaf73 100644 --- a/impeller/entity/geometry/stroke_path_geometry.cc +++ b/impeller/entity/geometry/stroke_path_geometry.cc @@ -41,7 +41,7 @@ class PositionWriter { bool HasOversizedBuffer() const { return !oversized_.empty(); } - const std::vector& GetOveriszedBuffer() const { return oversized_; } + const std::vector& GetOversizedBuffer() const { return oversized_; } private: std::vector& points_; @@ -618,7 +618,7 @@ GeometryResult StrokePathGeometry::GetPositionBuffer( .mode = GeometryResult::Mode::kPreventOverdraw}; } const std::vector& oversized_data = - position_writer.GetOveriszedBuffer(); + position_writer.GetOversizedBuffer(); BufferView buffer_view = host_buffer.Emplace( /*buffer=*/nullptr, // (arena_length + oversized_length) * sizeof(Point), // diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index a302bceea3cee..1637bc9eb4a06 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -93,7 +93,7 @@ void LineStripVertexWriter::Write(Point point) { } } -const std::vector& LineStripVertexWriter::GetOveriszedBuffer() const { +const std::vector& LineStripVertexWriter::GetOversizedBuffer() const { return overflow_; } diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 62c3e73769f34..b558c006a36e6 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -80,7 +80,7 @@ class LineStripVertexWriter : public VertexWriter { std::pair GetVertexCount() const; - const std::vector& GetOveriszedBuffer() const; + const std::vector& GetOversizedBuffer() const; private: size_t offset_ = 0u; diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 2122e460a4519..383440658cc7f 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -135,7 +135,7 @@ VertexBuffer Tessellator::GenerateLineStrip(const Path& path, .index_type = IndexType::kNone, }; } - const std::vector& oversized_data = writer.GetOveriszedBuffer(); + const std::vector& oversized_data = writer.GetOversizedBuffer(); BufferView buffer_view = host_buffer.Emplace( /*buffer=*/nullptr, // (arena_length + oversized_length) * sizeof(Point), //