[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.
///