diff options
author | Chris Dalton <csmartdalton@google.com> | 2018-02-07 13:02:58 -0700 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-02-12 17:06:31 +0000 |
commit | 4138c972effe4eb0227fbb96571df290f3d1979b (patch) | |
tree | f506f42ba68294dac155f09c01f77ae8186e3150 /src/gpu | |
parent | a58bcf7ef8227708dad1501db59bd506e83e5341 (diff) |
ccpr: Tessellate fans for very large and/or simple paths
This increases CPU work, but reduces overdraw on the GPU as compared to
Redbook fanning.
Bug: skia:
Change-Id: I47239c964261e0014a94266a71223eab0597bfb8
Reviewed-on: https://skia-review.googlesource.com/105203
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/GrTessellator.cpp | 1 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 7 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 54 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 34 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 76 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCGeometry.cpp | 2 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCGeometry.h | 18 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.cpp | 285 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.h | 12 |
9 files changed, 353 insertions, 136 deletions
diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp index 2974cf02db..00e5c8a21c 100644 --- a/src/gpu/GrTessellator.cpp +++ b/src/gpu/GrTessellator.cpp @@ -2359,6 +2359,7 @@ int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBou GrTessellator::WindingVertex** verts) { int contourCnt = get_contour_count(path, tolerance); if (contourCnt <= 0) { + *verts = nullptr; return 0; } SkArenaAlloc alloc(kArenaChunkSize); diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index c875625745..2c3166d526 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -66,8 +66,11 @@ int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentBui void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const { - int key = (int)fRenderPass << 1; - if (Impl::kGeometryShader == fImpl) { + int key = (int)fRenderPass << 2; + if (WindMethod::kInstanceData == fWindMethod) { + key |= 2; + } + if (Impl::kVertexShader == fImpl) { key |= 1; } #ifdef SK_DEBUG diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index 1296e089dc..e6adfffe14 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -33,8 +33,9 @@ class GrMesh; */ class GrCCCoverageProcessor : public GrGeometryProcessor { public: - // Defines a single triangle or closed quadratic bezier, with transposed x,y point values. - struct TriangleInstance { + // Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics). + // X,Y point values are transposed. + struct TriPointInstance { float fX[3]; float fY[3]; @@ -42,12 +43,15 @@ public: void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans); }; - // Defines a single closed cubic bezier, with transposed x,y point values. - struct CubicInstance { + // Defines a single primitive shape with 4 input points, or 3 input points plus a W parameter + // duplicated in both 4th components (i.e. Cubics or Triangles with a custom winding number). + // X,Y point values are transposed. + struct QuadPointInstance { float fX[4]; float fY[4]; void set(const SkPoint[4], float dx, float dy); + void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w); }; // All primitive shapes (triangles and closed, convex bezier curves) require more than one @@ -93,24 +97,29 @@ public: caps.shaderCaps()->geometryShaderSupport(); } - GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, const GrCaps& caps) + enum class WindMethod : bool { + kCrossProduct, // Calculate wind = +/-1 by sign of the cross product. + kInstanceData // Instance data provides custom, signed wind values of any magnitude. + // (For tightly-wound tessellated triangles.) + }; + + GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, WindMethod windMethod) : INHERITED(kGrCCCoverageProcessor_ClassID) , fRenderPass(pass) - , fImpl(caps.shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader - : Impl::kVertexShader) { - SkASSERT(DoesRenderPass(pass, caps)); + , fWindMethod(windMethod) + , fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader + : Impl::kVertexShader) { + SkASSERT(DoesRenderPass(pass, *rp->caps())); if (Impl::kGeometryShader == fImpl) { this->initGS(); } else { - this->initVS(rp, caps); + this->initVS(rp); } } // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array - // of either TriangleInstance or CubicInstance, depending on this processor's RendererPass, with - // coordinates in the desired shape's final atlas-space position. - // - // NOTE: Quadratics use TriangleInstance since both have 3 points. + // of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass, + // with coordinates in the desired shape's final atlas-space position. void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, SkTArray<GrMesh>* out) { if (Impl::kGeometryShader == fImpl) { @@ -227,7 +236,7 @@ private: }; void initGS(); - void initVS(GrResourceProvider*, const GrCaps&); + void initVS(GrResourceProvider*); void appendGSMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, SkTArray<GrMesh>* out) const; @@ -238,6 +247,7 @@ private: GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const; const RenderPass fRenderPass; + const WindMethod fWindMethod; const Impl fImpl; SkDEBUGCODE(float fDebugBloat = 0); @@ -250,11 +260,11 @@ private: typedef GrGeometryProcessor INHERITED; }; -inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint p[3], const Sk2f& trans) { +inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) { this->set(p[0], p[1], p[2], trans); } -inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint& p0, const SkPoint& p1, +inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, const Sk2f& trans) { Sk2f P0 = Sk2f::Load(&p0) + trans; Sk2f P1 = Sk2f::Load(&p1) + trans; @@ -262,13 +272,23 @@ inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint& p0, cons Sk2f::Store3(this, P0, P1, P2); } -inline void GrCCCoverageProcessor::CubicInstance::set(const SkPoint p[4], float dx, float dy) { +inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint p[4], float dx, float dy) { Sk4f X,Y; Sk4f::Load2(p, &X, &Y); (X + dx).store(&fX); (Y + dy).store(&fY); } +inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, const SkPoint& p1, + const SkPoint& p2, const Sk2f& trans, + float w) { + Sk2f P0 = Sk2f::Load(&p0) + trans; + Sk2f P1 = Sk2f::Load(&p1) + trans; + Sk2f P2 = Sk2f::Load(&p2) + trans; + Sk2f W = Sk2f(w); + Sk2f::Store4(this, P0, P1, P2, W); +} + inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) { switch (pass) { case RenderPass::kTriangleHulls: diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index 0754389c0e..224533b298 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -31,7 +31,7 @@ protected: // The vertex shader simply forwards transposed x or y values to the geometry shader. SkASSERT(1 == proc.numAttribs()); - gpArgs->fPositionVar.set(4 == proc.numInputPoints() ? kFloat4_GrSLType : kFloat3_GrSLType, + gpArgs->fPositionVar.set(GrVertexAttribTypeToSLType(proc.getAttrib(0).fType), proc.getAttrib(0).fName); // Geometry shader. @@ -57,11 +57,20 @@ protected: GrShaderVar wind("wind", kHalf_GrSLType); g->declareGlobal(wind); - 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]));"); + if (WindMethod::kCrossProduct == proc.fWindMethod) { + 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 { + SkASSERT(WindMethod::kInstanceData == proc.fWindMethod); + 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 = sign(area_x2);", wind.c_str()); SkString emitVertexFn; SkSTArray<2, GrShaderVar> emitArgs; @@ -322,12 +331,17 @@ private: void GrCCCoverageProcessor::initGS() { SkASSERT(Impl::kGeometryShader == fImpl); - if (RenderPassIsCubic(fRenderPass)) { - this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); // (See appendMesh.) - SkASSERT(sizeof(CubicInstance) == this->getVertexStride() * 2); + if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { + SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); + this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); + SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2); + SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride()); + GR_STATIC_ASSERT(0 == offsetof(QuadPointInstance, fX)); } else { - this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); // (See appendMesh.) - SkASSERT(sizeof(TriangleInstance) == this->getVertexStride() * 2); + this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); + SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2); + SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride()); + GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX)); } this->setWillUseGeoShader(); } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index 8f2884ae75..4ac6443322 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -35,15 +35,25 @@ protected: GrGLSLVertexBuilder* v = args.fVertBuilder; int numInputPoints = proc.numInputPoints(); - v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s, %s));", - numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, - proc.getAttrib(kAttribIdx_Y).fName); - - 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]));"); + const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz"; + v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));", + numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle, + proc.getAttrib(kAttribIdx_Y).fName, swizzle); + + if (WindMethod::kCrossProduct == proc.fWindMethod) { + 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 { + SkASSERT(WindMethod::kInstanceData == proc.fWindMethod); + SkASSERT(3 == numInputPoints); + SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType); + v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName); } - v->codeAppend ("half wind = sign(area_x2);"); float bloat = kAABloatRadius; #ifdef SK_DEBUG @@ -340,17 +350,9 @@ public: } }; -void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) { +void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { SkASSERT(Impl::kVertexShader == fImpl); - - GrVertexAttribType inputPtsType = RenderPassIsCubic(fRenderPass) ? - kFloat4_GrVertexAttribType : kFloat3_GrVertexAttribType; - - SkASSERT(kAttribIdx_X == this->numAttribs()); - this->addInstanceAttrib("X", inputPtsType); - - SkASSERT(kAttribIdx_Y == this->numAttribs()); - this->addInstanceAttrib("Y", inputPtsType); + const GrCaps& caps = *rp->caps(); switch (fRenderPass) { case RenderPass::kTriangleHulls: { @@ -373,8 +375,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) { gHull3AndEdgeIndexBufferKey); fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris); } - SkASSERT(kAttribIdx_VertexData == this->numAttribs()); - this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType); break; } case RenderPass::kQuadraticHulls: @@ -396,8 +396,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) { gHull4IndexBufferKey); fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris); } - SkASSERT(kAttribIdx_VertexData == this->numAttribs()); - this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType); break; } case RenderPass::kTriangleEdges: @@ -427,20 +425,36 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) { } } -#ifdef SK_DEBUG - if (RenderPassIsCubic(fRenderPass)) { - SkASSERT(offsetof(CubicInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord); - SkASSERT(offsetof(CubicInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord); - SkASSERT(sizeof(CubicInstance) == this->getInstanceStride()); + if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { + SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); + + SkASSERT(kAttribIdx_X == this->numAttribs()); + this->addInstanceAttrib("X", kFloat4_GrVertexAttribType); + + SkASSERT(kAttribIdx_Y == this->numAttribs()); + this->addInstanceAttrib("Y", kFloat4_GrVertexAttribType); + + SkASSERT(offsetof(QuadPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord); + SkASSERT(offsetof(QuadPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord); + SkASSERT(sizeof(QuadPointInstance) == this->getInstanceStride()); } else { - SkASSERT(offsetof(TriangleInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord); - SkASSERT(offsetof(TriangleInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord); - SkASSERT(sizeof(TriangleInstance) == this->getInstanceStride()); + SkASSERT(kAttribIdx_X == this->numAttribs()); + this->addInstanceAttrib("X", kFloat3_GrVertexAttribType); + + SkASSERT(kAttribIdx_Y == this->numAttribs()); + this->addInstanceAttrib("Y", kFloat3_GrVertexAttribType); + + SkASSERT(offsetof(TriPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord); + SkASSERT(offsetof(TriPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord); + SkASSERT(sizeof(TriPointInstance) == this->getInstanceStride()); } + if (fVertexBuffer) { + SkASSERT(kAttribIdx_VertexData == this->numAttribs()); + this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType); + SkASSERT(sizeof(int32_t) == this->getVertexStride()); } -#endif if (caps.usePrimitiveRestart()) { this->setWillUsePrimitiveRestart(); diff --git a/src/gpu/ccpr/GrCCGeometry.cpp b/src/gpu/ccpr/GrCCGeometry.cpp index 19bc8747b7..481f4e4725 100644 --- a/src/gpu/ccpr/GrCCGeometry.cpp +++ b/src/gpu/ccpr/GrCCGeometry.cpp @@ -30,7 +30,7 @@ void GrCCGeometry::beginContour(const SkPoint& devPt) { // Store the current verb count in the fTriangles field for now. When we close the contour we // will use this value to calculate the actual number of triangles in its fan. - fCurrContourTallies = {fVerbs.count(), 0, 0}; + fCurrContourTallies = {fVerbs.count(), 0, 0, 0}; fPoints.push_back(devPt); fVerbs.push_back(Verb::kBeginContour); diff --git a/src/gpu/ccpr/GrCCGeometry.h b/src/gpu/ccpr/GrCCGeometry.h index b50dd015eb..47b57c1c72 100644 --- a/src/gpu/ccpr/GrCCGeometry.h +++ b/src/gpu/ccpr/GrCCGeometry.h @@ -35,9 +35,10 @@ public: kEndOpenContour // endPt != startPt. }; - // These tallies track numbers of CCPR primitives are required to draw a contour. + // These tallies track numbers of CCPR primitives that are required to draw a contour. struct PrimitiveTallies { int fTriangles; // Number of triangles in the contour's fan. + int fWoundTriangles; // Triangles (from the tessellator) whose winding magnitude > 1. int fQuadratics; int fCubics; @@ -115,11 +116,11 @@ private: int maxSubdivisions = kMaxSubdivionsPerCubicSection); // Transient state used while building a contour. - SkPoint fCurrAnchorPoint; - SkPoint fCurrFanPoint; - PrimitiveTallies fCurrContourTallies; - SkCubicType fCurrCubicType; - SkDEBUGCODE(bool fBuildingContour = false); + SkPoint fCurrAnchorPoint; + SkPoint fCurrFanPoint; + PrimitiveTallies fCurrContourTallies; + SkCubicType fCurrCubicType; + SkDEBUGCODE(bool fBuildingContour = false); // TODO: These points could eventually be written directly to block-allocated GPU buffers. SkSTArray<128, SkPoint, true> fPoints; @@ -128,6 +129,7 @@ private: inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) { fTriangles += b.fTriangles; + fWoundTriangles += b.fWoundTriangles; fQuadratics += b.fQuadratics; fCubics += b.fCubics; } @@ -135,12 +137,14 @@ inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b GrCCGeometry::PrimitiveTallies inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const { return {fTriangles - b.fTriangles, + fWoundTriangles - b.fWoundTriangles, fQuadratics - b.fQuadratics, fCubics - b.fCubics}; } inline bool GrCCGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) { - return fTriangles == b.fTriangles && fQuadratics == b.fQuadratics && fCubics == b.fCubics; + return fTriangles == b.fTriangles && fWoundTriangles == b.fWoundTriangles && + fQuadratics == b.fQuadratics && fCubics == b.fCubics; } #endif diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index 2a632d3bda..f729290187 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -17,8 +17,8 @@ #include "SkPoint.h" #include "ccpr/GrCCGeometry.h" -using TriangleInstance = GrCCCoverageProcessor::TriangleInstance; -using CubicInstance = GrCCCoverageProcessor::CubicInstance; +using TriPointInstance = GrCCCoverageProcessor::TriPointInstance; +using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance; GrCCPathParser::GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPoints, int numSkVerbs) @@ -32,7 +32,8 @@ GrCCPathParser::GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPo // that "end" at the beginning of the data. These will not be drawn, but will only be be read by // the first actual batch. fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()}; - fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count()}; + fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), + PrimitiveTallies()}; } void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds, @@ -105,6 +106,7 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts fCurrPathPointsIdx = fGeometry.points().count(); fCurrPathVerbsIdx = fGeometry.verbs().count(); fCurrPathPrimitiveCounts = PrimitiveTallies(); + fCurrPathFillType = path.getFillType(); fGeometry.beginPath(); @@ -160,7 +162,81 @@ void GrCCPathParser::saveParsedPath(ScissorMode scissorMode, const SkIRect& clip int16_t atlasOffsetX, int16_t atlasOffsetY) { SkASSERT(fParsingPath); - fPathsInfo.push_back() = {scissorMode, atlasOffsetX, atlasOffsetY}; + fPathsInfo.emplace_back(scissorMode, atlasOffsetX, atlasOffsetY); + + // Tessellate fans from very large and/or simple paths, in order to reduce overdraw. + int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 1; + int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N. + int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width(); + if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100. + fCurrPathPrimitiveCounts.fTriangles = + fCurrPathPrimitiveCounts.fWoundTriangles = 0; + + const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs(); + const SkTArray<SkPoint, true>& pts = fGeometry.points(); + int ptsIdx = fCurrPathPointsIdx; + + // Build an SkPath of the Redbook fan. + SkPath fan; + fan.setFillType(fCurrPathFillType); + SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]); + for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) { + switch (verbs[i]) { + case GrCCGeometry::Verb::kBeginPath: + SK_ABORT("Invalid GrCCGeometry"); + continue; + + case GrCCGeometry::Verb::kBeginContour: + fan.moveTo(pts[ptsIdx++]); + continue; + + case GrCCGeometry::Verb::kLineTo: + fan.lineTo(pts[ptsIdx++]); + continue; + + case GrCCGeometry::Verb::kMonotonicQuadraticTo: + fan.lineTo(pts[ptsIdx + 1]); + ptsIdx += 2; + continue; + + case GrCCGeometry::Verb::kMonotonicCubicTo: + fan.lineTo(pts[ptsIdx + 2]); + ptsIdx += 3; + continue; + + case GrCCGeometry::Verb::kEndClosedContour: + case GrCCGeometry::Verb::kEndOpenContour: + fan.close(); + continue; + } + } + GrTessellator::WindingVertex* vertices = nullptr; + int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(), + SkRect::Make(clippedDevIBounds), &vertices); + SkASSERT(0 == count % 3); + for (int i = 0; i < count; i += 3) { + SkASSERT(vertices[i].fWinding == vertices[i + 1].fWinding); + SkASSERT(vertices[i].fWinding == vertices[i + 2].fWinding); + if (1 == abs(vertices[i].fWinding)) { + // Ensure this triangle's points actually wind in the same direction as fWinding. + float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX; + float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY; + float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX; + float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY; + float wind = ay*bx - ax*by; + if ((wind > 0) != (vertices[i].fWinding > 0)) { + std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos); + } + ++fCurrPathPrimitiveCounts.fTriangles; + } else { + ++fCurrPathPrimitiveCounts.fWoundTriangles; + } + } + + fPathsInfo.back().fFanTessellation.reset(vertices); + fPathsInfo.back().fFanTessellationCount = count; + } + fTotalPrimitiveCounts[(int)scissorMode] += fCurrPathPrimitiveCounts; if (ScissorMode::kScissored == scissorMode) { @@ -180,15 +256,23 @@ void GrCCPathParser::discardParsedPath() { GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() { SkASSERT(!fInstanceBuffer); SkASSERT(!fCoverageCountBatches.empty()); + const auto& lastBatch = fCoverageCountBatches.back(); + const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1]; - int maxMeshes = 1 + fScissorSubBatches.count() - - fCoverageCountBatches.back().fEndScissorSubBatchIdx; - fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes); + PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored] - + lastBatch.fEndNonScissorIndices; + batchTotalCounts += fTotalPrimitiveCounts[(int)ScissorMode::kScissored] - + lastScissorSubBatch.fEndPrimitiveIndices; fCoverageCountBatches.push_back() = { fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored], - fScissorSubBatches.count() + fScissorSubBatches.count(), + batchTotalCounts }; + + int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx; + fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes); + return fCoverageCountBatches.count() - 1; } @@ -205,10 +289,10 @@ GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() { // elements past the end for this method to use as scratch space. // // Returns the next triangle instance after the final one emitted. -static TriangleInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts, +static TriPointInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts, SkTArray<int32_t, true>& indices, int firstIndex, int indexCount, const Sk2f& atlasOffset, - TriangleInstance out[]) { + TriPointInstance out[]) { if (indexCount < 3) { return out; } @@ -232,6 +316,22 @@ static TriangleInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts, return out; } +static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, int numVertices, + const Sk2f& atlasOffset, TriPointInstance* triPointInstanceData, + QuadPointInstance* quadPointInstanceData, + GrCCGeometry::PrimitiveTallies* indices) { + for (int i = 0; i < numVertices; i += 3) { + if (1 == abs(vertices[i].fWinding)) { + triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos, + vertices[i + 2].fPos, atlasOffset); + } else { + quadPointInstanceData[indices->fWoundTriangles++].set( + vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, atlasOffset, + static_cast<float>(vertices[i].fWinding)); + } + } +} + bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath(). SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch(). @@ -250,7 +350,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { // // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is // straightforward. Start with triangles and quadratics. They both view the instance buffer as - // an array of TriangleInstance[], so we can begin at zero and lay them out one after the other. + // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other. fBaseInstances[0].fTriangles = 0; fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles + fTotalPrimitiveCounts[0].fTriangles; @@ -260,85 +360,112 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { fTotalPrimitiveCounts[0].fQuadratics; int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics; - // Cubics view the same instance buffer as an array of CubicInstance[]. So, reinterpreting the - // instance data as CubicInstance[], we start them on the first index that will not overwrite - // previous TriangleInstance data. - int cubicBaseIdx = - GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriangleInstance), sizeof(CubicInstance)); - fBaseInstances[0].fCubics = cubicBaseIdx; + // Wound triangles and cubics both view the same instance buffer as an array of + // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start + // them on the first index that will not overwrite previous TriPointInstance data. + int quadBaseIdx = + GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance)); + fBaseInstances[0].fWoundTriangles = quadBaseIdx; + fBaseInstances[1].fWoundTriangles = fBaseInstances[0].fWoundTriangles + + fTotalPrimitiveCounts[0].fWoundTriangles; + fBaseInstances[0].fCubics = fBaseInstances[1].fWoundTriangles + + fTotalPrimitiveCounts[1].fWoundTriangles; fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics; - int cubicEndIdx = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics; + int quadEndIdx = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics; fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType, - cubicEndIdx * sizeof(CubicInstance)); + quadEndIdx * sizeof(QuadPointInstance)); if (!fInstanceBuffer) { return false; } - TriangleInstance* triangleInstanceData = static_cast<TriangleInstance*>(fInstanceBuffer->map()); - CubicInstance* cubicInstanceData = reinterpret_cast<CubicInstance*>(triangleInstanceData); - SkASSERT(cubicInstanceData); + TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map()); + QuadPointInstance* quadPointInstanceData = + reinterpret_cast<QuadPointInstance*>(triPointInstanceData); + SkASSERT(quadPointInstanceData); - PathInfo* currPathInfo = fPathsInfo.begin(); + PathInfo* nextPathInfo = fPathsInfo.begin(); float atlasOffsetX = 0.0, atlasOffsetY = 0.0; Sk2f atlasOffset; - int ptsIdx = -1; PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]}; PrimitiveTallies* currIndices = nullptr; SkSTArray<256, int32_t, true> currFan; + bool currFanIsTessellated = false; const SkTArray<SkPoint, true>& pts = fGeometry.points(); + int ptsIdx = -1; // Expand the ccpr verbs into GPU instance buffers. for (GrCCGeometry::Verb verb : fGeometry.verbs()) { switch (verb) { case GrCCGeometry::Verb::kBeginPath: SkASSERT(currFan.empty()); - currIndices = &instanceIndices[(int)currPathInfo->fScissorMode]; - atlasOffsetX = static_cast<float>(currPathInfo->fAtlasOffsetX); - atlasOffsetY = static_cast<float>(currPathInfo->fAtlasOffsetY); + currIndices = &instanceIndices[(int)nextPathInfo->fScissorMode]; + atlasOffsetX = static_cast<float>(nextPathInfo->fAtlasOffsetX); + atlasOffsetY = static_cast<float>(nextPathInfo->fAtlasOffsetY); atlasOffset = {atlasOffsetX, atlasOffsetY}; - ++currPathInfo; + currFanIsTessellated = nextPathInfo->fFanTessellation.get(); + if (currFanIsTessellated) { + emit_tessellated_fan(nextPathInfo->fFanTessellation.get(), + nextPathInfo->fFanTessellationCount, atlasOffset, + triPointInstanceData, quadPointInstanceData, currIndices); + } + ++nextPathInfo; continue; case GrCCGeometry::Verb::kBeginContour: SkASSERT(currFan.empty()); - currFan.push_back(++ptsIdx); + ++ptsIdx; + if (!currFanIsTessellated) { + currFan.push_back(ptsIdx); + } continue; case GrCCGeometry::Verb::kLineTo: - SkASSERT(!currFan.empty()); - currFan.push_back(++ptsIdx); + ++ptsIdx; + if (!currFanIsTessellated) { + SkASSERT(!currFan.empty()); + currFan.push_back(ptsIdx); + } continue; case GrCCGeometry::Verb::kMonotonicQuadraticTo: - SkASSERT(!currFan.empty()); - triangleInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset); - currFan.push_back(ptsIdx += 2); + triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset); + ptsIdx += 2; + if (!currFanIsTessellated) { + SkASSERT(!currFan.empty()); + currFan.push_back(ptsIdx); + } continue; case GrCCGeometry::Verb::kMonotonicCubicTo: - SkASSERT(!currFan.empty()); - cubicInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX, - atlasOffsetY); - currFan.push_back(ptsIdx += 3); + quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX, + atlasOffsetY); + ptsIdx += 3; + if (!currFanIsTessellated) { + SkASSERT(!currFan.empty()); + currFan.push_back(ptsIdx); + } continue; case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt. - SkASSERT(!currFan.empty()); - currFan.pop_back(); + if (!currFanIsTessellated) { + SkASSERT(!currFan.empty()); + currFan.pop_back(); + } // fallthru. case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt. - if (currFan.count() >= 3) { + SkASSERT(!currFanIsTessellated || currFan.empty()); + if (!currFanIsTessellated && currFan.count() >= 3) { int fanSize = currFan.count(); // Reserve space for emit_recursive_fan. Technically this can grow to // fanSize + log3(fanSize), but we approximate with log2. currFan.push_back_n(SkNextLog2(fanSize)); - SkDEBUGCODE(TriangleInstance* end =) + SkDEBUGCODE(TriPointInstance* end =) emit_recursive_fan(pts, currFan, 0, fanSize, atlasOffset, - triangleInstanceData + currIndices->fTriangles); + triPointInstanceData + currIndices->fTriangles); currIndices->fTriangles += fanSize - 2; - SkASSERT(triangleInstanceData + currIndices->fTriangles == end); + SkASSERT(triPointInstanceData + currIndices->fTriangles == end); } currFan.reset(); continue; @@ -347,14 +474,16 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { fInstanceBuffer->unmap(); - SkASSERT(currPathInfo == fPathsInfo.end()); + SkASSERT(nextPathInfo == fPathsInfo.end()); SkASSERT(ptsIdx == pts.count() - 1); SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles); SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics); SkASSERT(instanceIndices[0].fQuadratics == fBaseInstances[1].fQuadratics); SkASSERT(instanceIndices[1].fQuadratics == triEndIdx); + SkASSERT(instanceIndices[0].fWoundTriangles == fBaseInstances[1].fWoundTriangles); + SkASSERT(instanceIndices[1].fWoundTriangles == fBaseInstances[0].fCubics); SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics); - SkASSERT(instanceIndices[1].fCubics == cubicEndIdx); + SkASSERT(instanceIndices[1].fCubics == quadEndIdx); fMeshesScratchBuffer.reserve(fMaxMeshesPerDraw); fDynamicStatesScratchBuffer.reserve(fMaxMeshesPerDraw); @@ -365,36 +494,56 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID, const SkIRect& drawBounds) const { using RenderPass = GrCCCoverageProcessor::RenderPass; + using WindMethod = GrCCCoverageProcessor::WindMethod; SkASSERT(fInstanceBuffer); + const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts; + GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrPipeline::ScissorState::kEnabled, SkBlendMode::kPlus); - // Triangles. - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls, - &PrimitiveTallies::fTriangles, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges, - &PrimitiveTallies::fTriangles, drawBounds); // Might get skipped. - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, - &PrimitiveTallies::fTriangles, drawBounds); - - // Quadratics. - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls, - &PrimitiveTallies::fQuadratics, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners, - &PrimitiveTallies::fQuadratics, drawBounds); - - // Cubics. - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicHulls, - &PrimitiveTallies::fCubics, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners, - &PrimitiveTallies::fCubics, drawBounds); + if (batchTotalCounts.fTriangles) { + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls, + WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges, + WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, + drawBounds); // Might get skipped. + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, + WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); + } + + if (batchTotalCounts.fWoundTriangles) { + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls, + WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, + drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges, + WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, + drawBounds); // Might get skipped. + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, + WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, + drawBounds); + } + + if (batchTotalCounts.fQuadratics) { + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls, + WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners, + WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds); + } + + if (batchTotalCounts.fCubics) { + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicHulls, + WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners, + WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); + } } void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline, CoverageCountBatchID batchID, GrCCCoverageProcessor::RenderPass renderPass, + GrCCCoverageProcessor::WindMethod windMethod, int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const { SkASSERT(pipeline.getScissorState().enabled()); @@ -407,12 +556,13 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count()); fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count()); - GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, flushState->caps()); + GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod); SkASSERT(batchID > 0); SkASSERT(batchID < fCoverageCountBatches.count()); const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1]; const CoverageCountBatch& batch = fCoverageCountBatches[batchID]; + SkDEBUGCODE(int totalInstanceCount = 0); if (int instanceCount = batch.fEndNonScissorIndices.*instanceType - previousBatch.fEndNonScissorIndices.*instanceType) { @@ -422,6 +572,7 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance, &fMeshesScratchBuffer); fDynamicStatesScratchBuffer.push_back().fScissorRect.setXYWH(0, 0, drawBounds.width(), drawBounds.height()); + SkDEBUGCODE(totalInstanceCount += instanceCount); } SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0); @@ -439,10 +590,12 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseScissorInstance + startIndex, &fMeshesScratchBuffer); fDynamicStatesScratchBuffer.push_back().fScissorRect = scissorSubBatch.fScissor; + SkDEBUGCODE(totalInstanceCount += instanceCount); } SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count()); SkASSERT(fMeshesScratchBuffer.count() <= fMaxMeshesPerDraw); + SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType); if (!fMeshesScratchBuffer.empty()) { SkASSERT(flushState->rtCommandBuffer()); diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h index aff6b1069d..f041610521 100644 --- a/src/gpu/ccpr/GrCCPathParser.h +++ b/src/gpu/ccpr/GrCCPathParser.h @@ -10,6 +10,7 @@ #include "GrMesh.h" #include "GrNonAtomicRef.h" +#include "GrTessellator.h" #include "SkRect.h" #include "SkRefCnt.h" #include "ccpr/GrCCCoverageProcessor.h" @@ -76,8 +77,13 @@ private: // Every kBeginPath verb has a corresponding PathInfo entry. struct PathInfo { + PathInfo(ScissorMode scissorMode, int16_t offsetX, int16_t offsetY) + : fScissorMode(scissorMode), fAtlasOffsetX(offsetX), fAtlasOffsetY(offsetY) {} + ScissorMode fScissorMode; int16_t fAtlasOffsetX, fAtlasOffsetY; + std::unique_ptr<GrTessellator::WindingVertex[]> fFanTessellation; + int fFanTessellationCount = 0; }; // Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous @@ -85,6 +91,7 @@ private: struct CoverageCountBatch { PrimitiveTallies fEndNonScissorIndices; int fEndScissorSubBatchIdx; + PrimitiveTallies fTotalPrimitiveCounts; }; // Defines a sub-batch from CoverageCountBatch that will be drawn with the given scissor rect. @@ -98,8 +105,8 @@ private: void endContourIfNeeded(bool insideContour); void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID, - GrCCCoverageProcessor::RenderPass, int PrimitiveTallies::*instanceType, - const SkIRect& drawBounds) const; + GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod, + int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const; // Staging area for the path being parsed. SkDEBUGCODE(int fParsingPath = false); @@ -107,6 +114,7 @@ private: int fCurrPathPointsIdx; int fCurrPathVerbsIdx; PrimitiveTallies fCurrPathPrimitiveCounts; + SkPath::FillType fCurrPathFillType; GrCCGeometry fGeometry; SkSTArray<32, PathInfo, true> fPathsInfo; |