[Impeller] revert non-zero tessellation optimization. (flutter/engine#48234)

Forgot about the winding order. partially reverts https://github.com/flutter/engine/pull/46282

See https://github.com/flutter/flutter/issues/138598

Reopens https://github.com/flutter/flutter/issues/135458
This commit is contained in:
Jonah Williams 2023-11-27 09:26:36 -08:00 committed by GitHub
parent 35434128d8
commit 2753839ce5
3 changed files with 61 additions and 147 deletions

View File

@ -89,142 +89,81 @@ Tessellator::Result Tessellator::Tessellate(const Path& path,
constexpr int kVertexSize = 2;
constexpr int kPolygonSize = 3;
// 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.
///
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);
//----------------------------------------------------------------------------
/// 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 //
);
}
::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;
}
if (result != 1) {
return Result::kTessellationError;
}
int element_item_count = tessGetElementCount(tessellator) * kPolygonSize;
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]);
}
// 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);
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();
// 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(data.data(), total, nullptr, 0u)) {
if (!callback(vertices, vertex_item_count, indices.data(),
element_item_count)) {
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);
std::vector<Point> points;
std::vector<float> data;
::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);
points.reserve(vertex_item_count);
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;
// 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);
points.reserve(vertex_item_count);
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);
data.reserve(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);
}
if (!callback(data.data(), element_item_count, nullptr, 0u)) {
return Result::kInputError;
}
auto elements = tessGetElements(tessellator);
data.reserve(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);
}
if (!callback(data.data(), element_item_count, nullptr, 0u)) {
return Result::kInputError;
}
}

View File

@ -45,10 +45,6 @@ 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

View File

@ -79,27 +79,6 @@ TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) {
ASSERT_EQ(result, Tessellator::Result::kInputError);
}
// More than 30 contours, non-zero fill mode.
{
Tessellator t;
PathBuilder builder = {};
for (auto i = 0u; i < Tessellator::kMultiContourThreshold + 1; i++) {
builder.AddCircle(Point(i, i), 4);
}
auto path = builder.TakePath(FillType::kNonZero);
bool no_indices = false;
Tessellator::Result result = t.Tessellate(
path, 1.0f,
[&no_indices](const float* vertices, size_t vertices_count,
const uint16_t* indices, size_t indices_count) {
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;