Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 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
21 changes: 15 additions & 6 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 @@ -48,10 +49,16 @@ GeometryResult FillPathGeometry::GetPositionBuffer(
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
Contributor 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;
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 = indices_count;
vertex_buffer.index_type = IndexType::kNone;
}
return true;
});
if (tesselation_result != Tessellator::Result::kSuccess) {
Expand Down Expand Up @@ -122,8 +129,10 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer(
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]);
if (indices != nullptr) {
for (auto i = 0u; i < indices_count; i++) {
vertex_builder.AppendIndex(indices[i]);
}
}
return true;
});
Expand Down
180 changes: 137 additions & 43 deletions impeller/tessellator/tessellator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ static int ToTessWindingRule(FillType fill_type) {
return TESS_WINDING_ODD;
}

/// @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;

Tessellator::Result Tessellator::Tessellate(
FillType fill_type,
const Path::Polyline& polyline,
Expand All @@ -78,51 +82,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 vertexItemCount = tessGetVertexCount(tessellator) * kVertexSize;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
int vertexItemCount = tessGetVertexCount(tessellator) * kVertexSize;
int vertex_item_count = tessGetVertexCount(tessellator) * kVertexSize;

These variable names don't conform to the c++ style guide.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

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

int elementItemCount = tessGetElementCount(tessellator) * kPolygonSize;
auto elements = tessGetElements(tessellator);
total += elementItemCount;
for (int i = 0; i < elementItemCount; i++) {
data.emplace_back(points[elements[i]].x);
data.emplace_back(points[elements[i]].y);
}
points.clear();
}
if (!callback(data.data(), data.size(), nullptr, total)) {
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 elementItemCount = 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 (elementItemCount < 65535) {
int vertexItemCount = tessGetVertexCount(tessellator) * kVertexSize;
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(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;
}
} else {
std::vector<Point> points;
std::vector<float> data;

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

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

return Result::kSuccess;
Expand Down
9 changes: 8 additions & 1 deletion impeller/tessellator/tessellator.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,17 @@ class Tessellator {

~Tessellator();

/// @brief A callback that returns the results of the tessellation.
///
/// The index buffer may not be populated, in which case [indices] will
/// be nullptr. The [vertices_size] is the size of the vertices array
/// and not the number of vertices, which is usually vertices_size / 2.
/// In cases where the indices is nullptr, vertex_or_index_count will
/// contain the count of indices.
using BuilderCallback = std::function<bool(const float* vertices,
size_t vertices_size,
const uint16_t* indices,
size_t indices_size)>;
size_t vertex_or_index_count)>;
Copy link
Member

Choose a reason for hiding this comment

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

Overloading this variable doesn't seem prudent. Can't it just be indices_size and be zero if indices == nullptr? We didn't need vertex count in the past

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason this is overloaded is a bit of an issue yes, I should just fix it. Really the problem is that these variables aren't counts, they are the value to multiply with sizeof(float)/sizeof(uint16_t) to compute offsets. But I just make them counts, then this problem goes away.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


//----------------------------------------------------------------------------
/// @brief Generates filled triangles from the polyline. A callback is
Expand Down
47 changes: 47 additions & 0 deletions impeller/tessellator/tessellator_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "flutter/testing/testing.h"
#include "gtest/gtest.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/tessellator/tessellator.h"

Expand Down Expand Up @@ -82,6 +83,52 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) {
ASSERT_EQ(polyline.points.size(), 2u);
ASSERT_EQ(result, Tessellator::Result::kInputError);
}

// More than 30 contours, non-zero fill mode.
{
Tessellator t;
PathBuilder builder = {};
for (auto i = 0; i < 60; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of using a hardcoded 60, you can use the variable you created now to make this test a bit more robust to changes in that number.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

builder.AddCircle(Point(i, i), 4);
}
auto polyline = builder.TakePath().CreatePolyline(1.0f);
bool no_indices = false;
Tessellator::Result result = t.Tessellate(
FillType::kNonZero, polyline,
[&no_indices](const float* vertices, size_t vertices_size,
const uint16_t* indices, size_t indices_size) {
no_indices = indices == nullptr;
return true;
});

ASSERT_TRUE(no_indices);
ASSERT_EQ(result, Tessellator::Result::kSuccess);
}

// More than uint16 points, odd fill mode.
{
Tessellator t;
PathBuilder builder = {};
for (auto i = 0; i < 1000; i++) {
builder.AddCircle(Point(i, i), 4);
}
auto polyline = builder.TakePath(FillType::kOdd).CreatePolyline(1.0f);
bool no_indices = false;
size_t indices_count = 0u;
Tessellator::Result result =
t.Tessellate(FillType::kOdd, polyline,
[&no_indices, &indices_count](
const float* vertices, size_t vertices_size,
const uint16_t* indices, size_t indices_size) {
no_indices = indices == nullptr;
indices_count = indices_size;
return true;
});

ASSERT_TRUE(no_indices);
ASSERT_TRUE(indices_count >= 65535);
ASSERT_EQ(result, Tessellator::Result::kSuccess);
}
}

} // namespace testing
Expand Down