[Impeller] use primitive restart for faster tessellation: write directly into host buffer. (#56173)

Using primitive restart we can avoid tracking even odd or inserting degenerate triangles. Instead a special index value `0xFFFF` is used to signal a break. This can be combined with triangle fan on vulkan for a dramatically simpler tessellation.

Additionally, switches to a two pass system where we first estimate the storage required by the path so tha the host buffer can be written to directly.
diff --git a/impeller/display_list/aiks_dl_blend_unittests.cc b/impeller/display_list/aiks_dl_blend_unittests.cc
index f4691f6..37f37e7 100644
--- a/impeller/display_list/aiks_dl_blend_unittests.cc
+++ b/impeller/display_list/aiks_dl_blend_unittests.cc
@@ -338,6 +338,7 @@
   FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
   FLT_FORWARD(mock_capabilities, old_capabilities,
               SupportsDecalSamplerAddressMode);
+  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsPrimitiveRestart);
   ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
 
   bool has_color_filter = true;
diff --git a/impeller/display_list/aiks_dl_blur_unittests.cc b/impeller/display_list/aiks_dl_blur_unittests.cc
index 96f852b..d8e9f58 100644
--- a/impeller/display_list/aiks_dl_blur_unittests.cc
+++ b/impeller/display_list/aiks_dl_blur_unittests.cc
@@ -1271,6 +1271,7 @@
               SupportsTextureToTextureBlits);
   FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
   FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
+  FLT_FORWARD(mock_capabilities, old_capabilities, SupportsPrimitiveRestart);
   ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
 
   auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
diff --git a/impeller/entity/geometry/fill_path_geometry.cc b/impeller/entity/geometry/fill_path_geometry.cc
index 6ed0ec3..72937bf 100644
--- a/impeller/entity/geometry/fill_path_geometry.cc
+++ b/impeller/entity/geometry/fill_path_geometry.cc
@@ -38,12 +38,20 @@
     };
   }
 
+  bool supports_primitive_restart =
+      renderer.GetDeviceCapabilities().SupportsPrimitiveRestart();
+  bool supports_triangle_fan =
+      renderer.GetDeviceCapabilities().SupportsTriangleFan() &&
+      supports_primitive_restart;
   VertexBuffer vertex_buffer = renderer.GetTessellator().TessellateConvex(
-      path_, host_buffer, entity.GetTransform().GetMaxBasisLengthXY());
+      path_, host_buffer, entity.GetTransform().GetMaxBasisLengthXY(),
+      /*supports_primitive_restart=*/supports_primitive_restart,
+      /*supports_triangle_fan=*/supports_triangle_fan);
 
   return GeometryResult{
-      .type = PrimitiveType::kTriangleStrip,
-      .vertex_buffer = vertex_buffer,
+      .type = supports_triangle_fan ? PrimitiveType::kTriangleFan
+                                    : PrimitiveType::kTriangleStrip,
+      .vertex_buffer = std::move(vertex_buffer),
       .transform = entity.GetShaderTransform(pass),
       .mode = GetResultMode(),
   };
diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc
index 7979f83..7762ada 100644
--- a/impeller/geometry/path.cc
+++ b/impeller/geometry/path.cc
@@ -5,6 +5,7 @@
 #include "impeller/geometry/path.h"
 
 #include <optional>
+#include <utility>
 
 #include "flutter/fml/logging.h"
 #include "impeller/geometry/path_component.h"
@@ -58,6 +59,45 @@
           data_->components[0] == ComponentType::kContour);
 }
 
