diff --git a/impeller/entity/geometry/fill_path_geometry.cc b/impeller/entity/geometry/fill_path_geometry.cc index 1b3dc51813266..de2709894218a 100644 --- a/impeller/entity/geometry/fill_path_geometry.cc +++ b/impeller/entity/geometry/fill_path_geometry.cc @@ -38,17 +38,10 @@ GeometryResult FillPathGeometry::GetPositionBuffer( VertexBuffer vertex_buffer; - 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/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 ba56a4101c1bd..5e18905ebfca8 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -11,6 +11,7 @@ #include #include "impeller/geometry/path_component.h" +#include "impeller/geometry/vertex_writer.h" namespace impeller { @@ -137,6 +138,11 @@ class Path { bool IsEmpty() const; + // 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 55a4565708417..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}; } @@ -98,60 +93,23 @@ 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))); } -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 { - 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; + VertexWriter& writer) const { + Scalar line_count = quadratic(scale_factor, *this); 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)); + 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 58aa1f9f83530..9a20db1eac520 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -13,9 +13,16 @@ #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" +#include "impeller/geometry/vertex_writer.h" 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. @@ -36,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 { @@ -67,22 +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; + void ToLinearPathComponents(Scalar scale_factor, VertexWriter& writer) const; std::vector Extrema() const; @@ -121,18 +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; + 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/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 58f6fa923f96a..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 = 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..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/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 afbf01da90f83..094de3a2264e2 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -3,7 +3,12 @@ // found in the LICENSE file. #include "impeller/tessellator/tessellator.h" +#include +#include +#include "impeller/core/host_buffer.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/path.h" #include "third_party/libtess2/Include/tesselator.h" namespace impeller { @@ -33,8 +38,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. @@ -695,4 +702,31 @@ void Tessellator::GenerateFilledRoundRect( } } +VertexBuffer Tessellator::TessellateConvex2(const Path& path, + HostBuffer& host_bufer, + Scalar tolerance, + bool create_uvs) { + index_buffer_->clear(); + point_buffer_->clear(); + + 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(), sizeof(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..8666ec865a750 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,11 @@ class Tessellator { /// std::vector TessellateConvex(const Path& path, 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 /// a time. @@ -299,6 +307,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 diff --git a/impeller/tessellator/tessellator_unittests.cc b/impeller/tessellator/tessellator_unittests.cc index 723f42f629639..c2fee7cabb7e6 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"