From 6f5e77a08faf9d967eba8194811cbf2391092c23 Mon Sep 17 00:00:00 2001 From: Chris Dalton Date: Mon, 23 Apr 2018 21:14:42 -0600 Subject: ccpr: Cull extremely thin triangles When triangles get too thin it's possible for FP round-off error to actually give us the wrong winding direction, causing rendering artifacts. This change also allows us to unblacklist ANGLE. Bug: skia:7805 Bug: skia:7820 Change-Id: Ibaa0f033eba625d720e3a594c4515d8264cc413d Reviewed-on: https://skia-review.googlesource.com/123262 Reviewed-by: Brian Osman Commit-Queue: Chris Dalton --- bench/GrCCGeometryBench.cpp | 5 +- gm/mandoline.cpp | 202 ++++++++++++++++++++++++++ gn/gm.gni | 1 + src/gpu/ccpr/GrCCCoverageProcessor.cpp | 30 ++++ src/gpu/ccpr/GrCCCoverageProcessor.h | 10 ++ src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 24 +-- src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 14 +- src/gpu/ccpr/GrCCGeometry.cpp | 56 +++---- src/gpu/ccpr/GrCCGeometry.h | 4 +- src/gpu/ccpr/GrCCPathParser.cpp | 5 +- src/gpu/gl/GrGLCaps.cpp | 19 +-- 11 files changed, 295 insertions(+), 75 deletions(-) create mode 100644 gm/mandoline.cpp diff --git a/bench/GrCCGeometryBench.cpp b/bench/GrCCGeometryBench.cpp index 5e47df042c..0628644ab9 100644 --- a/bench/GrCCGeometryBench.cpp +++ b/bench/GrCCGeometryBench.cpp @@ -22,6 +22,7 @@ public: fPoints[1].set(x1, y1); fPoints[2].set(x2, y2); fPoints[3].set(x3, y3); + fPoints[4].set(x0, y0); // Flat closing edge. fName = "ccprgeometry"; switch (SkClassifyCubic(fPoints)) { @@ -52,7 +53,7 @@ public: fGeometry.beginContour(fPoints[0]); for (int i = 0; i < kNumBaseLoops; ++i) { fGeometry.cubicTo(fPoints); - fGeometry.lineTo(fPoints[0]); + fGeometry.lineTo(fPoints+3); } fGeometry.endContour(); fGeometry.reset(); @@ -60,7 +61,7 @@ public: } private: - SkPoint fPoints[4]; + SkPoint fPoints[5]; SkString fName; GrCCGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops}; diff --git a/gm/mandoline.cpp b/gm/mandoline.cpp new file mode 100644 index 0000000000..17dd02f7e0 --- /dev/null +++ b/gm/mandoline.cpp @@ -0,0 +1,202 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "sk_tool_utils.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkGeometry.h" +#include + +namespace skiagm { + +// Slices paths into sliver-size contours shaped like ice cream cones. +class MandolineSlicer { +public: + static constexpr int kDefaultSubdivisions = 10; + + MandolineSlicer(SkPoint anchorPt) { + fPath.setFillType(SkPath::kEvenOdd_FillType); + fPath.setIsVolatile(true); + this->reset(anchorPt); + } + + void reset(SkPoint anchorPt) { + fPath.reset(); + fLastPt = fAnchorPt = anchorPt; + } + + void sliceLine(SkPoint pt, int numSubdivisions = kDefaultSubdivisions) { + if (numSubdivisions <= 0) { + fPath.moveTo(fAnchorPt); + fPath.lineTo(fLastPt); + fPath.lineTo(pt); + fPath.close(); + fLastPt = pt; + return; + } + float T = this->chooseChopT(numSubdivisions); + if (0 == T) { + fPath.lineTo(fLastPt); + this->sliceLine(pt, numSubdivisions - 1); + return; + } + SkPoint midpt = fLastPt * (1 - T) + pt * T; + this->sliceLine(midpt, numSubdivisions - 1); + this->sliceLine(pt, numSubdivisions - 1); + } + + void sliceQuadratic(SkPoint p1, SkPoint p2, int numSubdivisions = kDefaultSubdivisions) { + if (numSubdivisions <= 0) { + fPath.moveTo(fAnchorPt); + fPath.lineTo(fLastPt); + fPath.quadTo(p1, p2); + fPath.close(); + fLastPt = p2; + return; + } + float T = this->chooseChopT(numSubdivisions); + if (0 == T) { + fPath.quadTo(fLastPt, fLastPt); + this->sliceQuadratic(p1, p2, numSubdivisions - 1); + return; + } + SkPoint P[3] = {fLastPt, p1, p2}, PP[5]; + SkChopQuadAt(P, PP, T); + this->sliceQuadratic(PP[1], PP[2], numSubdivisions - 1); + this->sliceQuadratic(PP[3], PP[4], numSubdivisions - 1); + } + + void sliceCubic(SkPoint p1, SkPoint p2, SkPoint p3, + int numSubdivisions = kDefaultSubdivisions) { + if (numSubdivisions <= 0) { + fPath.moveTo(fAnchorPt); + fPath.lineTo(fLastPt); + fPath.cubicTo(p1, p2, p3); + fPath.close(); + fLastPt = p3; + return; + } + float T = this->chooseChopT(numSubdivisions); + if (0 == T) { + fPath.cubicTo(fLastPt, fLastPt, fLastPt); + this->sliceCubic(p1, p2, p3, numSubdivisions - 1); + return; + } + SkPoint P[4] = {fLastPt, p1, p2, p3}, PP[7]; + SkChopCubicAt(P, PP, T); + this->sliceCubic(PP[1], PP[2], PP[3], numSubdivisions - 1); + this->sliceCubic(PP[4], PP[5], PP[6], numSubdivisions - 1); + } + + void sliceConic(SkPoint p1, SkPoint p2, float w, int numSubdivisions = kDefaultSubdivisions) { + if (numSubdivisions <= 0) { + fPath.moveTo(fAnchorPt); + fPath.lineTo(fLastPt); + fPath.conicTo(p1, p2, w); + fPath.close(); + fLastPt = p2; + return; + } + float T = this->chooseChopT(numSubdivisions); + if (0 == T) { + fPath.conicTo(fLastPt, fLastPt, w); + this->sliceConic(p1, p2, w, numSubdivisions - 1); + return; + } + SkConic conic(fLastPt, p1, p2, w), halves[2]; + if (!conic.chopAt(T, halves)) { + SK_ABORT("SkConic::chopAt failed"); + } + this->sliceConic(halves[0].fPts[1], halves[0].fPts[2], halves[0].fW, numSubdivisions - 1); + this->sliceConic(halves[1].fPts[1], halves[1].fPts[2], halves[1].fW, numSubdivisions - 1); + } + + const SkPath& path() const { return fPath; } + +private: + float chooseChopT(int numSubdivisions) { + SkASSERT(numSubdivisions > 0); + if (numSubdivisions > 1) { + return .5f; + } + float T = (0 == fRand.nextU() % 10) ? 0 : scalbnf(1, -(int)fRand.nextRangeU(10, 149)); + SkASSERT(T >= 0 && T < 1); + return T; + } + + SkRandom fRand; + SkPath fPath; + SkPoint fAnchorPt; + SkPoint fLastPt; +}; + +class SliverPathsGM : public GM { +public: + SliverPathsGM() { + this->setBGColor(sk_tool_utils::color_to_565(SK_ColorBLACK)); + } + +protected: + SkString onShortName() override { + return SkString("mandoline"); + } + + SkISize onISize() override { + return SkISize::Make(560, 475); + } + + void onDraw(SkCanvas* canvas) override { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + paint.setAntiAlias(true); + + MandolineSlicer mandoline({41, 43}); + mandoline.sliceCubic({5, 277}, {381, -74}, {243, 162}); + mandoline.sliceLine({41, 43}); + canvas->drawPath(mandoline.path(), paint); + + mandoline.reset({357.049988f, 446.049988f}); + mandoline.sliceCubic({472.750000f, -71.950012f}, {639.750000f, 531.950012f}, + {309.049988f, 347.950012f}); + mandoline.sliceLine({309.049988f, 419}); + mandoline.sliceLine({357.049988f, 446.049988f}); + canvas->drawPath(mandoline.path(), paint); + + canvas->save(); + canvas->translate(421, 105); + canvas->scale(100, 81); + mandoline.reset({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))}); + mandoline.sliceConic({-2, 0}, + {-cosf(SkDegreesToRadians(60)), sinf(SkDegreesToRadians(60))}, .5f); + mandoline.sliceConic({-cosf(SkDegreesToRadians(120))*2, sinf(SkDegreesToRadians(120))*2}, + {1, 0}, .5f); + mandoline.sliceLine({0, 0}); + mandoline.sliceLine({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))}); + canvas->drawPath(mandoline.path(), paint); + canvas->restore(); + + canvas->save(); + canvas->translate(150, 300); + canvas->scale(75, 75); + mandoline.reset({1, 0}); + constexpr int nquads = 5; + for (int i = 0; i < nquads; ++i) { + float theta1 = 2*SK_ScalarPI/nquads * (i + .5f); + float theta2 = 2*SK_ScalarPI/nquads * (i + 1); + mandoline.sliceQuadratic({cosf(theta1)*2, sinf(theta1)*2}, + {cosf(theta2), sinf(theta2)}); + } + canvas->drawPath(mandoline.path(), paint); + canvas->restore(); + } +}; + +DEF_GM(return new SliverPathsGM;) + +} diff --git a/gn/gm.gni b/gn/gm.gni index 0e5055b29b..a02ff958a7 100644 --- a/gn/gm.gni +++ b/gn/gm.gni @@ -206,6 +206,7 @@ gm_sources = [ "$_gm/lumafilter.cpp", "$_gm/makecolorspace.cpp", "$_gm/makeRasterImage.cpp", + "$_gm/mandoline.cpp", "$_gm/manypaths.cpp", "$_gm/matrixconvolution.cpp", "$_gm/matriximagefilter.cpp", diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index d38db27a19..75d0667b09 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -44,6 +44,36 @@ class GrCCCoverageProcessor::TriangleShader : public GrCCCoverageProcessor::Shad GrGLSLVarying fCoverages; }; +void GrCCCoverageProcessor::Shader::CalcWind(const GrCCCoverageProcessor& proc, + GrGLSLVertexGeoBuilder* s, const char* pts, + const char* outputWind) { + if (3 == proc.numInputPoints()) { + s->codeAppendf("float2 a = %s[0] - %s[1], " + "b = %s[0] - %s[2];", pts, pts, pts, pts); + } else { + // All inputs are convex, so it's sufficient to just average the middle two input points. + SkASSERT(4 == proc.numInputPoints()); + s->codeAppendf("float2 p12 = (%s[1] + %s[2]) * .5;", pts, pts); + s->codeAppendf("float2 a = %s[0] - p12, " + "b = %s[0] - %s[3];", pts, pts, pts); + } + + s->codeAppend ("float area_x2 = determinant(float2x2(a, b));"); + if (proc.isTriangles()) { + // We cull extremely thin triangles by zeroing wind. When a triangle gets too thin it's + // possible for FP round-off error to actually give us the wrong winding direction, causing + // rendering artifacts. The criteria we choose is "height <~ 1/1024". So we drop a triangle + // if the max effect it can have on any single pixel is <~ 1/1024, or 1/4 of a bit in 8888. + s->codeAppend ("float2 bbox_size = max(abs(a), abs(b));"); + s->codeAppend ("float basewidth = max(bbox_size.x + bbox_size.y, 1);"); + s->codeAppendf("%s = (abs(area_x2 * 1024) > basewidth) ? sign(area_x2) : 0;", outputWind); + } else { + // We already converted nearly-flat curves to lines on the CPU, so no need to worry about + // thin curve hulls at this point. + s->codeAppendf("%s = sign(area_x2);", outputWind); + } +} + void GrCCCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder* s, const char* leftPt, const char* rightPt, diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index 454e728ae9..e3ea34f8dd 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -133,6 +133,11 @@ public: void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* skOutputColor, const char* skOutputCoverage) const; + // Calculates the winding direction of the input points (+1, -1, or 0). Wind for extremely + // thin triangles gets rounded to zero. + static void CalcWind(const GrCCCoverageProcessor&, GrGLSLVertexGeoBuilder*, const char* pts, + const char* outputWind); + // Defines an equation ("dot(float3(pt, 1), distance_equation)") that is -1 on the outside // border of a conservative raster edge and 0 on the inside. 'leftPt' and 'rightPt' must be // ordered clockwise. @@ -207,6 +212,11 @@ private: // Number of bezier points for curves, or 3 for triangles. int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; } + bool isTriangles() const { + return PrimitiveType::kTriangles == fPrimitiveType || + PrimitiveType::kWeightedTriangles == fPrimitiveType; + } + int hasInputWeight() const { return PrimitiveType::kWeightedTriangles == fPrimitiveType || PrimitiveType::kConics == fPrimitiveType; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index b1d886cf8c..61bb4ec7cf 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -59,18 +59,11 @@ protected: GrShaderVar wind("wind", kHalf_GrSLType); g->declareGlobal(wind); - if (PrimitiveType::kWeightedTriangles != proc.fPrimitiveType) { - g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], " - "pts[0] - pts[2]));"); - if (4 == numInputPoints) { - g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], " - "pts[0] - pts[3]));"); - } - g->codeAppendf("%s = sign(area_x2);", wind.c_str()); - } else { + Shader::CalcWind(proc, g, "pts", wind.c_str()); + if (PrimitiveType::kWeightedTriangles == proc.fPrimitiveType) { SkASSERT(3 == numInputPoints); SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(0).fType); - g->codeAppendf("%s = sk_in[0].sk_Position.w;", wind.c_str()); + g->codeAppendf("%s *= sk_in[0].sk_Position.w;", wind.c_str()); } SkString emitVertexFn; @@ -306,10 +299,8 @@ public: const GrShaderVar& wind, const char* emitVertexFn) const override { fShader->emitSetupCode(g, "pts", wind.c_str()); - bool isTriangle = PrimitiveType::kTriangles == proc.fPrimitiveType || - PrimitiveType::kWeightedTriangles == proc.fPrimitiveType; g->codeAppendf("int corneridx = sk_InvocationID;"); - if (!isTriangle) { + if (!proc.isTriangles()) { g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1); } @@ -336,7 +327,7 @@ public: Shader::CalcCornerAttenuation(g, "leftdir", "rightdir", "attenuation"); g->codeAppend ("}"); - if (isTriangle) { + if (proc.isTriangles()) { g->codeAppend ("half2 left_coverages; {"); Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "-outbloat", "-crossbloat", "left_coverages"); @@ -384,7 +375,7 @@ public: g->codeAppendf("%s(corner + crossbloat * bloat, -1, half2(1));", emitVertexFn); } - g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, isTriangle ? 3 : 2); + g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, proc.isTriangles() ? 3 : 2); } }; @@ -416,8 +407,7 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr shadr) const { if (GSSubpass::kHulls == fGSSubpass) { - return (PrimitiveType::kTriangles == fPrimitiveType || - PrimitiveType::kWeightedTriangles == fPrimitiveType) + return this->isTriangles() ? (GSImpl*) new GSTriangleHullImpl(std::move(shadr)) : (GSImpl*) new GSCurveHullImpl(std::move(shadr)); } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index dd8da96f82..08b8886aed 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -263,18 +263,12 @@ void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) inputWidth, inputWidth, proc.getAttrib(kAttribIdx_X).fName, swizzle, proc.getAttrib(kAttribIdx_Y).fName, swizzle); - if (PrimitiveType::kWeightedTriangles != proc.fPrimitiveType) { - v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], " - "pts[0] - pts[2]));"); - if (4 == numInputPoints) { - v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], " - "pts[0] - pts[3]));"); - } - v->codeAppend ("half wind = sign(area_x2);"); - } else { + v->codeAppend ("half wind;"); + Shader::CalcWind(proc, v, "pts", "wind"); + if (PrimitiveType::kWeightedTriangles == proc.fPrimitiveType) { SkASSERT(3 == numInputPoints); SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType); - v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName); + v->codeAppendf("wind *= %s.w;", proc.getAttrib(kAttribIdx_X).fName); } float bloat = kAABloatRadius; diff --git a/src/gpu/ccpr/GrCCGeometry.cpp b/src/gpu/ccpr/GrCCGeometry.cpp index 9fbf3e61f8..30d93ad416 100644 --- a/src/gpu/ccpr/GrCCGeometry.cpp +++ b/src/gpu/ccpr/GrCCGeometry.cpp @@ -38,14 +38,20 @@ void GrCCGeometry::beginContour(const SkPoint& pt) { SkDEBUGCODE(fBuildingContour = true); } -void GrCCGeometry::lineTo(const SkPoint& pt) { +void GrCCGeometry::lineTo(const SkPoint P[2]) { SkASSERT(fBuildingContour); - fPoints.push_back(pt); - fVerbs.push_back(Verb::kLineTo); + SkASSERT(P[0] == fPoints.back()); + Sk2f p0 = Sk2f::Load(P); + Sk2f p1 = Sk2f::Load(P+1); + this->appendLine(p0, p1); } -void GrCCGeometry::appendLine(const Sk2f& endpt) { - endpt.store(&fPoints.push_back()); +inline void GrCCGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) { + SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); + if ((p0 == p1).allTrue()) { + return; + } + p1.store(&fPoints.push_back()); fVerbs.push_back(Verb::kLineTo); } @@ -142,7 +148,7 @@ void GrCCGeometry::quadraticTo(const SkPoint P[3]) { // Don't crunch on the curve if it is nearly flat (or just very small). Flat curves can break // The monotonic chopping math. if (are_collinear(p0, p1, p2)) { - this->appendLine(p2); + this->appendLine(p0, p2); return; } @@ -190,12 +196,12 @@ inline void GrCCGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const inline void GrCCGeometry::appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) { // Don't send curves to the GPU if we know they are nearly flat (or just very small). if (are_collinear(p0, p1, p2)) { - SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); - this->appendLine(p2); + this->appendLine(p0, p2); return; } SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); + SkASSERT((p0 != p2).anyTrue()); p1.store(&fPoints.push_back()); p2.store(&fPoints.push_back()); fVerbs.push_back(Verb::kMonotonicQuadraticTo); @@ -466,7 +472,9 @@ void GrCCGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopInter // Don't crunch on the curve or inflate geometry if it is nearly flat (or just very small). // Flat curves can break the math below. if (are_collinear(P)) { - this->lineTo(P[3]); + Sk2f p0 = Sk2f::Load(P); + Sk2f p3 = Sk2f::Load(P+3); + this->appendLine(p0, p3); return; } @@ -570,10 +578,6 @@ void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) { - if ((p0 == p3).allTrue()) { - return; - } - if (SkCubicType::kLoop != fCurrCubicType) { // Serpentines and cusps are always monotonic after chopping around inflection points. SkASSERT(!SkCubicIsDegenerate(fCurrCubicType)); @@ -583,8 +587,7 @@ void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f // This can cause some curves to feel slightly more flat when inspected rigorously back // and forth against another renderer, but for now this seems acceptable given the // simplicity. - SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); - this->appendLine(p3); + this->appendLine(p0, p3); return; } } else { @@ -613,12 +616,12 @@ void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f // Don't send curves to the GPU if we know they are nearly flat (or just very small). // Since the cubic segment is known to be convex at this point, our flatness check is simple. if (are_collinear(p0, (p1 + p2) * .5f, p3)) { - SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); - this->appendLine(p3); + this->appendLine(p0, p3); return; } SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); + SkASSERT((p0 != p3).anyTrue()); p1.store(&fPoints.push_back()); p2.store(&fPoints.push_back()); p3.store(&fPoints.push_back()); @@ -686,7 +689,7 @@ inline void GrCCGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, c // near-flat cubics in cubicTo().) if (!(midT > 0 && midT < 1)) { // The cubic is flat. Otherwise there would be a real midtangent inside T=0..1. - this->appendLine(p3); + this->appendLine(p0, p3); return; } @@ -720,7 +723,7 @@ void GrCCGeometry::conicTo(const SkPoint P[3], float w) { // midtangents.) if (!(midT > 0 && midT < 1)) { // The conic is flat. Otherwise there would be a real midtangent inside T=0..1. - this->appendLine(p2); + this->appendLine(p0, p2); return; } @@ -747,7 +750,6 @@ void GrCCGeometry::conicTo(const SkPoint P[3], float w) { void GrCCGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, float w) { SkASSERT(w >= 0); - SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); Sk2f base = p2 - p0; Sk2f baseAbs = base.abs(); @@ -758,24 +760,28 @@ void GrCCGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk float h1 = std::abs(d[1] - d[0]); // Height of p1 above the base. float ht = h1*w, hs = 1 + w; // Height of the conic = ht/hs. - if (ht < (baseWidth*hs) * kFlatnessThreshold) { // i.e. ht/hs < baseWidth * kFlatnessThreshold + // i.e. (ht/hs <= baseWidth * kFlatnessThreshold). Use "<=" in case base == 0. + if (ht <= (baseWidth*hs) * kFlatnessThreshold) { // We are flat. (See rationale in are_collinear.) - this->appendLine(p2); + this->appendLine(p0, p2); return; } - if (w > 1 && h1*hs - ht < baseWidth*hs) { // i.e. w > 1 && h1 - ht/hs < baseWidth + // i.e. (w > 1 && h1 - ht/hs < baseWidth). + if (w > 1 && h1*hs - ht < baseWidth*hs) { // If we get within 1px of p1 when w > 1, we will pick up artifacts from the implicit // function's reflection. Chop at max height (T=.5) and draw a triangle instead. Sk2f p1w = p1*w; Sk2f ab = p0 + p1w; Sk2f bc = p1w + p2; Sk2f highpoint = (ab + bc) / (2*(1 + w)); - this->appendLine(highpoint); - this->appendLine(p2); + this->appendLine(p0, highpoint); + this->appendLine(highpoint, p2); return; } + SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1])); + SkASSERT((p0 != p2).anyTrue()); p1.store(&fPoints.push_back()); p2.store(&fPoints.push_back()); fConicWeights.push_back(w); diff --git a/src/gpu/ccpr/GrCCGeometry.h b/src/gpu/ccpr/GrCCGeometry.h index 96a38e94e7..571b3c0a52 100644 --- a/src/gpu/ccpr/GrCCGeometry.h +++ b/src/gpu/ccpr/GrCCGeometry.h @@ -76,7 +76,7 @@ public: void beginPath(); void beginContour(const SkPoint&); - void lineTo(const SkPoint&); + void lineTo(const SkPoint P[2]); void quadraticTo(const SkPoint[3]); // We pass through inflection points and loop intersections using a line and quadratic(s) @@ -97,7 +97,7 @@ public: PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour. private: - inline void appendLine(const Sk2f& endpt); + inline void appendLine(const Sk2f& p0, const Sk2f& p1); inline void appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2); inline void appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2); diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index 2740569fe6..1629a191a5 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -132,7 +132,7 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts insideContour = false; continue; case SkPath::kLine_Verb: - fGeometry.lineTo(deviceSpacePts[ptsIdx]); + fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]); ++ptsIdx; continue; case SkPath::kQuad_Verb: @@ -340,8 +340,7 @@ static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, i } else { quadPointInstanceData[indices->fWeightedTriangles++].setW( vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, atlasOffset, - // Tessellator has opposite winding sense. - -static_cast(vertices[i].fWinding)); + static_cast(abs(vertices[i].fWinding))); } } } diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index c569e031e2..3bd857d53a 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -2271,7 +2271,9 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, fRequiresCullFaceEnableDisableWhenDrawingLinesAfterNonLines = true; } - if (kIntelSkylake_GrGLRenderer == ctxInfo.renderer()) { + if (kIntelSkylake_GrGLRenderer == ctxInfo.renderer() || + (kANGLE_GrGLRenderer == ctxInfo.renderer() && + GrGLANGLERenderer::kSkylake == ctxInfo.angleRenderer())) { fRequiresFlushBetweenNonAndInstancedDraws = true; } @@ -2319,21 +2321,6 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, fDrawArraysBaseVertexIsBroken = true; } - // Blacklisting CCPR on ANGLE while we investigate http://skbug.com/7805. - if (kANGLE_GrGLRenderer == ctxInfo.renderer()) { - fBlacklistCoverageCounting = true; - } - - // The ccpr vertex-shader implementation does not work on this platform. Only allow CCPR with - // GS. - if (kANGLE_GrGLRenderer == ctxInfo.renderer() && - GrGLANGLERenderer::kSkylake == ctxInfo.angleRenderer()) { - bool gsSupport = fShaderCaps->geometryShaderSupport(); -#if GR_TEST_UTILS - gsSupport &= !contextOptions.fSuppressGeometryShaders; -#endif - fBlacklistCoverageCounting = !gsSupport; - } // Currently the extension is advertised but fb fetch is broken on 500 series Adrenos like the // Galaxy S7. // TODO: Once this is fixed we can update the check here to look at a driver version number too. -- cgit v1.2.3