+/// Determine required storage for points and indices.
+std::pair<size_t, size_t> Path::CountStorage(Scalar scale) const {
+  size_t points = 0;
+  size_t contours = 0;
+
+  auto& path_components = data_->components;
+  auto& path_points = data_->points;
+
+  size_t storage_offset = 0u;
+  for (size_t component_i = 0; component_i < path_components.size();
+       component_i++) {
+    const auto& path_component = path_components[component_i];
+    switch (path_component) {
+      case ComponentType::kLinear: {
+        points += 2;
+        break;
+      }
+      case ComponentType::kQuadratic: {
+        const QuadraticPathComponent* quad =
+            reinterpret_cast<const QuadraticPathComponent*>(
+                &path_points[storage_offset]);
+        points += quad->CountLinearPathComponents(scale);
+        break;
+      }
+      case ComponentType::kCubic: {
+        const CubicPathComponent* cubic =
+            reinterpret_cast<const CubicPathComponent*>(
+                &path_points[storage_offset]);
+        points += cubic->CountLinearPathComponents(scale);
+        break;
+      }
+      case Path::ComponentType::kContour:
+        contours++;
+    }
+    storage_offset += VerbToOffset(path_component);
+  }
+  return std::make_pair(points, contours);
+}
+
 void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
   auto& path_components = data_->components;
   auto& path_points = data_->points;
diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h
index 27a345e..edad3c9 100644
--- a/impeller/geometry/path.h
+++ b/impeller/geometry/path.h
@@ -193,6 +193,9 @@
   /// lines.
   void WritePolyline(Scalar scale, VertexWriter& writer) const;
 
+  /// Determine required storage for points and number of contours.
+  std::pair<size_t, size_t> CountStorage(Scalar scale) const;
+
  private:
   friend class PathBuilder;
 
diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc
index d990f37..c6ba536 100644
--- a/impeller/geometry/path_component.cc
+++ b/impeller/geometry/path_component.cc
@@ -6,15 +6,84 @@
 
 #include <cmath>
 
