GrTessellator: fix for ping-pong split fuzzer hang.
Change 3b5a3fa8b1
introduced support for
splitting on out-of-range intersections. However, this is only necessary
for correctness when the edge is nearly-flat (the top and bottom
points only differ by 1/2 machine epsilon in the primary sort criterion).
In other cases, it can cause repeated splitting and re-merging of edges,
as the intersection code (being approximate and not exact) may produce a
ping-pong set of intersections.
The fix is to support out-of-range intersections only if they differ by
1/2 machine epsilon. This also generalizes the
out_of_range_and_collinear() check, so it was removed.
Bug: 838978
Change-Id: I134f7eff3f15707e0d68de11c55f7fadce4ff8e7
Reviewed-on: https://skia-review.googlesource.com/130448
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
This commit is contained in:
parent
36be574e7b
commit
bfb2a05af1
@ -1131,17 +1131,6 @@ void merge_vertices(Vertex* src, Vertex* dst, VertexList* mesh, Comparator& c,
|
|||||||
mesh->remove(src);
|
mesh->remove(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool out_of_range_and_collinear(const SkPoint& p, Edge* edge, Comparator& c) {
|
|
||||||
if (c.sweep_lt(p, edge->fTop->fPoint) &&
|
|
||||||
!Line(p, edge->fBottom->fPoint).dist(edge->fTop->fPoint)) {
|
|
||||||
return true;
|
|
||||||
} else if (c.sweep_lt(edge->fBottom->fPoint, p) &&
|
|
||||||
!Line(edge->fTop->fPoint, p).dist(edge->fBottom->fPoint)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex* create_sorted_vertex(const SkPoint& p, uint8_t alpha, VertexList* mesh,
|
Vertex* create_sorted_vertex(const SkPoint& p, uint8_t alpha, VertexList* mesh,
|
||||||
Vertex* reference, Comparator& c, SkArenaAlloc& alloc) {
|
Vertex* reference, Comparator& c, SkArenaAlloc& alloc) {
|
||||||
Vertex* prevV = reference;
|
Vertex* prevV = reference;
|
||||||
@ -1174,6 +1163,12 @@ Vertex* create_sorted_vertex(const SkPoint& p, uint8_t alpha, VertexList* mesh,
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nearly_flat(Comparator& c, Edge* edge) {
|
||||||
|
SkPoint diff = edge->fBottom->fPoint - edge->fTop->fPoint;
|
||||||
|
float primaryDiff = c.fDirection == Comparator::Direction::kHorizontal ? diff.fX : diff.fY;
|
||||||
|
return fabs(primaryDiff) < std::numeric_limits<float>::epsilon();
|
||||||
|
}
|
||||||
|
|
||||||
bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
|
bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
|
||||||
VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) {
|
VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) {
|
||||||
if (!edge || !other) {
|
if (!edge || !other) {
|
||||||
@ -1182,13 +1177,7 @@ bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vert
|
|||||||
SkPoint p;
|
SkPoint p;
|
||||||
uint8_t alpha;
|
uint8_t alpha;
|
||||||
if (edge->intersect(*other, &p, &alpha) && p.isFinite()) {
|
if (edge->intersect(*other, &p, &alpha) && p.isFinite()) {
|
||||||
// Ignore any out-of-range intersections which are also collinear,
|
Vertex* v = nullptr;
|
||||||
// since the resulting edges would cancel each other out by merging.
|
|
||||||
if (out_of_range_and_collinear(p, edge, c) ||
|
|
||||||
out_of_range_and_collinear(p, other, c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Vertex* v;
|
|
||||||
LOG("found intersection, pt is %g, %g\n", p.fX, p.fY);
|
LOG("found intersection, pt is %g, %g\n", p.fX, p.fY);
|
||||||
Vertex* top = *current;
|
Vertex* top = *current;
|
||||||
// If the intersection point is above the current vertex, rewind to the vertex above the
|
// If the intersection point is above the current vertex, rewind to the vertex above the
|
||||||
@ -1196,15 +1185,23 @@ bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vert
|
|||||||
while (top && c.sweep_lt(p, top->fPoint)) {
|
while (top && c.sweep_lt(p, top->fPoint)) {
|
||||||
top = top->fPrev;
|
top = top->fPrev;
|
||||||
}
|
}
|
||||||
if (p == edge->fTop->fPoint) {
|
if (p == edge->fTop->fPoint ||
|
||||||
|
(c.sweep_lt(p, edge->fTop->fPoint) && !nearly_flat(c, edge))) {
|
||||||
v = edge->fTop;
|
v = edge->fTop;
|
||||||
} else if (p == edge->fBottom->fPoint) {
|
}
|
||||||
|
if (p == edge->fBottom->fPoint ||
|
||||||
|
(c.sweep_lt(edge->fBottom->fPoint, p) && !nearly_flat(c, edge))) {
|
||||||
v = edge->fBottom;
|
v = edge->fBottom;
|
||||||
} else if (p == other->fTop->fPoint) {
|
}
|
||||||
|
if (p == other->fTop->fPoint ||
|
||||||
|
(c.sweep_lt(p, other->fTop->fPoint) && !nearly_flat(c, other))) {
|
||||||
v = other->fTop;
|
v = other->fTop;
|
||||||
} else if (p == other->fBottom->fPoint) {
|
}
|
||||||
|
if (p == other->fBottom->fPoint ||
|
||||||
|
(c.sweep_lt(other->fBottom->fPoint, p) && !nearly_flat(c, other))) {
|
||||||
v = other->fBottom;
|
v = other->fBottom;
|
||||||
} else {
|
}
|
||||||
|
if (!v) {
|
||||||
v = create_sorted_vertex(p, alpha, mesh, top, c, alloc);
|
v = create_sorted_vertex(p, alpha, mesh, top, c, alloc);
|
||||||
if (edge->fTop->fPartner) {
|
if (edge->fTop->fPartner) {
|
||||||
Line line1 = edge->fLine;
|
Line line1 = edge->fLine;
|
||||||
|
@ -502,6 +502,16 @@ static SkPath create_path_33() {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From crbug.com/844873. Hangs repeatedly splitting alternate vertices.
|
||||||
|
static SkPath create_path_34() {
|
||||||
|
SkPath path;
|
||||||
|
path.moveTo(10, -1e+20f);
|
||||||
|
path.lineTo(11, 25000);
|
||||||
|
path.lineTo(10, 25000);
|
||||||
|
path.lineTo(11, 25010);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
static std::unique_ptr<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) {
|
static std::unique_ptr<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) {
|
||||||
|
|
||||||
SkPoint pts[2] = { {0, 0}, {1, 1} };
|
SkPoint pts[2] = { {0, 0}, {1, 1} };
|
||||||
@ -595,5 +605,6 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TessellatingPathRendererTests, reporter, ctxInfo) {
|
|||||||
test_path(ctx, rtc.get(), create_path_31(), SkMatrix(), GrAAType::kCoverage);
|
test_path(ctx, rtc.get(), create_path_31(), SkMatrix(), GrAAType::kCoverage);
|
||||||
test_path(ctx, rtc.get(), create_path_32());
|
test_path(ctx, rtc.get(), create_path_32());
|
||||||
test_path(ctx, rtc.get(), create_path_33());
|
test_path(ctx, rtc.get(), create_path_33());
|
||||||
|
test_path(ctx, rtc.get(), create_path_34());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user