From e2e67ec003c70b9f451bc838a3089b2fe0848d39 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sun, 7 Apr 2024 15:21:58 -0700 Subject: [PATCH 1/5] path stuff testing. --- .../entity/geometry/fill_path_geometry.cc | 11 +-- impeller/geometry/path.cc | 46 ++++++++++ impeller/geometry/path.h | 12 +++ impeller/geometry/path_component.cc | 79 +++--------------- .../renderer/backend/vulkan/context_vk.cc | 2 +- .../renderer/backend/vulkan/pipeline_vk.cc | 1 + impeller/renderer/render_pass.h | 3 + impeller/tessellator/tessellator.cc | 83 ++++++++++++++++++- impeller/tessellator/tessellator.h | 8 ++ 9 files changed, 168 insertions(+), 77 deletions(-) diff --git a/impeller/entity/geometry/fill_path_geometry.cc b/impeller/entity/geometry/fill_path_geometry.cc index 7f0f807a2fced..2fe98cee94fcc 100644 --- a/impeller/entity/geometry/fill_path_geometry.cc +++ b/impeller/entity/geometry/fill_path_geometry.cc @@ -69,17 +69,10 @@ GeometryResult FillPathGeometry::GetPositionBuffer( } } - auto points = renderer.GetTessellator()->TessellateConvex( - path_, entity.GetTransform().GetMaxBasisLength()); - - vertex_buffer.vertex_buffer = host_buffer.Emplace( - points.data(), points.size() * sizeof(Point), alignof(Point)); - vertex_buffer.index_buffer = {}, vertex_buffer.vertex_count = points.size(); - vertex_buffer.index_type = IndexType::kNone; - return GeometryResult{ .type = PrimitiveType::kTriangleStrip, - .vertex_buffer = vertex_buffer, + .vertex_buffer = renderer.GetTessellator()->TessellateConvex2( + path_, host_buffer, entity.GetTransform().GetMaxBasisLength()), .transform = entity.GetShaderTransform(pass), .mode = GetResultMode(), }; diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 2eb682f5ff722..6f9d037d1ef7f 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -336,6 +336,52 @@ Path::Polyline Path::CreatePolyline( return polyline; } +void Path::WritePolyline(PolylineWriter& writer, Scalar scale) const { + auto& path_components = data_->components; + auto& path_points = data_->points; + + for (size_t component_i = 0; component_i < path_components.size(); + component_i++) { + const auto& path_component = path_components[component_i]; + switch (path_component.type) { + case ComponentType::kLinear: { + const LinearPathComponent* linear = + reinterpret_cast( + &path_points[path_component.index]); + writer.Write(linear->p1); + writer.Write(linear->p2); + break; + } + case ComponentType::kQuadratic: { + const QuadraticPathComponent* quad = + reinterpret_cast( + &path_points[path_component.index]); + quad->ToLinearPathComponents( + scale, [&writer](Point point) { writer.Write(point); }); + + for (size_t i = 1; i < 12; i += 1) { + writer.Write(quad->Solve(i / 12.0)); + } + writer.Write(quad->p2); + break; + } + case ComponentType::kCubic: { + const CubicPathComponent* cubic = + reinterpret_cast( + &path_points[path_component.index]); + for (size_t i = 1; i < 12; i += 1) { + writer.Write(cubic->Solve(i / 12.0)); + } + writer.Write(cubic->p2); + break; + } + case ComponentType::kContour: + writer.EndContour(); + break; + } + } +} + std::optional Path::GetBoundingBox() const { return data_->bounds; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index ba56a4101c1bd..11aff1c1594af 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -10,6 +10,7 @@ #include #include +#include "impeller/core/host_buffer.h" #include "impeller/geometry/path_component.h" namespace impeller { @@ -36,6 +37,13 @@ enum class Convexity { kConvex, }; +class PolylineWriter { + public: + virtual void EndContour() = 0; + + virtual void Write(Point a) = 0; +}; + //------------------------------------------------------------------------------ /// @brief Paths are lightweight objects that describe a collection of /// linear, quadratic, or cubic segments. These segments may be @@ -137,6 +145,10 @@ class Path { bool IsEmpty() const; + void WritePolyline(PolylineWriter& writer, Scalar scale) const; + + size_t ComputePolylineLength() const; + template using Applier = std::function; void EnumerateComponents( diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 55a4565708417..f6348911c2fea 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -98,10 +98,14 @@ Point QuadraticPathComponent::SolveDerivative(Scalar time) const { }; } -static Scalar ApproximateParabolaIntegral(Scalar x) { - constexpr Scalar d = 0.67; - return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); -} +/// Used to approximate quadratic curves using parabola. +/// +/// See +/// https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html +// static inline Scalar ApproximateParabolaIntegral(Scalar x) { +// constexpr Scalar d = 0.67; +// return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); +// } void QuadraticPathComponent::AppendPolylinePoints( Scalar scale_factor, @@ -114,42 +118,8 @@ void QuadraticPathComponent::AppendPolylinePoints( void QuadraticPathComponent::ToLinearPathComponents( Scalar scale_factor, const PointProc& proc) const { - auto tolerance = kDefaultCurveTolerance / scale_factor; - auto sqrt_tolerance = sqrt(tolerance); - - auto d01 = cp - p1; - auto d12 = p2 - cp; - auto dd = d01 - d12; - auto cross = (p2 - p1).Cross(dd); - auto x0 = d01.Dot(dd) * 1 / cross; - auto x2 = d12.Dot(dd) * 1 / cross; - auto scale = std::abs(cross / (hypot(dd.x, dd.y) * (x2 - x0))); - - auto a0 = ApproximateParabolaIntegral(x0); - auto a2 = ApproximateParabolaIntegral(x2); - Scalar val = 0.f; - if (std::isfinite(scale)) { - auto da = std::abs(a2 - a0); - auto sqrt_scale = sqrt(scale); - if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { - val = da * sqrt_scale; - } else { - // cusp case - auto xmin = sqrt_tolerance / sqrt_scale; - val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); - } - } - auto u0 = ApproximateParabolaIntegral(a0); - auto u2 = ApproximateParabolaIntegral(a2); - auto uscale = 1 / (u2 - u0); - - auto line_count = std::max(1., ceil(0.5 * val / sqrt_tolerance)); - auto step = 1 / line_count; - for (size_t i = 1; i < line_count; i += 1) { - auto u = i * step; - auto a = a0 + (a2 - a0) * u; - auto t = (ApproximateParabolaIntegral(a) - u0) * uscale; - proc(Solve(t)); + for (size_t i = 1; i < 12; i += 1) { + proc(Solve(1 / 12.0)); } proc(p2); } @@ -217,33 +187,10 @@ CubicPathComponent CubicPathComponent::Subsegment(Scalar t0, Scalar t1) const { void CubicPathComponent::ToLinearPathComponents(Scalar scale, const PointProc& proc) const { - constexpr Scalar accuracy = 0.1; - // The maximum error, as a vector from the cubic to the best approximating - // quadratic, is proportional to the third derivative, which is constant - // across the segment. Thus, the error scales down as the third power of - // the number of subdivisions. Our strategy then is to subdivide `t` evenly. - // - // This is an overestimate of the error because only the component - // perpendicular to the first derivative is important. But the simplicity is - // appealing. - - // This magic number is the square of 36 / sqrt(3). - // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html - auto max_hypot2 = 432.0 * accuracy * accuracy; - auto p1x2 = 3.0 * cp1 - p1; - auto p2x2 = 3.0 * cp2 - p2; - auto p = p2x2 - p1x2; - auto err = p.Dot(p); - auto quad_count = std::max(1., ceil(pow(err / max_hypot2, 1. / 6.0))); - for (size_t i = 0; i < quad_count; i++) { - auto t0 = i / quad_count; - auto t1 = (i + 1) / quad_count; - auto seg = Subsegment(t0, t1); - auto p1x2 = 3.0 * seg.cp1 - seg.p1; - auto p2x2 = 3.0 * seg.cp2 - seg.p2; - QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2) - .ToLinearPathComponents(scale, proc); + for (size_t i = 1; i < 12; i += 1) { + proc(Solve(i / 12.0)); } + proc(p2); } static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) { diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 58f6fa923f96a..3ef5b686a29c2 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -162,7 +162,7 @@ void ContextVK::Setup(Settings settings) { // 1. The user has explicitly enabled it. // 2. We are in a combination of debug mode, and running on Android. // (It's possible 2 is overly conservative and we can simplify this) - auto enable_validation = settings.enable_validation; + auto enable_validation = false; //settings.enable_validation; #if defined(FML_OS_ANDROID) && !defined(NDEBUG) enable_validation = true; diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc index 84374190a249e..ce8ac0bd674ec 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc @@ -287,6 +287,7 @@ std::unique_ptr PipelineVK::Create( vk::PipelineInputAssemblyStateCreateInfo input_assembly; const auto topology = ToVKPrimitiveTopology(desc.GetPrimitiveType()); input_assembly.setTopology(topology); + input_assembly.primitiveRestartEnable = true; pipeline_info.setPInputAssemblyState(&input_assembly); //---------------------------------------------------------------------------- diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index 1b424c35d1e3d..072eb8a6be6b8 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -46,6 +46,8 @@ class RenderPass : public ResourceBinder { void SetLabel(std::string label); + const std::shared_ptr& GetCommandBuffer(); + /// @brief Reserve [command_count] commands in the HAL command buffer. /// /// Note: this is not the native command buffer. @@ -166,6 +168,7 @@ class RenderPass : public ResourceBinder { protected: const std::shared_ptr context_; + std::shared_ptr command_buffer_; // The following properties: sample_count, pixel_format, // has_stencil_attachment, and render_target_size are cached on the // RenderTarget to speed up numerous lookups during rendering. This is safe as diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 665a6248217cb..005ecd1c04bc2 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -3,7 +3,11 @@ // found in the LICENSE file. #include "impeller/tessellator/tessellator.h" +#include +#include +#include "impeller/core/host_buffer.h" +#include "impeller/geometry/path.h" #include "third_party/libtess2/Include/tesselator.h" namespace impeller { @@ -33,8 +37,10 @@ static const TESSalloc kAlloc = { Tessellator::Tessellator() : point_buffer_(std::make_unique>()), + index_buffer_(std::make_unique>()), c_tessellator_(nullptr, &DestroyTessellator) { - point_buffer_->reserve(2048); + point_buffer_->reserve(4096); + index_buffer_->reserve(4096); TESSalloc alloc = kAlloc; { // libTess2 copies the TESSalloc despite the non-const argument. @@ -692,4 +698,79 @@ void Tessellator::GenerateFilledRoundRect( } } +class ConvexWriter : public PolylineWriter { + public: + explicit ConvexWriter(std::vector& points, + std::vector& indices) + : points_(points), indices_(indices) {} + + ~ConvexWriter() = default; + + void EndContour() override { + if (points_.size() == 0u || contour_start_ == points_.size() - 1) { + // Empty or first contour. + return; + } + + auto start = contour_start_; + auto end = points_.size() - 1; + // 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 (points_[end] == points_[start]) { + end--; + } + + if (contour_start_ != 0) { + indices_.emplace_back(0xFFFF); + } + indices_.emplace_back(start); + + size_t a = start + 1; + size_t b = end; + while (a < b) { + indices_.emplace_back(a); + indices_.emplace_back(b); + a++; + b--; + } + if (a == b) { + indices_.emplace_back(a); + } + contour_start_ = points_.size(); + } + + void Write(Point point) override { points_.emplace_back(point); } + + private: + size_t contour_start_ = 0u; + std::vector& points_; + std::vector& indices_; +}; + +VertexBuffer Tessellator::TessellateConvex2(const Path& path, + HostBuffer& host_bufer, + Scalar tolerance) { + index_buffer_->clear(); + point_buffer_->clear(); + ConvexWriter writer(*point_buffer_, *index_buffer_); + path.WritePolyline(writer, tolerance); + writer.EndContour(); + + auto vertex_buffer = host_bufer.Emplace( + point_buffer_->data(), sizeof(Point) * point_buffer_->size(), + alignof(Point)); + + auto index_buffer = host_bufer.Emplace( + index_buffer_->data(), sizeof(uint16_t) * index_buffer_->size(), + alignof(uint16_t)); + + return VertexBuffer{ + .vertex_buffer = std::move(vertex_buffer), + .index_buffer = std::move(index_buffer), + .vertex_count = index_buffer_->size(), + .index_type = IndexType::k16bit, + }; +} + } // namespace impeller diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index 6251e814aa764..b397d6abd7b9b 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -9,7 +9,10 @@ #include #include +#include "impeller/core/buffer_view.h" #include "impeller/core/formats.h" +#include "impeller/core/host_buffer.h" +#include "impeller/core/vertex_buffer.h" #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" #include "impeller/geometry/trig.h" @@ -212,6 +215,8 @@ class Tessellator { /// std::vector TessellateConvex(const Path& path, Scalar tolerance); + VertexBuffer TessellateConvex2(const Path& path, HostBuffer& host_buffer, Scalar tolerance); + //---------------------------------------------------------------------------- /// @brief Create a temporary polyline. Only one per-process can exist at /// a time. @@ -299,6 +304,9 @@ class Tessellator { private: /// Used for polyline generation. std::unique_ptr> point_buffer_; + /// Scratch data for indices. + std::unique_ptr> index_buffer_; + CTessellator c_tessellator_; // Data for variouos Circle/EllipseGenerator classes, cached per From c8b562723e1e3c1326fd3b9c901a007a5605ee49 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 9 Apr 2024 15:42:52 -0700 Subject: [PATCH 2/5] more cleanups. --- impeller/geometry/path.cc | 46 -------------- impeller/geometry/path.h | 50 +++++++++++---- .../renderer/backend/vulkan/context_vk.cc | 2 +- .../renderer/backend/vulkan/pipeline_vk.cc | 1 - impeller/tessellator/tessellator.cc | 63 ++++++++++++++----- impeller/tessellator/tessellator.h | 5 +- 6 files changed, 92 insertions(+), 75 deletions(-) diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 6f9d037d1ef7f..2eb682f5ff722 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -336,52 +336,6 @@ Path::Polyline Path::CreatePolyline( return polyline; } -void Path::WritePolyline(PolylineWriter& writer, Scalar scale) const { - auto& path_components = data_->components; - auto& path_points = data_->points; - - for (size_t component_i = 0; component_i < path_components.size(); - component_i++) { - const auto& path_component = path_components[component_i]; - switch (path_component.type) { - case ComponentType::kLinear: { - const LinearPathComponent* linear = - reinterpret_cast( - &path_points[path_component.index]); - writer.Write(linear->p1); - writer.Write(linear->p2); - break; - } - case ComponentType::kQuadratic: { - const QuadraticPathComponent* quad = - reinterpret_cast( - &path_points[path_component.index]); - quad->ToLinearPathComponents( - scale, [&writer](Point point) { writer.Write(point); }); - - for (size_t i = 1; i < 12; i += 1) { - writer.Write(quad->Solve(i / 12.0)); - } - writer.Write(quad->p2); - break; - } - case ComponentType::kCubic: { - const CubicPathComponent* cubic = - reinterpret_cast( - &path_points[path_component.index]); - for (size_t i = 1; i < 12; i += 1) { - writer.Write(cubic->Solve(i / 12.0)); - } - writer.Write(cubic->p2); - break; - } - case ComponentType::kContour: - writer.EndContour(); - break; - } - } -} - std::optional Path::GetBoundingBox() const { return data_->bounds; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 11aff1c1594af..fac003a6c7560 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -37,13 +37,6 @@ enum class Convexity { kConvex, }; -class PolylineWriter { - public: - virtual void EndContour() = 0; - - virtual void Write(Point a) = 0; -}; - //------------------------------------------------------------------------------ /// @brief Paths are lightweight objects that describe a collection of /// linear, quadratic, or cubic segments. These segments may be @@ -145,10 +138,45 @@ class Path { bool IsEmpty() const; - void WritePolyline(PolylineWriter& writer, Scalar scale) const; - - size_t ComputePolylineLength() const; - + template + void WritePolyline(VertexWriter& writer, Scalar scale) const { + auto& path_components = data_->components; + auto& path_points = data_->points; + + const auto& cb = [&writer](Point point) { writer.Write(point); }; + + for (size_t component_i = 0; component_i < path_components.size(); + component_i++) { + const auto& path_component = path_components[component_i]; + switch (path_component.type) { + case ComponentType::kLinear: { + const LinearPathComponent* linear = + reinterpret_cast( + &path_points[path_component.index]); + writer.Write(linear->p1); + writer.Write(linear->p2); + break; + } + case ComponentType::kQuadratic: { + const QuadraticPathComponent* quad = + reinterpret_cast( + &path_points[path_component.index]); + quad->ToLinearPathComponents(scale, cb); + break; + } + case ComponentType::kCubic: { + const CubicPathComponent* cubic = + reinterpret_cast( + &path_points[path_component.index]); + cubic->ToLinearPathComponents(scale, cb); + break; + } + case ComponentType::kContour: + writer.EndContour(); + break; + } + } + } template using Applier = std::function; void EnumerateComponents( diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 3ef5b686a29c2..652b1ce72cbd9 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -162,7 +162,7 @@ void ContextVK::Setup(Settings settings) { // 1. The user has explicitly enabled it. // 2. We are in a combination of debug mode, and running on Android. // (It's possible 2 is overly conservative and we can simplify this) - auto enable_validation = false; //settings.enable_validation; + auto enable_validation = false; // settings.enable_validation; #if defined(FML_OS_ANDROID) && !defined(NDEBUG) enable_validation = true; diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc index ce8ac0bd674ec..84374190a249e 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc @@ -287,7 +287,6 @@ std::unique_ptr PipelineVK::Create( vk::PipelineInputAssemblyStateCreateInfo input_assembly; const auto topology = ToVKPrimitiveTopology(desc.GetPrimitiveType()); input_assembly.setTopology(topology); - input_assembly.primitiveRestartEnable = true; pipeline_info.setPInputAssemblyState(&input_assembly); //---------------------------------------------------------------------------- diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 005ecd1c04bc2..f76e11c384b06 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -7,6 +7,7 @@ #include #include "impeller/core/host_buffer.h" +#include "impeller/geometry/color.h" #include "impeller/geometry/path.h" #include "third_party/libtess2/Include/tesselator.h" @@ -698,15 +699,16 @@ void Tessellator::GenerateFilledRoundRect( } } -class ConvexWriter : public PolylineWriter { +template +class VertexWriter { public: - explicit ConvexWriter(std::vector& points, + explicit VertexWriter(std::vector& points, std::vector& indices) : points_(points), indices_(indices) {} - ~ConvexWriter() = default; + ~VertexWriter() = default; - void EndContour() override { + void EndContour() { if (points_.size() == 0u || contour_start_ == points_.size() - 1) { // Empty or first contour. return; @@ -721,10 +723,21 @@ class ConvexWriter : public PolylineWriter { end--; } - if (contour_start_ != 0) { - indices_.emplace_back(0xFFFF); + if (contour_start_ > 0) { + // Triangle strip break. + indices_.emplace_back(end); + indices_.emplace_back(start); + indices_.emplace_back(start); + + // If the contour has an odd number of points, insert an extra point when + // bridging to the next contour to preserve the correct triangle winding + // order. + if (previous_contour_odd_points_) { + indices_.emplace_back(start); + } + } else { + indices_.emplace_back(start); } - indices_.emplace_back(start); size_t a = start + 1; size_t b = end; @@ -736,13 +749,22 @@ class ConvexWriter : public PolylineWriter { } if (a == b) { indices_.emplace_back(a); + previous_contour_odd_points_ = false; + } else { + previous_contour_odd_points_ = true; } contour_start_ = points_.size(); } - void Write(Point point) override { points_.emplace_back(point); } + void Write(Point point) { + points_.emplace_back(point); + if constexpr (write_uvs) { + points_.emplace_back(Point{0, 0}); + } + } private: + bool previous_contour_odd_points_ = false; size_t contour_start_ = 0u; std::vector& points_; std::vector& indices_; @@ -750,16 +772,27 @@ class ConvexWriter : public PolylineWriter { VertexBuffer Tessellator::TessellateConvex2(const Path& path, HostBuffer& host_bufer, - Scalar tolerance) { + Scalar tolerance, + bool create_uvs) { index_buffer_->clear(); point_buffer_->clear(); - ConvexWriter writer(*point_buffer_, *index_buffer_); - path.WritePolyline(writer, tolerance); - writer.EndContour(); - auto vertex_buffer = host_bufer.Emplace( - point_buffer_->data(), sizeof(Point) * point_buffer_->size(), - alignof(Point)); + size_t alignment_size = 0; + if (create_uvs) { + alignment_size = alignof(Vector4); + VertexWriter writer(*point_buffer_, *index_buffer_); + path.WritePolyline(writer, tolerance); + writer.EndContour(); + } else { + alignment_size = alignof(Point); + VertexWriter writer(*point_buffer_, *index_buffer_); + path.WritePolyline(writer, tolerance); + writer.EndContour(); + } + + auto vertex_buffer = + host_bufer.Emplace(point_buffer_->data(), + sizeof(Point) * point_buffer_->size(), alignment_size); auto index_buffer = host_bufer.Emplace( index_buffer_->data(), sizeof(uint16_t) * index_buffer_->size(), diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index b397d6abd7b9b..8666ec865a750 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -215,7 +215,10 @@ class Tessellator { /// std::vector TessellateConvex(const Path& path, Scalar tolerance); - VertexBuffer TessellateConvex2(const Path& path, HostBuffer& host_buffer, Scalar tolerance); + VertexBuffer TessellateConvex2(const Path& path, + HostBuffer& host_buffer, + Scalar tolerance, + bool create_uvs = false); //---------------------------------------------------------------------------- /// @brief Create a temporary polyline. Only one per-process can exist at From 208ee9ad81d4bd6dcf3e2cdbade1f0aa0cb2f967 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 9 Apr 2024 17:18:45 -0700 Subject: [PATCH 3/5] still wonky. --- impeller/geometry/path.h | 8 +- impeller/geometry/path_component.cc | 79 ++++++++++++++++--- impeller/tessellator/tessellator.cc | 2 +- impeller/tessellator/tessellator_unittests.cc | 1 + 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index fac003a6c7560..21e627fc877ae 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -153,7 +153,6 @@ class Path { const LinearPathComponent* linear = reinterpret_cast( &path_points[path_component.index]); - writer.Write(linear->p1); writer.Write(linear->p2); break; } @@ -172,6 +171,13 @@ class Path { break; } case ComponentType::kContour: + if (component_i == path_components.size() - 1) { + // If the last component is a contour, that means it's an empty + // contour, so skip it. + continue; + } + const auto& contour = data_->contours[path_component.index]; + writer.Write(contour.destination); writer.EndContour(); break; } diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index f6348911c2fea..55a4565708417 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -98,14 +98,10 @@ Point QuadraticPathComponent::SolveDerivative(Scalar time) const { }; } -/// Used to approximate quadratic curves using parabola. -/// -/// See -/// https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html -// static inline Scalar ApproximateParabolaIntegral(Scalar x) { -// constexpr Scalar d = 0.67; -// return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); -// } +static Scalar ApproximateParabolaIntegral(Scalar x) { + constexpr Scalar d = 0.67; + return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); +} void QuadraticPathComponent::AppendPolylinePoints( Scalar scale_factor, @@ -118,8 +114,42 @@ void QuadraticPathComponent::AppendPolylinePoints( void QuadraticPathComponent::ToLinearPathComponents( Scalar scale_factor, const PointProc& proc) const { - for (size_t i = 1; i < 12; i += 1) { - proc(Solve(1 / 12.0)); + auto tolerance = kDefaultCurveTolerance / scale_factor; + auto sqrt_tolerance = sqrt(tolerance); + + auto d01 = cp - p1; + auto d12 = p2 - cp; + auto dd = d01 - d12; + auto cross = (p2 - p1).Cross(dd); + auto x0 = d01.Dot(dd) * 1 / cross; + auto x2 = d12.Dot(dd) * 1 / cross; + auto scale = std::abs(cross / (hypot(dd.x, dd.y) * (x2 - x0))); + + auto a0 = ApproximateParabolaIntegral(x0); + auto a2 = ApproximateParabolaIntegral(x2); + Scalar val = 0.f; + if (std::isfinite(scale)) { + auto da = std::abs(a2 - a0); + auto sqrt_scale = sqrt(scale); + if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { + val = da * sqrt_scale; + } else { + // cusp case + auto xmin = sqrt_tolerance / sqrt_scale; + val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); + } + } + auto u0 = ApproximateParabolaIntegral(a0); + auto u2 = ApproximateParabolaIntegral(a2); + auto uscale = 1 / (u2 - u0); + + auto line_count = std::max(1., ceil(0.5 * val / sqrt_tolerance)); + auto step = 1 / line_count; + for (size_t i = 1; i < line_count; i += 1) { + auto u = i * step; + auto a = a0 + (a2 - a0) * u; + auto t = (ApproximateParabolaIntegral(a) - u0) * uscale; + proc(Solve(t)); } proc(p2); } @@ -187,10 +217,33 @@ CubicPathComponent CubicPathComponent::Subsegment(Scalar t0, Scalar t1) const { void CubicPathComponent::ToLinearPathComponents(Scalar scale, const PointProc& proc) const { - for (size_t i = 1; i < 12; i += 1) { - proc(Solve(i / 12.0)); + constexpr Scalar accuracy = 0.1; + // The maximum error, as a vector from the cubic to the best approximating + // quadratic, is proportional to the third derivative, which is constant + // across the segment. Thus, the error scales down as the third power of + // the number of subdivisions. Our strategy then is to subdivide `t` evenly. + // + // This is an overestimate of the error because only the component + // perpendicular to the first derivative is important. But the simplicity is + // appealing. + + // This magic number is the square of 36 / sqrt(3). + // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html + auto max_hypot2 = 432.0 * accuracy * accuracy; + auto p1x2 = 3.0 * cp1 - p1; + auto p2x2 = 3.0 * cp2 - p2; + auto p = p2x2 - p1x2; + auto err = p.Dot(p); + auto quad_count = std::max(1., ceil(pow(err / max_hypot2, 1. / 6.0))); + for (size_t i = 0; i < quad_count; i++) { + auto t0 = i / quad_count; + auto t1 = (i + 1) / quad_count; + auto seg = Subsegment(t0, t1); + auto p1x2 = 3.0 * seg.cp1 - seg.p1; + auto p2x2 = 3.0 * seg.cp2 - seg.p2; + QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2) + .ToLinearPathComponents(scale, proc); } - proc(p2); } static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) { diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index f76e11c384b06..6b15c41519369 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -725,7 +725,7 @@ class VertexWriter { if (contour_start_ > 0) { // Triangle strip break. - indices_.emplace_back(end); + indices_.emplace_back(start - 1); indices_.emplace_back(start); indices_.emplace_back(start); diff --git a/impeller/tessellator/tessellator_unittests.cc b/impeller/tessellator/tessellator_unittests.cc index 772a794b85f87..2a9a27daca8e9 100644 --- a/impeller/tessellator/tessellator_unittests.cc +++ b/impeller/tessellator/tessellator_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/testing/testing.h" #include "gtest/gtest.h" +#include "impeller/core/host_buffer.h" #include "impeller/geometry/geometry_asserts.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" From 1df3f20bb9009930707642bce33639da0145cc41 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 9 Apr 2024 19:38:57 -0700 Subject: [PATCH 4/5] ++ --- impeller/geometry/path.h | 8 +-- impeller/geometry/path_component.cc | 64 +++++++++++------------ impeller/geometry/path_component.h | 81 +++++++++++++++++++++++++++++ impeller/tessellator/tessellator.cc | 2 +- 4 files changed, 116 insertions(+), 39 deletions(-) diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 21e627fc877ae..73783ece68015 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -143,8 +143,6 @@ class Path { auto& path_components = data_->components; auto& path_points = data_->points; - const auto& cb = [&writer](Point point) { writer.Write(point); }; - for (size_t component_i = 0; component_i < path_components.size(); component_i++) { const auto& path_component = path_components[component_i]; @@ -160,14 +158,14 @@ class Path { const QuadraticPathComponent* quad = reinterpret_cast( &path_points[path_component.index]); - quad->ToLinearPathComponents(scale, cb); + quad->WriteLinearPathComponents(scale, writer); break; } case ComponentType::kCubic: { const CubicPathComponent* cubic = reinterpret_cast( &path_points[path_component.index]); - cubic->ToLinearPathComponents(scale, cb); + cubic->WriteLinearPathComponents(scale, writer); break; } case ComponentType::kContour: @@ -176,8 +174,6 @@ class Path { // contour, so skip it. continue; } - const auto& contour = data_->contours[path_component.index]; - writer.Write(contour.destination); writer.EndContour(); break; } diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 55a4565708417..59c0e83846d8e 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -98,7 +98,7 @@ Point QuadraticPathComponent::SolveDerivative(Scalar time) const { }; } -static Scalar ApproximateParabolaIntegral(Scalar x) { +Scalar ApproximateParabolaIntegral(Scalar x) { constexpr Scalar d = 0.67; return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); } @@ -114,41 +114,41 @@ void QuadraticPathComponent::AppendPolylinePoints( void QuadraticPathComponent::ToLinearPathComponents( Scalar scale_factor, const PointProc& proc) const { - auto tolerance = kDefaultCurveTolerance / scale_factor; - auto sqrt_tolerance = sqrt(tolerance); - - auto d01 = cp - p1; - auto d12 = p2 - cp; - auto dd = d01 - d12; - auto cross = (p2 - p1).Cross(dd); - auto x0 = d01.Dot(dd) * 1 / cross; - auto x2 = d12.Dot(dd) * 1 / cross; - auto scale = std::abs(cross / (hypot(dd.x, dd.y) * (x2 - x0))); - - auto a0 = ApproximateParabolaIntegral(x0); - auto a2 = ApproximateParabolaIntegral(x2); + Scalar tolerance = kDefaultCurveTolerance / scale_factor; + Scalar sqrt_tolerance = sqrt(tolerance); + + Point d01 = cp - p1; + Point d12 = p2 - cp; + Point dd = d01 - d12; + Scalar cross = (p2 - p1).Cross(dd); + Scalar x0 = d01.Dot(dd) * 1 / cross; + Scalar x2 = d12.Dot(dd) * 1 / cross; + Scalar scale = std::abs(cross / (FastHypot(dd.x, dd.y) * (x2 - x0))); + + Scalar a0 = ApproximateParabolaIntegral(x0); + Scalar a2 = ApproximateParabolaIntegral(x2); Scalar val = 0.f; - if (std::isfinite(scale)) { - auto da = std::abs(a2 - a0); - auto sqrt_scale = sqrt(scale); - if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { - val = da * sqrt_scale; - } else { - // cusp case - auto xmin = sqrt_tolerance / sqrt_scale; - val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); - } + // if (std::isfinite(scale)) { + Scalar da = std::abs(a2 - a0); + Scalar sqrt_scale = sqrt(scale); + if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { + val = da * sqrt_scale; + } else { + // cusp case + Scalar xmin = sqrt_tolerance / sqrt_scale; + val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); } - auto u0 = ApproximateParabolaIntegral(a0); - auto u2 = ApproximateParabolaIntegral(a2); - auto uscale = 1 / (u2 - u0); + // } + Scalar u0 = ApproximateParabolaIntegral(a0); + Scalar u2 = ApproximateParabolaIntegral(a2); + Scalar uscale = 1.0f / (u2 - u0); - auto line_count = std::max(1., ceil(0.5 * val / sqrt_tolerance)); - auto step = 1 / line_count; + Scalar line_count = std::max(1.0f, ceil(0.5f * val / sqrt_tolerance)); + Scalar step = 1.0f / line_count; for (size_t i = 1; i < line_count; i += 1) { - auto u = i * step; - auto a = a0 + (a2 - a0) * u; - auto t = (ApproximateParabolaIntegral(a) - u0) * uscale; + Scalar u = i * step; + Scalar a = a0 + (a2 - a0) * u; + Scalar t = (ApproximateParabolaIntegral(a) - u0) * uscale; proc(Solve(t)); } proc(p2); diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 58aa1f9f83530..74c1c5f0c59a0 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -16,6 +16,12 @@ namespace impeller { +Scalar ApproximateParabolaIntegral(Scalar x); + +constexpr Scalar FastHypot(Scalar a, Scalar b) { + return b + 0.337f * a; +} + // The default tolerance value for QuadraticCurveComponent::AppendPolylinePoints // and CubicCurveComponent::AppendPolylinePoints. It also impacts the number of // quadratics created when flattening a cubic curve to a polyline. @@ -84,6 +90,49 @@ struct QuadraticPathComponent { void ToLinearPathComponents(Scalar scale_factor, const PointProc& proc) const; + template + void WriteLinearPathComponents(Scalar scale_factor, + VertexWriter& writer) const { + Scalar tolerance = kDefaultCurveTolerance / scale_factor; + Scalar sqrt_tolerance = sqrt(tolerance); + + Point d01 = cp - p1; + Point d12 = p2 - cp; + Point dd = d01 - d12; + Scalar cross = (p2 - p1).Cross(dd); + Scalar x0 = d01.Dot(dd) * 1.0f / cross; + Scalar x2 = d12.Dot(dd) * 1.0f / cross; + Scalar scale = std::abs(cross / (FastHypot(dd.x, dd.y) * (x2 - x0))); + + Scalar a0 = ApproximateParabolaIntegral(x0); + Scalar a2 = ApproximateParabolaIntegral(x2); + Scalar val = 0.f; + // if (std::isfinite(scale)) { + Scalar da = std::abs(a2 - a0); + Scalar sqrt_scale = sqrt(scale); + if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { + val = da * sqrt_scale; + } else { + // cusp case + Scalar xmin = sqrt_tolerance / sqrt_scale; + val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); + } + // } + Scalar u0 = ApproximateParabolaIntegral(a0); + Scalar u2 = ApproximateParabolaIntegral(a2); + Scalar uscale = 1.0f / (u2 - u0); + + Scalar line_count = std::max(1.0f, ceil(0.5f * val / sqrt_tolerance)); + Scalar step = 1.0f / line_count; + for (size_t i = 1; i < line_count; i += 1) { + Scalar u = i * step; + Scalar a = a0 + (a2 - a0) * u; + Scalar t = (ApproximateParabolaIntegral(a) - u0) * uscale; + writer.Write(Solve(t)); + } + writer.Write(p2); + } + std::vector Extrema() const; bool operator==(const QuadraticPathComponent& other) const { @@ -134,6 +183,38 @@ struct CubicPathComponent { void ToLinearPathComponents(Scalar scale, const PointProc& proc) const; + template + void WriteLinearPathComponents(Scalar scale_factor, + VertexWriter& writer) const { + constexpr Scalar accuracy = 0.1f; + // The maximum error, as a vector from the cubic to the best approximating + // quadratic, is proportional to the third derivative, which is constant + // across the segment. Thus, the error scales down as the third power of + // the number of subdivisions. Our strategy then is to subdivide `t` evenly. + // + // This is an overestimate of the error because only the component + // perpendicular to the first derivative is important. But the simplicity is + // appealing. + + // This magic number is the square of 36 / sqrt(3). + // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html + Scalar max_hypot2 = 432.0f * accuracy * accuracy; + Point p1x2 = 3.0f * cp1 - p1; + Point p2x2 = 3.0f * cp2 - p2; + Point p = p2x2 - p1x2; + Scalar err = p.Dot(p); + Scalar quad_count = std::max(1.0f, ceil(pow(err / max_hypot2, 1.0f / 6.0f))); + for (size_t i = 0; i < quad_count; i++) { + Scalar t0 = i / quad_count; + Scalar t1 = (i + 1) / quad_count; + CubicPathComponent seg = Subsegment(t0, t1); + Point p1x2 = 3.0 * seg.cp1 - seg.p1; + Point p2x2 = 3.0 * seg.cp2 - seg.p2; + QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2) + .WriteLinearPathComponents(scale_factor, writer); + } + } + CubicPathComponent Subsegment(Scalar t0, Scalar t1) const; bool operator==(const CubicPathComponent& other) const { diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 6b15c41519369..d5c0dcc2a81bf 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -725,7 +725,7 @@ class VertexWriter { if (contour_start_ > 0) { // Triangle strip break. - indices_.emplace_back(start - 1); + indices_.emplace_back(indices_.back()); indices_.emplace_back(start); indices_.emplace_back(start); From dab482e70d89633fcce36f69de4855cdd61f1764 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Apr 2024 09:33:16 -0700 Subject: [PATCH 5/5] more testing. --- .../entity/geometry/stroke_path_geometry.cc | 126 +++++++++--------- impeller/geometry/BUILD.gn | 2 + impeller/geometry/path.cc | 98 ++++++++++++-- impeller/geometry/path.h | 48 +------ impeller/geometry/path_component.cc | 105 +++------------ impeller/geometry/path_component.h | 102 +------------- impeller/geometry/path_unittests.cc | 18 +-- impeller/geometry/vector.h | 3 + impeller/geometry/vertex_writer.h | 81 +++++++++++ impeller/geometry/wangs_formula.h | 76 +++++++++++ .../renderer/backend/vulkan/pipeline_vk.cc | 1 + impeller/tessellator/tessellator.cc | 88 +----------- 12 files changed, 358 insertions(+), 390 deletions(-) create mode 100644 impeller/geometry/vertex_writer.h create mode 100644 impeller/geometry/wangs_formula.h diff --git a/impeller/entity/geometry/stroke_path_geometry.cc b/impeller/entity/geometry/stroke_path_geometry.cc index 49f8ee92d0a07..0a710599a32ac 100644 --- a/impeller/entity/geometry/stroke_path_geometry.cc +++ b/impeller/entity/geometry/stroke_path_geometry.cc @@ -321,35 +321,36 @@ void CreateRoundCap(VertexWriter& vtx_builder, const Point& offset, Scalar scale, bool reverse) { - Point orientation = offset * (reverse ? -1 : 1); - Point forward(offset.y, -offset.x); - Point forward_normal = forward.Normalize(); - - CubicPathComponent arc; - if (reverse) { - arc = CubicPathComponent( - forward, forward + orientation * PathBuilder::kArcApproximationMagic, - orientation + forward * PathBuilder::kArcApproximationMagic, - orientation); - } else { - arc = CubicPathComponent( - orientation, - orientation + forward * PathBuilder::kArcApproximationMagic, - forward + orientation * PathBuilder::kArcApproximationMagic, forward); - } - - Point vtx = position + orientation; - vtx_builder.AppendVertex(vtx); - vtx = position - orientation; - vtx_builder.AppendVertex(vtx); - - arc.ToLinearPathComponents(scale, [&vtx_builder, &vtx, forward_normal, - position](const Point& point) { - vtx = position + point; - vtx_builder.AppendVertex(vtx); - vtx = position + (-point).Reflect(forward_normal); - vtx_builder.AppendVertex(vtx); - }); + // Point orientation = offset * (reverse ? -1 : 1); + // Point forward(offset.y, -offset.x); + // Point forward_normal = forward.Normalize(); + + // CubicPathComponent arc; + // if (reverse) { + // arc = CubicPathComponent( + // forward, forward + orientation * PathBuilder::kArcApproximationMagic, + // orientation + forward * PathBuilder::kArcApproximationMagic, + // orientation); + // } else { + // arc = CubicPathComponent( + // orientation, + // orientation + forward * PathBuilder::kArcApproximationMagic, + // forward + orientation * PathBuilder::kArcApproximationMagic, + // forward); + // } + + // Point vtx = position + orientation; + // vtx_builder.AppendVertex(vtx); + // vtx = position - orientation; + // vtx_builder.AppendVertex(vtx); + + // arc.ToLinearPathComponents(scale, [&vtx_builder, &vtx, forward_normal, + // position](const Point& point) { + // vtx = position + point; + // vtx_builder.AppendVertex(vtx); + // vtx = position + (-point).Reflect(forward_normal); + // vtx_builder.AppendVertex(vtx); + // }); } template @@ -425,38 +426,41 @@ void CreateRoundJoin(VertexWriter& vtx_builder, const Point& end_offset, Scalar miter_limit, Scalar scale) { - Point start_normal = start_offset.Normalize(); - Point end_normal = end_offset.Normalize(); - - // 0 for no joint (straight line), 1 for max joint (180 degrees). - Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2; - if (ScalarNearlyEqual(alignment, 0)) { - return; - } - - Scalar direction = CreateBevelAndGetDirection(vtx_builder, position, - start_offset, end_offset); - - Point middle = - (start_offset + end_offset).Normalize() * start_offset.GetLength(); - Point middle_normal = middle.Normalize(); - - Point middle_handle = middle + Point(-middle.y, middle.x) * - PathBuilder::kArcApproximationMagic * - alignment * direction; - Point start_handle = start_offset + Point(start_offset.y, -start_offset.x) * - PathBuilder::kArcApproximationMagic * - alignment * direction; - - VS::PerVertexData vtx; - CubicPathComponent(start_offset, start_handle, middle_handle, middle) - .ToLinearPathComponents(scale, [&vtx_builder, direction, &vtx, position, - middle_normal](const Point& point) { - vtx.position = position + point * direction; - vtx_builder.AppendVertex(vtx.position); - vtx.position = position + (-point * direction).Reflect(middle_normal); - vtx_builder.AppendVertex(vtx.position); - }); + // Point start_normal = start_offset.Normalize(); + // Point end_normal = end_offset.Normalize(); + + // // 0 for no joint (straight line), 1 for max joint (180 degrees). + // Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2; + // if (ScalarNearlyEqual(alignment, 0)) { + // return; + // } + + // Scalar direction = CreateBevelAndGetDirection(vtx_builder, position, + // start_offset, end_offset); + + // Point middle = + // (start_offset + end_offset).Normalize() * start_offset.GetLength(); + // Point middle_normal = middle.Normalize(); + + // Point middle_handle = middle + Point(-middle.y, middle.x) * + // PathBuilder::kArcApproximationMagic * + // alignment * direction; + // Point start_handle = start_offset + Point(start_offset.y, -start_offset.x) + // * + // PathBuilder::kArcApproximationMagic + // * alignment * direction; + + // VS::PerVertexData vtx; + // CubicPathComponent(start_offset, start_handle, middle_handle, middle) + // .ToLinearPathComponents(scale, [&vtx_builder, direction, &vtx, + // position, + // middle_normal](const Point& point) { + // vtx.position = position + point * direction; + // vtx_builder.AppendVertex(vtx.position); + // vtx.position = position + (-point * + // direction).Reflect(middle_normal); + // vtx_builder.AppendVertex(vtx.position); + // }); } template diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index 0db775b86090a..bde118e284820 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -43,6 +43,8 @@ impeller_component("geometry") { "type_traits.h", "vector.cc", "vector.h", + "vertex_writer.h", + "wangs_formula.h", ] deps = [ diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 2eb682f5ff722..6cbfedc015e04 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -10,6 +10,7 @@ #include "flutter/fml/logging.h" #include "impeller/geometry/path_component.h" #include "impeller/geometry/point.h" +#include "impeller/geometry/wangs_formula.h" namespace impeller { @@ -288,9 +289,9 @@ Path::Polyline Path::CreatePolyline( .component_start_index = polyline.points->size() - 1, .is_curve = false, }); - reinterpret_cast( - &path_points[path_component.index]) - ->AppendPolylinePoints(*polyline.points); + // reinterpret_cast( + // &path_points[path_component.index]) + // ->AppendPolylinePoints(*polyline.points); previous_path_component_index = component_i; break; case ComponentType::kQuadratic: @@ -298,9 +299,9 @@ Path::Polyline Path::CreatePolyline( .component_start_index = polyline.points->size() - 1, .is_curve = true, }); - reinterpret_cast( - &path_points[path_component.index]) - ->AppendPolylinePoints(scale, *polyline.points); + // reinterpret_cast( + // &path_points[path_component.index]) + // ->AppendPolylinePoints(scale, *polyline.points); previous_path_component_index = component_i; break; case ComponentType::kCubic: @@ -308,9 +309,9 @@ Path::Polyline Path::CreatePolyline( .component_start_index = polyline.points->size() - 1, .is_curve = true, }); - reinterpret_cast( - &path_points[path_component.index]) - ->AppendPolylinePoints(scale, *polyline.points); + // reinterpret_cast( + // &path_points[path_component.index]) + // ->AppendPolylinePoints(scale, *polyline.points); previous_path_component_index = component_i; break; case ComponentType::kContour: @@ -349,4 +350,83 @@ std::optional Path::GetTransformedBoundingBox( return bounds->TransformBounds(transform); } +std::pair Path::ComputeStorage(Scalar scale) const { + auto& path_components = data_->components; + auto& path_points = data_->points; + + size_t count = 0; + size_t contours = 0; + for (size_t component_i = 0; component_i < path_components.size(); + component_i++) { + const auto& path_component = path_components[component_i]; + switch (path_component.type) { + case ComponentType::kLinear: { + count++; + break; + } + case ComponentType::kQuadratic: { + const QuadraticPathComponent* quad = + reinterpret_cast( + &path_points[path_component.index]); + count += quadratic(scale, *quad); + break; + } + case ComponentType::kCubic: { + const CubicPathComponent* cub = + reinterpret_cast( + &path_points[path_component.index]); + count += cubic(scale, *cub); + break; + } + case ComponentType::kContour: + if (component_i > 0 && component_i < path_components.size() - 1) { + contours++; + } + break; + } + } + return std::make_pair(count, count + contours); +} + +void Path::WritePolyline(VertexWriter& writer, Scalar scale) const { + auto& path_components = data_->components; + auto& path_points = data_->points; + + for (size_t component_i = 0; component_i < path_components.size(); + component_i++) { + const auto& path_component = path_components[component_i]; + switch (path_component.type) { + case ComponentType::kLinear: { + const LinearPathComponent* linear = + reinterpret_cast( + &path_points[path_component.index]); + writer.Write(linear->p2); + break; + } + case ComponentType::kQuadratic: { + const QuadraticPathComponent* quad = + reinterpret_cast( + &path_points[path_component.index]); + quad->ToLinearPathComponents(scale, writer); + break; + } + case ComponentType::kCubic: { + const CubicPathComponent* cubic = + reinterpret_cast( + &path_points[path_component.index]); + cubic->ToLinearPathComponents(scale, writer); + break; + } + case ComponentType::kContour: + if (component_i == path_components.size() - 1) { + // If the last component is a contour, that means it's an empty + // contour, so skip it. + continue; + } + writer.EndContour(); + break; + } + } +} + } // namespace impeller diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 73783ece68015..5e18905ebfca8 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -10,8 +10,8 @@ #include #include -#include "impeller/core/host_buffer.h" #include "impeller/geometry/path_component.h" +#include "impeller/geometry/vertex_writer.h" namespace impeller { @@ -138,47 +138,11 @@ class Path { bool IsEmpty() const; - template - void WritePolyline(VertexWriter& writer, Scalar scale) const { - auto& path_components = data_->components; - auto& path_points = data_->points; - - for (size_t component_i = 0; component_i < path_components.size(); - component_i++) { - const auto& path_component = path_components[component_i]; - switch (path_component.type) { - case ComponentType::kLinear: { - const LinearPathComponent* linear = - reinterpret_cast( - &path_points[path_component.index]); - writer.Write(linear->p2); - break; - } - case ComponentType::kQuadratic: { - const QuadraticPathComponent* quad = - reinterpret_cast( - &path_points[path_component.index]); - quad->WriteLinearPathComponents(scale, writer); - break; - } - case ComponentType::kCubic: { - const CubicPathComponent* cubic = - reinterpret_cast( - &path_points[path_component.index]); - cubic->WriteLinearPathComponents(scale, writer); - break; - } - case ComponentType::kContour: - if (component_i == path_components.size() - 1) { - // If the last component is a contour, that means it's an empty - // contour, so skip it. - continue; - } - writer.EndContour(); - break; - } - } - } + // vertex count, index count + std::pair ComputeStorage(Scalar scale) const; + + void WritePolyline(VertexWriter& writer, Scalar scale) const; + template using Applier = std::function; void EnumerateComponents( diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 59c0e83846d8e..904c3c31dc847 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -5,6 +5,8 @@ #include "path_component.h" #include +#include "impeller/geometry/path.h" +#include "impeller/geometry/wangs_formula.h" namespace impeller { @@ -59,13 +61,6 @@ Point LinearPathComponent::Solve(Scalar time) const { }; } -void LinearPathComponent::AppendPolylinePoints( - std::vector& points) const { - if (points.size() == 0 || points.back() != p2) { - points.push_back(p2); - } -} - std::vector LinearPathComponent::Extrema() const { return {p1, p2}; } @@ -103,55 +98,18 @@ Scalar ApproximateParabolaIntegral(Scalar x) { return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); } -void QuadraticPathComponent::AppendPolylinePoints( - Scalar scale_factor, - std::vector& points) const { - ToLinearPathComponents(scale_factor, [&points](const Point& point) { - points.emplace_back(point); - }); -} - void QuadraticPathComponent::ToLinearPathComponents( Scalar scale_factor, - const PointProc& proc) const { - Scalar tolerance = kDefaultCurveTolerance / scale_factor; - Scalar sqrt_tolerance = sqrt(tolerance); - - Point d01 = cp - p1; - Point d12 = p2 - cp; - Point dd = d01 - d12; - Scalar cross = (p2 - p1).Cross(dd); - Scalar x0 = d01.Dot(dd) * 1 / cross; - Scalar x2 = d12.Dot(dd) * 1 / cross; - Scalar scale = std::abs(cross / (FastHypot(dd.x, dd.y) * (x2 - x0))); - - Scalar a0 = ApproximateParabolaIntegral(x0); - Scalar a2 = ApproximateParabolaIntegral(x2); - Scalar val = 0.f; - // if (std::isfinite(scale)) { - Scalar da = std::abs(a2 - a0); - Scalar sqrt_scale = sqrt(scale); - if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { - val = da * sqrt_scale; - } else { - // cusp case - Scalar xmin = sqrt_tolerance / sqrt_scale; - val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); - } - // } - Scalar u0 = ApproximateParabolaIntegral(a0); - Scalar u2 = ApproximateParabolaIntegral(a2); - Scalar uscale = 1.0f / (u2 - u0); - - Scalar line_count = std::max(1.0f, ceil(0.5f * val / sqrt_tolerance)); - Scalar step = 1.0f / line_count; + VertexWriter& writer) const { + Scalar line_count = quadratic(scale_factor, *this); for (size_t i = 1; i < line_count; i += 1) { - Scalar u = i * step; - Scalar a = a0 + (a2 - a0) * u; - Scalar t = (ApproximateParabolaIntegral(a) - u0) * uscale; - proc(Solve(t)); + Scalar time = i / line_count; + writer.Write({ + QuadraticSolve(time, p1.x, cp.x, p2.x), // x + QuadraticSolve(time, p1.y, cp.y, p2.y), // y + }); } - proc(p2); + writer.Write(p2); } std::vector QuadraticPathComponent::Extrema() const { @@ -193,13 +151,6 @@ Point CubicPathComponent::SolveDerivative(Scalar time) const { }; } -void CubicPathComponent::AppendPolylinePoints( - Scalar scale, - std::vector& points) const { - ToLinearPathComponents( - scale, [&points](const Point& point) { points.emplace_back(point); }); -} - inline QuadraticPathComponent CubicPathComponent::Lower() const { return QuadraticPathComponent(3.0 * (cp1 - p1), 3.0 * (cp2 - cp1), 3.0 * (p2 - cp2)); @@ -216,34 +167,16 @@ CubicPathComponent CubicPathComponent::Subsegment(Scalar t0, Scalar t1) const { } void CubicPathComponent::ToLinearPathComponents(Scalar scale, - const PointProc& proc) const { - constexpr Scalar accuracy = 0.1; - // The maximum error, as a vector from the cubic to the best approximating - // quadratic, is proportional to the third derivative, which is constant - // across the segment. Thus, the error scales down as the third power of - // the number of subdivisions. Our strategy then is to subdivide `t` evenly. - // - // This is an overestimate of the error because only the component - // perpendicular to the first derivative is important. But the simplicity is - // appealing. - - // This magic number is the square of 36 / sqrt(3). - // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html - auto max_hypot2 = 432.0 * accuracy * accuracy; - auto p1x2 = 3.0 * cp1 - p1; - auto p2x2 = 3.0 * cp2 - p2; - auto p = p2x2 - p1x2; - auto err = p.Dot(p); - auto quad_count = std::max(1., ceil(pow(err / max_hypot2, 1. / 6.0))); - for (size_t i = 0; i < quad_count; i++) { - auto t0 = i / quad_count; - auto t1 = (i + 1) / quad_count; - auto seg = Subsegment(t0, t1); - auto p1x2 = 3.0 * seg.cp1 - seg.p1; - auto p2x2 = 3.0 * seg.cp2 - seg.p2; - QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2) - .ToLinearPathComponents(scale, proc); + VertexWriter& writer) const { + Scalar line_count = cubic(scale, *this); + for (size_t i = 1; i < line_count; i += 1) { + Scalar time = i / line_count; + writer.Write({ + CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x + CubicSolve(time, p1.y, cp1.y, cp2.y, p2.y) // y + }); } + writer.Write(p2); } static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) { diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 74c1c5f0c59a0..9a20db1eac520 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -13,6 +13,7 @@ #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" +#include "impeller/geometry/vertex_writer.h" namespace impeller { @@ -42,8 +43,6 @@ struct LinearPathComponent { Point Solve(Scalar time) const; - void AppendPolylinePoints(std::vector& points) const; - std::vector Extrema() const; bool operator==(const LinearPathComponent& other) const { @@ -73,65 +72,9 @@ struct QuadraticPathComponent { Point SolveDerivative(Scalar time) const; - // Uses the algorithm described by Raph Levien in - // https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html. - // - // The algorithm has several benefits: - // - It does not require elevation to cubics for processing. - // - It generates fewer and more accurate points than recursive subdivision. - // - Each turn of the core iteration loop has no dependencies on other turns, - // making it trivially parallelizable. - // - // See also the implementation in kurbo: https://github.com/linebender/kurbo. - void AppendPolylinePoints(Scalar scale_factor, - std::vector& points) const; - using PointProc = std::function; - void ToLinearPathComponents(Scalar scale_factor, const PointProc& proc) const; - - template - void WriteLinearPathComponents(Scalar scale_factor, - VertexWriter& writer) const { - Scalar tolerance = kDefaultCurveTolerance / scale_factor; - Scalar sqrt_tolerance = sqrt(tolerance); - - Point d01 = cp - p1; - Point d12 = p2 - cp; - Point dd = d01 - d12; - Scalar cross = (p2 - p1).Cross(dd); - Scalar x0 = d01.Dot(dd) * 1.0f / cross; - Scalar x2 = d12.Dot(dd) * 1.0f / cross; - Scalar scale = std::abs(cross / (FastHypot(dd.x, dd.y) * (x2 - x0))); - - Scalar a0 = ApproximateParabolaIntegral(x0); - Scalar a2 = ApproximateParabolaIntegral(x2); - Scalar val = 0.f; - // if (std::isfinite(scale)) { - Scalar da = std::abs(a2 - a0); - Scalar sqrt_scale = sqrt(scale); - if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { - val = da * sqrt_scale; - } else { - // cusp case - Scalar xmin = sqrt_tolerance / sqrt_scale; - val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); - } - // } - Scalar u0 = ApproximateParabolaIntegral(a0); - Scalar u2 = ApproximateParabolaIntegral(a2); - Scalar uscale = 1.0f / (u2 - u0); - - Scalar line_count = std::max(1.0f, ceil(0.5f * val / sqrt_tolerance)); - Scalar step = 1.0f / line_count; - for (size_t i = 1; i < line_count; i += 1) { - Scalar u = i * step; - Scalar a = a0 + (a2 - a0) * u; - Scalar t = (ApproximateParabolaIntegral(a) - u0) * uscale; - writer.Write(Solve(t)); - } - writer.Write(p2); - } + void ToLinearPathComponents(Scalar scale_factor, VertexWriter& writer) const; std::vector Extrema() const; @@ -170,50 +113,11 @@ struct CubicPathComponent { Point SolveDerivative(Scalar time) const; - // This method approximates the cubic component with quadratics, and then - // generates a polyline from those quadratics. - // - // See the note on QuadraticPathComponent::AppendPolylinePoints for - // references. - void AppendPolylinePoints(Scalar scale, std::vector& points) const; - std::vector Extrema() const; using PointProc = std::function; - void ToLinearPathComponents(Scalar scale, const PointProc& proc) const; - - template - void WriteLinearPathComponents(Scalar scale_factor, - VertexWriter& writer) const { - constexpr Scalar accuracy = 0.1f; - // The maximum error, as a vector from the cubic to the best approximating - // quadratic, is proportional to the third derivative, which is constant - // across the segment. Thus, the error scales down as the third power of - // the number of subdivisions. Our strategy then is to subdivide `t` evenly. - // - // This is an overestimate of the error because only the component - // perpendicular to the first derivative is important. But the simplicity is - // appealing. - - // This magic number is the square of 36 / sqrt(3). - // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html - Scalar max_hypot2 = 432.0f * accuracy * accuracy; - Point p1x2 = 3.0f * cp1 - p1; - Point p2x2 = 3.0f * cp2 - p2; - Point p = p2x2 - p1x2; - Scalar err = p.Dot(p); - Scalar quad_count = std::max(1.0f, ceil(pow(err / max_hypot2, 1.0f / 6.0f))); - for (size_t i = 0; i < quad_count; i++) { - Scalar t0 = i / quad_count; - Scalar t1 = (i + 1) / quad_count; - CubicPathComponent seg = Subsegment(t0, t1); - Point p1x2 = 3.0 * seg.cp1 - seg.p1; - Point p2x2 = 3.0 * seg.cp2 - seg.p2; - QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2) - .WriteLinearPathComponents(scale_factor, writer); - } - } + void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const; CubicPathComponent Subsegment(Scalar t0, Scalar t1) const; diff --git a/impeller/geometry/path_unittests.cc b/impeller/geometry/path_unittests.cc index 6ffd69a1de56a..70389cc16d6bd 100644 --- a/impeller/geometry/path_unittests.cc +++ b/impeller/geometry/path_unittests.cc @@ -12,15 +12,15 @@ namespace impeller { namespace testing { -TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) { - CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40}); - std::vector polyline; - component.AppendPolylinePoints(1.0f, polyline); - ASSERT_NE(polyline.front().x, 10); - ASSERT_NE(polyline.front().y, 10); - ASSERT_EQ(polyline.back().x, 40); - ASSERT_EQ(polyline.back().y, 40); -} +// TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) { +// CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40}); +// std::vector polyline; +// component.AppendPolylinePoints(1.0f, polyline); +// ASSERT_NE(polyline.front().x, 10); +// ASSERT_NE(polyline.front().y, 10); +// ASSERT_EQ(polyline.back().x, 40); +// ASSERT_EQ(polyline.back().y, 40); +// } TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) { PathBuilder builder; diff --git a/impeller/geometry/vector.h b/impeller/geometry/vector.h index d55136d5cef80..cdd968d2f7681 100644 --- a/impeller/geometry/vector.h +++ b/impeller/geometry/vector.h @@ -252,6 +252,9 @@ struct Vector4 { constexpr Vector4(const Point& p) : x(p.x), y(p.y) {} + constexpr Vector4(const Point& p1, const Point& p2) + : x(p1.x), y(p1.y), z(p2.x), w(p2.y) {} + constexpr Vector4(std::array values) : x(values[0]), y(values[1]), z(values[2]), w(values[3]) {} diff --git a/impeller/geometry/vertex_writer.h b/impeller/geometry/vertex_writer.h new file mode 100644 index 0000000000000..e68ac4778e710 --- /dev/null +++ b/impeller/geometry/vertex_writer.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef FLUTTER_IMPELLER_GEOMETRY_VERTEX_WRITER_H_ +#define FLUTTER_IMPELLER_GEOMETRY_VERTEX_WRITER_H_ + +#include + +#include "impeller/geometry/point.h" + +namespace impeller { + +class VertexWriter { + public: + explicit VertexWriter(std::vector& points, + std::vector& indices) + : points_(points), indices_(indices) {} + + ~VertexWriter() = default; + + void EndContour() { + if (points_.size() == 0u || contour_start_ == points_.size() - 1) { + // Empty or first contour. + return; + } + + auto start = contour_start_; + auto end = points_.size() - 1; + // 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 (points_[end] == points_[start]) { + end--; + } + + if (contour_start_ > 0) { + // Triangle strip break. + indices_.emplace_back(indices_.back()); + indices_.emplace_back(start); + indices_.emplace_back(start); + + // If the contour has an odd number of points, insert an extra point when + // bridging to the next contour to preserve the correct triangle winding + // order. + if (previous_contour_odd_points_) { + indices_.emplace_back(start); + } + } else { + indices_.emplace_back(start); + } + + size_t a = start + 1; + size_t b = end; + while (a < b) { + indices_.emplace_back(a); + indices_.emplace_back(b); + a++; + b--; + } + if (a == b) { + indices_.emplace_back(a); + previous_contour_odd_points_ = false; + } else { + previous_contour_odd_points_ = true; + } + contour_start_ = points_.size(); + } + + void Write(Point point) { points_.emplace_back(point); } + + private: + bool previous_contour_odd_points_ = false; + size_t contour_start_ = 0u; + std::vector& points_; + std::vector& indices_; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_GEOMETRY_VERTEX_WRITER_H_ diff --git a/impeller/geometry/wangs_formula.h b/impeller/geometry/wangs_formula.h new file mode 100644 index 0000000000000..203d1cdec1999 --- /dev/null +++ b/impeller/geometry/wangs_formula.h @@ -0,0 +1,76 @@ +// 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. + +#ifndef FLUTTER_IMPELLER_GEOMETRY_WANGS_FORMULA_H_ +#define FLUTTER_IMPELLER_GEOMETRY_WANGS_FORMULA_H_ + +#include "impeller/geometry/path_component.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/scalar.h" + +// Skia GPU Ports + +// Wang's formula gives the minimum number of evenly spaced (in the parametric +// sense) line segments that a bezier curve must be chopped into in order to +// guarantee all lines stay within a distance of "1/precision" pixels from the +// true curve. Its definition for a bezier curve of degree "n" is as follows: +// +// maxLength = max([length(p[i+2] - 2p[i+1] + p[i]) for (0 <= i <= n-2)]) +// numParametricSegments = sqrt(maxLength * precision * n*(n - 1)/8) +// +// (Goldman, Ron. (2003). 5.6.3 Wang's Formula. "Pyramid Algorithms: A Dynamic +// Programming Approach to Curves and Surfaces for Geometric Modeling". Morgan +// Kaufmann Publishers.) +namespace impeller { + +// Don't allow linearized segments to be off by more than 1/4th of a pixel from +// the true curve. +constexpr static Scalar kPrecision = 4; + +inline static float length(Point n) { + Point nn = n * n; + return std::sqrt(nn.x + nn.y); +} + +inline static Point Max(Point a, Point b) { + return Point{ + a.x > b.x ? a.x : b.x, // + a.y > b.y ? a.y : b.y // + }; +} + +inline static float cubic(float intolerance, + Point p0, + Point p1, + Point p2, + Point p3) { + float k = intolerance * .75f * kPrecision; + Point a = (p0 - p1 * 2 + p2).Abs(); + Point b = (p1 - p2 * 2 + p3).Abs(); + return std::sqrt(k * length(Max(a, b))); +} + +inline static float quadratic(float intolerance, Point p0, Point p1, Point p2) { + float k = intolerance * .25f * kPrecision; + return std::sqrt(k * length(p0 - p1 * 2 + p2)); +} + +// Returns the minimum number of evenly spaced (in the parametric sense) line +// segments that the quadratic must be chopped into in order to guarantee all +// lines stay within a distance of "1/intolerance" pixels from the true curve. +inline static float quadratic(float intolerance, + const QuadraticPathComponent& quad) { + return quadratic(intolerance, quad.p1, quad.cp, quad.p2); +} + +// Returns the minimum number of evenly spaced (in the parametric sense) line +// segments that the cubic must be chopped into in order to guarantee all lines +// stay within a distance of "1/intolerance" pixels from the true curve. +inline static float cubic(float intolerance, const CubicPathComponent& cub) { + return cubic(intolerance, cub.p1, cub.cp1, cub.cp2, cub.p2); +} + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_GEOMETRY_WANGS_FORMULA_H_ diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc index 84374190a249e..bd3367795d222 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc @@ -287,6 +287,7 @@ std::unique_ptr PipelineVK::Create( vk::PipelineInputAssemblyStateCreateInfo input_assembly; const auto topology = ToVKPrimitiveTopology(desc.GetPrimitiveType()); input_assembly.setTopology(topology); + input_assembly.setPrimitiveRestartEnable(true); pipeline_info.setPInputAssemblyState(&input_assembly); //---------------------------------------------------------------------------- diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index c205353d06641..094de3a2264e2 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -702,77 +702,6 @@ void Tessellator::GenerateFilledRoundRect( } } -template -class VertexWriter { - public: - explicit VertexWriter(std::vector& points, - std::vector& indices) - : points_(points), indices_(indices) {} - - ~VertexWriter() = default; - - void EndContour() { - if (points_.size() == 0u || contour_start_ == points_.size() - 1) { - // Empty or first contour. - return; - } - - auto start = contour_start_; - auto end = points_.size() - 1; - // 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 (points_[end] == points_[start]) { - end--; - } - - if (contour_start_ > 0) { - // Triangle strip break. - indices_.emplace_back(indices_.back()); - indices_.emplace_back(start); - indices_.emplace_back(start); - - // If the contour has an odd number of points, insert an extra point when - // bridging to the next contour to preserve the correct triangle winding - // order. - if (previous_contour_odd_points_) { - indices_.emplace_back(start); - } - } else { - indices_.emplace_back(start); - } - - size_t a = start + 1; - size_t b = end; - while (a < b) { - indices_.emplace_back(a); - indices_.emplace_back(b); - a++; - b--; - } - if (a == b) { - indices_.emplace_back(a); - previous_contour_odd_points_ = false; - } else { - previous_contour_odd_points_ = true; - } - contour_start_ = points_.size(); - } - - void Write(Point point) { - points_.emplace_back(point); - if constexpr (write_uvs) { - points_.emplace_back(Point{0, 0}); - } - } - - private: - bool previous_contour_odd_points_ = false; - size_t contour_start_ = 0u; - std::vector& points_; - std::vector& indices_; -}; - VertexBuffer Tessellator::TessellateConvex2(const Path& path, HostBuffer& host_bufer, Scalar tolerance, @@ -780,22 +709,13 @@ VertexBuffer Tessellator::TessellateConvex2(const Path& path, index_buffer_->clear(); point_buffer_->clear(); - size_t alignment_size = 0; - if (create_uvs) { - alignment_size = alignof(Vector4); - VertexWriter writer(*point_buffer_, *index_buffer_); - path.WritePolyline(writer, tolerance); - writer.EndContour(); - } else { - alignment_size = alignof(Point); - VertexWriter writer(*point_buffer_, *index_buffer_); - path.WritePolyline(writer, tolerance); - writer.EndContour(); - } + VertexWriter writer(*point_buffer_, *index_buffer_); + path.WritePolyline(writer, tolerance); + writer.EndContour(); auto vertex_buffer = host_bufer.Emplace(point_buffer_->data(), - sizeof(Point) * point_buffer_->size(), alignment_size); + sizeof(Point) * point_buffer_->size(), sizeof(Point)); auto index_buffer = host_bufer.Emplace( index_buffer_->data(), sizeof(uint16_t) * index_buffer_->size(),