Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] Fallback to no index buffer when tesselation count is large, split up nonZero contours. #46282

Merged
merged 11 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions impeller/entity/geometry/fill_path_geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "impeller/entity/geometry/fill_path_geometry.h"
#include "impeller/core/formats.h"

namespace impeller {

Expand Down Expand Up @@ -47,11 +48,17 @@ GeometryResult FillPathGeometry::GetPositionBuffer(
const float* vertices, size_t vertices_count, const uint16_t* indices,
size_t indices_count) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name here belies the fact that it actually can represent 2 different things.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

vertex_buffer.vertex_buffer = host_buffer.Emplace(
vertices, vertices_count * sizeof(float), alignof(float));
vertex_buffer.index_buffer = host_buffer.Emplace(
indices, indices_count * sizeof(uint16_t), alignof(uint16_t));
vertex_buffer.vertex_count = indices_count;
vertex_buffer.index_type = IndexType::k16bit;
vertices, vertices_count * sizeof(float) * 2, alignof(float));
if (indices != nullptr) {
vertex_buffer.index_buffer = host_buffer.Emplace(
indices, indices_count * sizeof(uint16_t), alignof(uint16_t));
vertex_buffer.vertex_count = indices_count;
vertex_buffer.index_type = IndexType::k16bit;
} else {
vertex_buffer.index_buffer = {};
vertex_buffer.vertex_count = vertices_count;
vertex_buffer.index_type = IndexType::kNone;
}
return true;
});
if (tesselation_result != Tessellator::Result::kSuccess) {
Expand Down Expand Up @@ -112,7 +119,7 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer(
[&vertex_builder, &texture_coverage, &effect_transform](
const float* vertices, size_t vertices_count, const uint16_t* indices,
size_t indices_count) {
for (auto i = 0u; i < vertices_count; i += 2) {
for (auto i = 0u; i < vertices_count * 2; i += 2) {
VS::PerVertexData data;
Point vtx = {vertices[i], vertices[i + 1]};
data.position = vtx;
Expand All @@ -121,9 +128,11 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer(
texture_coverage.size;
vertex_builder.AppendVertex(data);
}
FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count / 2);
for (auto i = 0u; i < indices_count; i++) {
vertex_builder.AppendIndex(indices[i]);
FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count);
if (indices != nullptr) {
for (auto i = 0u; i < indices_count; i++) {
vertex_builder.AppendIndex(indices[i]);
}
}
return true;
});
Expand Down
4 changes: 2 additions & 2 deletions impeller/geometry/geometry_benchmarks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ static void BM_Polyline(benchmark::State& state, Args&&... args) {
if (tessellate) {
tess.Tessellate(
FillType::kNonZero, polyline,
[](const float* vertices, size_t vertices_size,
const uint16_t* indices, size_t indices_size) { return true; });
[](const float* vertices, size_t vertices_count,
const uint16_t* indices, size_t indices_count) { return true; });
}
}
state.counters["SinglePointCount"] = single_point_count;
Expand Down
8 changes: 4 additions & 4 deletions impeller/renderer/renderer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -404,14 +404,14 @@ TEST_P(RendererTest, CanRenderInstanced) {
.AddRect(Rect::MakeXYWH(10, 10, 100, 100))
.TakePath()
.CreatePolyline(1.0f),
[&builder](const float* vertices, size_t vertices_size,
const uint16_t* indices, size_t indices_size) {
for (auto i = 0u; i < vertices_size; i += 2) {
[&builder](const float* vertices, size_t vertices_count,
const uint16_t* indices, size_t indices_count) {
for (auto i = 0u; i < vertices_count * 2; i += 2) {
VS::PerVertexData data;
data.vtx = {vertices[i], vertices[i + 1]};
builder.AppendVertex(data);
}
for (auto i = 0u; i < indices_size; i++) {
for (auto i = 0u; i < indices_count; i++) {
builder.AppendIndex(indices[i]);
}
return true;
Expand Down
8 changes: 4 additions & 4 deletions impeller/tessellator/c/tessellator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ struct Vertices* Tessellate(PathBuilder* builder,
std::vector<float> points;
if (Tessellator{}.Tessellate(
path.GetFillType(), polyline,
[&points](const float* vertices, size_t vertices_size,
const uint16_t* indices, size_t indices_size) {
[&points](const float* vertices, size_t vertices_count,
const uint16_t* indices, size_t indices_count) {
// Results are expected to be re-duplicated.
std::vector<Point> raw_points;
for (auto i = 0u; i < vertices_size; i += 2) {
for (auto i = 0u; i < vertices_count * 2; i += 2) {
raw_points.emplace_back(Point{vertices[i], vertices[i + 1]});
}
for (auto i = 0u; i < indices_size; i++) {
for (auto i = 0u; i < indices_count; i++) {
auto point = raw_points[indices[i]];
points.push_back(point.x);
points.push_back(point.y);
Expand Down
176 changes: 133 additions & 43 deletions impeller/tessellator/tessellator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,51 +78,141 @@ Tessellator::Result Tessellator::Tessellate(
constexpr int kVertexSize = 2;
constexpr int kPolygonSize = 3;

//----------------------------------------------------------------------------
/// Feed contour information to the tessellator.
///
static_assert(sizeof(Point) == 2 * sizeof(float));
for (size_t contour_i = 0; contour_i < polyline.contours.size();
contour_i++) {
size_t start_point_index, end_point_index;
std::tie(start_point_index, end_point_index) =
polyline.GetContourPointBounds(contour_i);

::tessAddContour(tessellator, // the C tessellator
kVertexSize, //
polyline.points.data() + start_point_index, //
sizeof(Point), //
end_point_index - start_point_index //
// If we have a larger polyline and the fill type is non-zero, we can split
// the tessellation up per contour. Since in general the complexity is at
// least nlog(n), this speeds up the processes substantially.
if (polyline.contours.size() > kMultiContourThreshold &&
fill_type == FillType::kNonZero) {
std::vector<Point> points;
std::vector<float> data;

//----------------------------------------------------------------------------
/// Feed contour information to the tessellator.
///
size_t total = 0u;
static_assert(sizeof(Point) == 2 * sizeof(float));
for (size_t contour_i = 0; contour_i < polyline.contours.size();
contour_i++) {
size_t start_point_index, end_point_index;
std::tie(start_point_index, end_point_index) =
polyline.GetContourPointBounds(contour_i);

::tessAddContour(tessellator, // the C tessellator
kVertexSize, //
polyline.points.data() + start_point_index, //
sizeof(Point), //
end_point_index - start_point_index //
);

//----------------------------------------------------------------------------
/// Let's tessellate.
///
auto result = ::tessTesselate(tessellator, // tessellator
ToTessWindingRule(fill_type), // winding
TESS_POLYGONS, // element type
kPolygonSize, // polygon size
kVertexSize, // vertex size
nullptr // normal (null is automatic)
);

if (result != 1) {
return Result::kTessellationError;
}

int vertex_item_count = tessGetVertexCount(tessellator) * kVertexSize;
auto vertices = tessGetVertices(tessellator);
for (int i = 0; i < vertex_item_count; i += 2) {
points.emplace_back(vertices[i], vertices[i + 1]);
}

int element_item_count = tessGetElementCount(tessellator) * kPolygonSize;
auto elements = tessGetElements(tessellator);
total += element_item_count;
for (int i = 0; i < element_item_count; i++) {
data.emplace_back(points[elements[i]].x);
data.emplace_back(points[elements[i]].y);
}
points.clear();
}
if (!callback(data.data(), total, nullptr, 0u)) {
return Result::kInputError;
}
} else {
//----------------------------------------------------------------------------
/// Feed contour information to the tessellator.
///
static_assert(sizeof(Point) == 2 * sizeof(float));
for (size_t contour_i = 0; contour_i < polyline.contours.size();
contour_i++) {
size_t start_point_index, end_point_index;
std::tie(start_point_index, end_point_index) =
polyline.GetContourPointBounds(contour_i);

::tessAddContour(tessellator, // the C tessellator
kVertexSize, //
polyline.points.data() + start_point_index, //
sizeof(Point), //
end_point_index - start_point_index //
);
}

//----------------------------------------------------------------------------
/// Let's tessellate.
///
auto result = ::tessTesselate(tessellator, // tessellator
ToTessWindingRule(fill_type), // winding
TESS_POLYGONS, // element type
kPolygonSize, // polygon size
kVertexSize, // vertex size
nullptr // normal (null is automatic)
);
}

//----------------------------------------------------------------------------
/// Let's tessellate.
///
auto result = ::tessTesselate(tessellator, // tessellator
ToTessWindingRule(fill_type), // winding
TESS_POLYGONS, // element type
kPolygonSize, // polygon size
kVertexSize, // vertex size
nullptr // normal (null is automatic)
);

if (result != 1) {
return Result::kTessellationError;
}

int vertexItemCount = tessGetVertexCount(tessellator) * kVertexSize;
auto vertices = tessGetVertices(tessellator);
int elementItemCount = tessGetElementCount(tessellator) * kPolygonSize;
auto elements = tessGetElements(tessellator);
// libtess uses an int index internally due to usage of -1 as a sentinel
// value.
std::vector<uint16_t> indices(elementItemCount);
for (int i = 0; i < elementItemCount; i++) {
indices[i] = static_cast<uint16_t>(elements[i]);
}
if (!callback(vertices, vertexItemCount, indices.data(), elementItemCount)) {
return Result::kInputError;
if (result != 1) {
return Result::kTessellationError;
}

int element_item_count = tessGetElementCount(tessellator) * kPolygonSize;

// We default to using a 16bit index buffer, but in cases where we generate
// more tessellated data than this can contain we need to fall back to
// dropping the index buffer entirely. Instead code could instead switch to
// a uint32 index buffer, but this is done for simplicity with the other
// fast path above.
if (element_item_count < USHRT_MAX) {
int vertex_item_count = tessGetVertexCount(tessellator);
auto vertices = tessGetVertices(tessellator);
auto elements = tessGetElements(tessellator);

// libtess uses an int index internally due to usage of -1 as a sentinel
// value.
std::vector<uint16_t> indices(element_item_count);
for (int i = 0; i < element_item_count; i++) {
indices[i] = static_cast<uint16_t>(elements[i]);
}
if (!callback(vertices, vertex_item_count, indices.data(),
element_item_count)) {
return Result::kInputError;
}
} else {
std::vector<Point> points;
std::vector<float> data;

int vertex_item_count = tessGetVertexCount(tessellator) * kVertexSize;
auto vertices = tessGetVertices(tessellator);
for (int i = 0; i < vertex_item_count; i += 2) {
points.emplace_back(vertices[i], vertices[i + 1]);
}

int element_item_count = tessGetElementCount(tessellator) * kPolygonSize;
auto elements = tessGetElements(tessellator);
for (int i = 0; i < element_item_count; i++) {
data.emplace_back(points[elements[i]].x);
data.emplace_back(points[elements[i]].y);
}
if (!callback(data.data(), element_item_count, nullptr, 0u)) {
return Result::kInputError;
}
}
}

return Result::kSuccess;
Expand Down
12 changes: 10 additions & 2 deletions impeller/tessellator/tessellator.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,18 @@ class Tessellator {

~Tessellator();

/// @brief An arbitrary value to determine when a multi-contour non-zero fill
/// path should be split into multiple tessellations.
static constexpr size_t kMultiContourThreshold = 30u;

/// @brief A callback that returns the results of the tessellation.
///
/// The index buffer may not be populated, in which case [indices] will
/// be nullptr and indices_count will be 0.
using BuilderCallback = std::function<bool(const float* vertices,
size_t vertices_size,
size_t vertices_count,
const uint16_t* indices,
size_t indices_size)>;
size_t indices_count)>;

//----------------------------------------------------------------------------
/// @brief Generates filled triangles from the polyline. A callback is
Expand Down
Loading