+#include "impeller/geometry/scalar.h"
 #include "impeller/geometry/wangs_formula.h"
 
 namespace impeller {
 
-VertexWriter::VertexWriter(std::vector<Point>& points,
-                           std::vector<uint16_t>& indices)
+/////////// FanVertexWriter ///////////
+
+FanVertexWriter::FanVertexWriter(Point* point_buffer, uint16_t* index_buffer)
+    : point_buffer_(point_buffer), index_buffer_(index_buffer) {}
+
+FanVertexWriter::~FanVertexWriter() = default;
+
+size_t FanVertexWriter::GetIndexCount() const {
+  return index_count_;
+}
+
+void FanVertexWriter::EndContour() {
+  if (count_ == 0) {
+    return;
+  }
+  index_buffer_[index_count_++] = 0xFFFF;
+}
+
+void FanVertexWriter::Write(Point point) {
+  index_buffer_[index_count_++] = count_;
+  point_buffer_[count_++] = point;
+}
+
+/////////// StripVertexWriter ///////////
+
+StripVertexWriter::StripVertexWriter(Point* point_buffer,
+                                     uint16_t* index_buffer)
+    : point_buffer_(point_buffer), index_buffer_(index_buffer) {}
+
+StripVertexWriter::~StripVertexWriter() = default;
+
+size_t StripVertexWriter::GetIndexCount() const {
+  return index_count_;
+}
+
+void StripVertexWriter::EndContour() {
+  if (count_ == 0u || contour_start_ == count_ - 1) {
+    // Empty or first contour.
+    return;
+  }
+
+  size_t start = contour_start_;
+  size_t end = count_ - 1;
+
+  index_buffer_[index_count_++] = start;
+
+  size_t a = start + 1;
+  size_t b = end;
+  while (a < b) {
+    index_buffer_[index_count_++] = a;
+    index_buffer_[index_count_++] = b;
+    a++;
+    b--;
+  }
+  if (a == b) {
+    index_buffer_[index_count_++] = a;
+  }
+
+  contour_start_ = count_;
+  index_buffer_[index_count_++] = 0xFFFF;
+}
+
+void StripVertexWriter::Write(Point point) {
+  point_buffer_[count_++] = point;
+}
+
+/////////// GLESVertexWriter ///////////
+
+GLESVertexWriter::GLESVertexWriter(std::vector<Point>& points,
+                                   std::vector<uint16_t>& indices)
     : points_(points), indices_(indices) {}
 
-void VertexWriter::EndContour() {
+void GLESVertexWriter::EndContour() {
   if (points_.size() == 0u || contour_start_ == points_.size() - 1) {
     // Empty or first contour.
     return;
@@ -64,7 +133,7 @@
   contour_start_ = points_.size();
 }
 
-void VertexWriter::Write(Point point) {
+void GLESVertexWriter::Write(Point point) {
   points_.push_back(point);
 }
 
@@ -187,6 +256,10 @@
   proc(p2);
 }
 
+size_t QuadraticPathComponent::CountLinearPathComponents(Scalar scale) const {
+  return std::ceilf(ComputeQuadradicSubdivisions(scale, *this)) + 2;
+}
+
 std::vector<Point> QuadraticPathComponent::Extrema() const {
   CubicPathComponent elevated(*this);
   return elevated.Extrema();
@@ -242,6 +315,10 @@
   writer.Write(p2);
 }
 
+size_t CubicPathComponent::CountLinearPathComponents(Scalar scale) const {
+  return std::ceilf(ComputeCubicSubdivisions(scale, *this)) + 2;
+}
+
 inline QuadraticPathComponent CubicPathComponent::Lower() const {
   return QuadraticPathComponent(3.0 * (cp1 - p1), 3.0 * (cp2 - cp1),
                                 3.0 * (p2 - cp2));
diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h
index b54735d..156b3de 100644
--- a/impeller/geometry/path_component.h
+++ b/impeller/geometry/path_component.h
@@ -19,14 +19,65 @@
 ///        strip.
 class VertexWriter {
  public:
-  explicit VertexWriter(std::vector<Point>& points,
-                        std::vector<uint16_t>& indices);
+  virtual void EndContour() = 0;
 
-  ~VertexWriter() = default;
+  virtual void Write(Point point) = 0;
+};
 
-  void EndContour();
+/// @brief A vertex writer that generates a triangle fan and requires primitive
+/// restart.
+class FanVertexWriter : public VertexWriter {
+ public:
+  explicit FanVertexWriter(Point* point_buffer, uint16_t* index_buffer);
 
-  void Write(Point point);
+  ~FanVertexWriter();
+
+  size_t GetIndexCount() const;
+
+  void EndContour() override;
+
+  void Write(Point point) override;
+
+ private:
+  size_t count_ = 0;
+  size_t index_count_ = 0;
+  Point* point_buffer_ = nullptr;
+  uint16_t* index_buffer_ = nullptr;
+};
+
+/// @brief A vertex writer that generates a triangle strip and requires
+///        primitive restart.
+class StripVertexWriter : public VertexWriter {
+ public:
+  explicit StripVertexWriter(Point* point_buffer, uint16_t* index_buffer);
+
+  ~StripVertexWriter();
+
+  size_t GetIndexCount() const;
+
+  void EndContour() override;
+
+  void Write(Point point) override;
+
+ private:
+  size_t count_ = 0;
+  size_t index_count_ = 0;
+  size_t contour_start_ = 0;
+  Point* point_buffer_ = nullptr;
+  uint16_t* index_buffer_ = nullptr;
+};
+
+/// @brief A vertex writer that has no hardware requirements.
+class GLESVertexWriter : public VertexWriter {
+ public:
+  explicit GLESVertexWriter(std::vector<Point>& points,
+                            std::vector<uint16_t>& indices);
+
+  ~GLESVertexWriter() = default;
+
+  void EndContour() override;
+
+  void Write(Point point) override;
 
  private:
   bool previous_contour_odd_points_ = false;
@@ -85,6 +136,8 @@
 
   void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
 
+  size_t CountLinearPathComponents(Scalar scale) const;
+
   std::vector<Point> Extrema() const;
 
   bool operator==(const QuadraticPathComponent& other) const {
@@ -132,6 +185,8 @@
 
   void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
 
+  size_t CountLinearPathComponents(Scalar scale) const;
+
   CubicPathComponent Subsegment(Scalar t0, Scalar t1) const;
 
   bool operator==(const CubicPathComponent& other) const {
diff --git a/impeller/geometry/path_unittests.cc b/impeller/geometry/path_unittests.cc
index 2f31c4b..51c2975 100644
--- a/impeller/geometry/path_unittests.cc
+++ b/impeller/geometry/path_unittests.cc
@@ -9,6 +9,7 @@
 #include "impeller/geometry/path.h"
 #include "impeller/geometry/path_builder.h"
 #include "impeller/geometry/path_component.h"
+#include "impeller/geometry/round_rect.h"
 
 namespace impeller {
 namespace testing {
@@ -558,6 +559,128 @@
   }
 }
 
+TEST(PathTest, FanTessellation) {
+  Path path = PathBuilder{}
+                  .AddRoundRect(RoundRect::MakeRectRadius(
+                      Rect::MakeLTRB(0, 0, 100, 100), 10))
+                  .TakePath();
+  auto [points, contours] = path.CountStorage(1.0);
+
+  std::vector<Point> point_storage(points);
+  std::vector<uint16_t> index_storage(points + (contours - 1));
+
+  FanVertexWriter writer(point_storage.data(), index_storage.data());
+  path.WritePolyline(1.0, writer);
+
+  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
+  EXPECT_EQ(point_storage[0], Point(10, 0));
+}
+
+// Filled Paths without an explicit close should still be closed
+TEST(PathTest, FanTessellationUnclosedPath) {
+  // Create a rectangle that lacks an explicit close.
+  Path path = PathBuilder{}
+                  .LineTo({100, 0})
+                  .LineTo({100, 100})
+                  .LineTo({0, 100})
+                  .TakePath();
+
+  std::vector<Point> expected = {{0, 0},   {100, 0}, {100, 100},
+                                 {0, 100}, {0, 0},   {0, 0}};
+  std::vector<uint16_t> expected_indices = {0, 1, 2, 3, 0xFFFF, 0};
+
+  auto [points, contours] = path.CountStorage(1.0);
+
+  std::vector<Point> point_storage(points);
+  std::vector<uint16_t> index_storage(points + (contours - 1));
+
+  FanVertexWriter writer(point_storage.data(), index_storage.data());
+  path.WritePolyline(1.0, writer);
+
+  EXPECT_LE(index_storage, expected_indices);
+  EXPECT_EQ(point_storage, expected);
+}
+
+// Filled Paths without an explicit close should still be closed
+TEST(PathTest, StripTessellationUnclosedPath) {
+  // Create a rectangle that lacks an explicit close.
+  Path path = PathBuilder{}
+                  .LineTo({100, 0})
+                  .LineTo({100, 100})
+                  .LineTo({0, 100})
+                  .TakePath();
+
+  std::vector<Point> expected = {{0, 0},   {100, 0}, {100, 100},
+                                 {0, 100}, {0, 0},   {0, 0}};
+  std::vector<uint16_t> expected_indices = {0, 1, 3, 2, 0xFFFF, 0};
+
+  auto [points, contours] = path.CountStorage(1.0);
+
+  std::vector<Point> point_storage(points);
+  std::vector<uint16_t> index_storage(points + (contours - 1));
+
+  StripVertexWriter writer(point_storage.data(), index_storage.data());
+  path.WritePolyline(1.0, writer);
+
+  EXPECT_LE(index_storage, expected_indices);
+  EXPECT_EQ(point_storage, expected);
+}
+
+TEST(PathTest, FanTessellationMultiContour) {
+  PathBuilder builder{};
+  for (auto i = 0; i < 10; i++) {
+    builder.AddRoundRect(
+        RoundRect::MakeRectRadius(Rect::MakeLTRB(0 + i, 0 + i, 100, 100), 10));
+  }
+  auto path = builder.TakePath();
+  auto [points, contours] = path.CountStorage(1.0);
+
+  std::vector<Point> point_storage(points);
+  std::vector<uint16_t> index_storage(points + (contours - 1));
+
+  FanVertexWriter writer(point_storage.data(), index_storage.data());
+  path.WritePolyline(1.0, writer);
+
+  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
+  EXPECT_EQ(point_storage[0], Point(10, 0));
+}
+
+TEST(PathTest, StripTessellation) {
+  Path path = PathBuilder{}
+                  .AddRoundRect(RoundRect::MakeRectRadius(
+                      Rect::MakeLTRB(0, 0, 100, 100), 10))
+                  .TakePath();
+  auto [points, contours] = path.CountStorage(1.0);
+
+  std::vector<Point> point_storage(points);
+  std::vector<uint16_t> index_storage(points + (contours - 1));
+
+  StripVertexWriter writer(point_storage.data(), index_storage.data());
+  path.WritePolyline(1.0, writer);
+
+  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
+  EXPECT_EQ(point_storage[0], Point(10, 0));
+}
+
+TEST(PathTest, StripTessellationMultiContour) {
+  PathBuilder builder{};
+  for (auto i = 0; i < 10; i++) {
+    builder.AddRoundRect(
+        RoundRect::MakeRectRadius(Rect::MakeLTRB(0 + i, 0 + i, 100, 100), 10));
+  }
+  auto path = builder.TakePath();
+  auto [points, contours] = path.CountStorage(1.0);
+
+  std::vector<Point> point_storage(points);
+  std::vector<uint16_t> index_storage(points + (contours - 1));
+
+  StripVertexWriter writer(point_storage.data(), index_storage.data());
+  path.WritePolyline(1.0, writer);
+
+  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
+  EXPECT_EQ(point_storage[0], Point(10, 0));
+}
+
 TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
   auto test_isolation =
       [](const std::function<void(PathBuilder & builder)>& mutator,
diff --git a/impeller/renderer/backend/gles/capabilities_gles.cc b/impeller/renderer/backend/gles/capabilities_gles.cc
index 5f078c2..66b6aff 100644
--- a/impeller/renderer/backend/gles/capabilities_gles.cc
+++ b/impeller/renderer/backend/gles/capabilities_gles.cc
@@ -201,6 +201,10 @@
   return is_angle_;
 }
 
+bool CapabilitiesGLES::SupportsPrimitiveRestart() const {
+  return false;
+}
+
 PixelFormat CapabilitiesGLES::GetDefaultGlyphAtlasFormat() const {
   return default_glyph_atlas_format_;
 }
diff --git a/impeller/renderer/backend/gles/capabilities_gles.h b/impeller/renderer/backend/gles/capabilities_gles.h
index bcc911c..f666992 100644
--- a/impeller/renderer/backend/gles/capabilities_gles.h
+++ b/impeller/renderer/backend/gles/capabilities_gles.h
@@ -111,6 +111,9 @@
   bool SupportsTriangleFan() const override;
 
   // |Capabilities|
+  bool SupportsPrimitiveRestart() const override;
+
+  // |Capabilities|
   PixelFormat GetDefaultColorFormat() const override;
 
   // |Capabilities|
diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc
index eb77f78..80d2003 100644
--- a/impeller/renderer/backend/vulkan/capabilities_vk.cc
+++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc
@@ -481,6 +481,10 @@
   return false;
 }
 
+bool CapabilitiesVK::SupportsPrimitiveRestart() const {
+  return true;
+}
+
 void CapabilitiesVK::SetOffscreenFormat(PixelFormat pixel_format) const {
   default_color_format_ = pixel_format;
 }
diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h
index 671e8fc..b1ae6c3 100644
--- a/impeller/renderer/backend/vulkan/capabilities_vk.h
+++ b/impeller/renderer/backend/vulkan/capabilities_vk.h
@@ -245,6 +245,9 @@
   bool SupportsTriangleFan() const override;
 
   // |Capabilities|
+  bool SupportsPrimitiveRestart() const override;
+
+  // |Capabilities|
   PixelFormat GetDefaultColorFormat() const override;
 
   // |Capabilities|
diff --git a/impeller/renderer/backend/vulkan/formats_vk.h b/impeller/renderer/backend/vulkan/formats_vk.h
index 4b1d57f..cb9a5fa 100644
--- a/impeller/renderer/backend/vulkan/formats_vk.h
+++ b/impeller/renderer/backend/vulkan/formats_vk.h
@@ -8,6 +8,7 @@
 #include <cstdint>
 #include <ostream>
 
+#include "fml/logging.h"
 #include "impeller/base/validation.h"
 #include "impeller/core/formats.h"
 #include "impeller/core/shader_types.h"
@@ -383,6 +384,21 @@
   FML_UNREACHABLE();
 }
 
+constexpr bool PrimitiveTopologySupportsPrimitiveRestart(
+    PrimitiveType primitive) {
+  switch (primitive) {
+    case PrimitiveType::kTriangleStrip:
+    case PrimitiveType::kLine:
+    case PrimitiveType::kPoint:
+    case PrimitiveType::kTriangleFan:
+      return true;
+    case PrimitiveType::kTriangle:
+    case PrimitiveType::kLineStrip:
+      return false;
+  }
+  FML_UNREACHABLE();
+}
+
 constexpr vk::PrimitiveTopology ToVKPrimitiveTopology(PrimitiveType primitive) {
   switch (primitive) {
     case PrimitiveType::kTriangle:
diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc
index fba6034..4ad8384 100644
--- a/impeller/renderer/backend/vulkan/pipeline_vk.cc
+++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc
@@ -361,6 +361,8 @@
   vk::PipelineInputAssemblyStateCreateInfo input_assembly;
   const auto topology = ToVKPrimitiveTopology(desc.GetPrimitiveType());
   input_assembly.setTopology(topology);
+  input_assembly.setPrimitiveRestartEnable(
+      PrimitiveTopologySupportsPrimitiveRestart(desc.GetPrimitiveType()));
   pipeline_info.setPInputAssemblyState(&input_assembly);
 
   //----------------------------------------------------------------------------
diff --git a/impeller/renderer/capabilities.cc b/impeller/renderer/capabilities.cc
index 338e5ff..5637d84 100644
--- a/impeller/renderer/capabilities.cc
+++ b/impeller/renderer/capabilities.cc
@@ -88,6 +88,9 @@
     return default_maximum_render_pass_attachment_size_;
   }
 
+  // |Capabilities|
+  bool SupportsPrimitiveRestart() const override { return true; }
+
  private:
   StandardCapabilities(bool supports_offscreen_msaa,
                        bool supports_ssbo,
diff --git a/impeller/renderer/capabilities.h b/impeller/renderer/capabilities.h
index 2129c9e..1a4d201 100644
--- a/impeller/renderer/capabilities.h
+++ b/impeller/renderer/capabilities.h
@@ -86,6 +86,9 @@
   /// @brief Whether the primitive type TriangleFan is supported by the backend.
   virtual bool SupportsTriangleFan() const = 0;
 
+  /// @brief Whether primitive restart is supported.
+  virtual bool SupportsPrimitiveRestart() const = 0;
+
   /// @brief  Returns a supported `PixelFormat` for textures that store
   ///         4-channel colors (red/green/blue/alpha).
   virtual PixelFormat GetDefaultColorFormat() const = 0;
diff --git a/impeller/renderer/testing/mocks.h b/impeller/renderer/testing/mocks.h
index fd41721..b389668 100644
--- a/impeller/renderer/testing/mocks.h
+++ b/impeller/renderer/testing/mocks.h
@@ -218,6 +218,7 @@
   MOCK_METHOD(bool, SupportsDecalSamplerAddressMode, (), (const, override));
   MOCK_METHOD(bool, SupportsDeviceTransientTextures, (), (const, override));
   MOCK_METHOD(bool, SupportsTriangleFan, (), (const override));
+  MOCK_METHOD(bool, SupportsPrimitiveRestart, (), (const override));
   MOCK_METHOD(PixelFormat, GetDefaultColorFormat, (), (const, override));
   MOCK_METHOD(PixelFormat, GetDefaultStencilFormat, (), (const, override));
   MOCK_METHOD(PixelFormat, GetDefaultDepthStencilFormat, (), (const, override));
diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc
index a36889b..bf07e45 100644
--- a/impeller/tessellator/tessellator.cc
+++ b/impeller/tessellator/tessellator.cc
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 
 #include "impeller/tessellator/tessellator.h"
+#include <cstdint>
+
+#include "impeller/core/device_buffer.h"
+#include "impeller/geometry/path_component.h"
 
 namespace impeller {
 
@@ -29,7 +33,49 @@
 
 VertexBuffer Tessellator::TessellateConvex(const Path& path,
                                            HostBuffer& host_buffer,
-                                           Scalar tolerance) {
+                                           Scalar tolerance,
+                                           bool supports_primitive_restart,
+                                           bool supports_triangle_fan) {
+  if (supports_primitive_restart) {
+    // Primitive Restart.
+    const auto [point_count, contour_count] = path.CountStorage(tolerance);
+    BufferView point_buffer = host_buffer.Emplace(
+        nullptr, sizeof(Point) * point_count, alignof(Point));
+    BufferView index_buffer = host_buffer.Emplace(
+        nullptr, sizeof(uint16_t) * (point_count + contour_count),
+        alignof(uint16_t));
+
+    if (supports_triangle_fan) {
+      FanVertexWriter writer(
+          reinterpret_cast<Point*>(point_buffer.buffer->OnGetContents() +
+                                   point_buffer.range.offset),
+          reinterpret_cast<uint16_t*>(index_buffer.buffer->OnGetContents() +
+                                      index_buffer.range.offset));
+      path.WritePolyline(tolerance, writer);
+
+      return VertexBuffer{
+          .vertex_buffer = std::move(point_buffer),
+          .index_buffer = std::move(index_buffer),
+          .vertex_count = writer.GetIndexCount(),
+          .index_type = IndexType::k16bit,
+      };
+    } else {
+      StripVertexWriter writer(
+          reinterpret_cast<Point*>(point_buffer.buffer->OnGetContents() +
+                                   point_buffer.range.offset),
+          reinterpret_cast<uint16_t*>(index_buffer.buffer->OnGetContents() +
+                                      index_buffer.range.offset));
+      path.WritePolyline(tolerance, writer);
+
+      return VertexBuffer{
+          .vertex_buffer = std::move(point_buffer),
+          .index_buffer = std::move(index_buffer),
+          .vertex_count = writer.GetIndexCount(),
+          .index_type = IndexType::k16bit,
+      };
+    }
+  }
+
   FML_DCHECK(point_buffer_);
   FML_DCHECK(index_buffer_);
   TessellateConvexInternal(path, *point_buffer_, *index_buffer_, tolerance);
@@ -66,7 +112,7 @@
   point_buffer.clear();
   index_buffer.clear();
 
-  VertexWriter writer(point_buffer, index_buffer);
+  GLESVertexWriter writer(point_buffer, index_buffer);
 
   path.WritePolyline(tolerance, writer);
 }
diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h
index 06e4a12..18fb013 100644
--- a/impeller/tessellator/tessellator.h
+++ b/impeller/tessellator/tessellator.h
@@ -187,7 +187,9 @@
   /// @return A vertex buffer containing all data from the provided curve.
   VertexBuffer TessellateConvex(const Path& path,
                                 HostBuffer& host_buffer,
-                                Scalar tolerance);
+                                Scalar tolerance,
+                                bool supports_primitive_restart = false,
+                                bool supports_triangle_fan = false);
 
   /// Visible for testing.
   ///