From baf3e78092243210ca4489e51134df6fc8a90eee Mon Sep 17 00:00:00 2001 From: Chris Dalton <csmartdalton@google.com> Date: Thu, 8 Mar 2018 15:55:58 +0000 Subject: [PATCH] Revert "ccpr: Draw curves in a single pass" This reverts commit df04ce29490a24f9d5b4f5caafd8f6a3368a19e0. Reason for revert: Going to revisit AAA quality Original change's description: > ccpr: Draw curves in a single pass > > Throws out the complicated MSAA curve corner shaders, and instead just > ramps coverage to zero at bloat vertices that fall outside the curve. > > Updates SampleCCPRGeometry to better visualize this new geometry by > clearing to black and drawing with SkBlendMode::kPlus. > > Bug: skia: > Change-Id: Ibe86cbc741d8b015127b10dd43e3b52e7cb35732 > Reviewed-on: https://skia-review.googlesource.com/112626 > Commit-Queue: Chris Dalton <csmartdalton@google.com> > Reviewed-by: Brian Salomon <bsalomon@google.com> TBR=bsalomon@google.com,csmartdalton@google.com Change-Id: I014baa60b248d870717f5ee8794e0bed66da86e6 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: skia: Reviewed-on: https://skia-review.googlesource.com/113181 Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com> --- samplecode/SampleCCPRGeometry.cpp | 33 +-- src/gpu/ccpr/GrCCCoverageProcessor.cpp | 119 ++++++---- src/gpu/ccpr/GrCCCoverageProcessor.h | 45 +++- src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 89 +++----- src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 203 +++++++++--------- src/gpu/ccpr/GrCCCubicShader.cpp | 118 ++++++++-- src/gpu/ccpr/GrCCCubicShader.h | 30 ++- src/gpu/ccpr/GrCCPathParser.cpp | 4 + src/gpu/ccpr/GrCCQuadraticShader.cpp | 120 ++++++++--- src/gpu/ccpr/GrCCQuadraticShader.h | 43 +++- src/gpu/ccpr/GrCCTriangleShader.cpp | 6 +- src/gpu/ccpr/GrCCTriangleShader.h | 6 +- 12 files changed, 528 insertions(+), 288 deletions(-) diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp index 2a7d6640a6..a90ece09a1 100644 --- a/samplecode/SampleCCPRGeometry.cpp +++ b/samplecode/SampleCCPRGeometry.cpp @@ -32,6 +32,10 @@ using RenderPass = GrCCCoverageProcessor::RenderPass; static constexpr float kDebugBloat = 40; +static int is_quadratic(RenderPass pass) { + return pass == RenderPass::kQuadratics || pass == RenderPass::kQuadraticCorners; +} + /** * This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It * increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green, @@ -115,13 +119,14 @@ static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3] } void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { - canvas->clear(SK_ColorBLACK); + SkAutoCanvasRestore acr(canvas, true); + canvas->setMatrix(SkMatrix::I()); SkPath outline; outline.moveTo(fPoints[0]); - if (RenderPass::kCubics == fRenderPass) { + if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]); - } else if (RenderPass::kQuadratics == fRenderPass) { + } else if (is_quadratic(fRenderPass)) { outline.quadTo(fPoints[1], fPoints[3]); } else { outline.lineTo(fPoints[1]); @@ -130,7 +135,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { } SkPaint outlinePaint; - outlinePaint.setColor(0x80ffffff); + outlinePaint.setColor(0x30000000); outlinePaint.setStyle(SkPaint::kStroke_Style); outlinePaint.setStrokeWidth(0); outlinePaint.setAntiAlias(true); @@ -154,7 +159,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) { rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this)); caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass)); - if (RenderPass::kCubics == fRenderPass) { + if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { caption.appendf(" (%s)", SkCubicTypeName(fCubicType)); } } else { @@ -166,7 +171,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { pointsPaint.setStrokeWidth(8); pointsPaint.setAntiAlias(true); - if (RenderPass::kCubics == fRenderPass) { + if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { int w = this->width(), h = this->height(); canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint); draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW); @@ -179,7 +184,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { SkPaint captionPaint; captionPaint.setTextSize(20); - captionPaint.setColor(SK_ColorWHITE); + captionPaint.setColor(SK_ColorBLACK); captionPaint.setAntiAlias(true); canvas->drawText(caption.c_str(), caption.size(), 10, 30, captionPaint); } @@ -188,7 +193,7 @@ void CCPRGeometryView::updateGpuData() { fTriPointInstances.reset(); fQuadPointInstances.reset(); - if (RenderPass::kCubics == fRenderPass) { + if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { double t[2], s[2]; fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s); GrCCGeometry geometry; @@ -212,7 +217,7 @@ void CCPRGeometryView::updateGpuData() { continue; } } - } else if (RenderPass::kQuadratics == fRenderPass) { + } else if (is_quadratic(fRenderPass)) { GrCCGeometry geometry; geometry.beginContour(fPoints[0]); geometry.quadraticTo(fPoints[1], fPoints[3]); @@ -249,7 +254,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) { SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat)); SkSTArray<1, GrMesh> mesh; - if (RenderPass::kCubics == fView->fRenderPass) { + if (GrCCCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) { sk_sp<GrBuffer> instBuff(rp->createBuffer( fView->fQuadPointInstances.count() * sizeof(QuadPointInstance), kVertex_GrBufferType, kDynamic_GrAccessPattern, @@ -270,11 +275,11 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) { } GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled, - SkBlendMode::kPlus); + SkBlendMode::kSrcOver); if (glGpu) { glGpu->handleDirtyContext(); - // GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE)); + GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE)); GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH)); } @@ -313,7 +318,7 @@ private: SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) { for (int i = 0; i < 4; ++i) { - if (RenderPass::kCubics != fRenderPass && 2 == i) { + if (!GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass) && 2 == i) { continue; } if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) { @@ -337,7 +342,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) { } SkUnichar unichar; if (SampleCode::CharQ(*evt, &unichar)) { - if (unichar >= '1' && unichar <= '4') { + if (unichar >= '1' && unichar <= '6') { fRenderPass = RenderPass(unichar - '1'); this->updateAndInval(); return true; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index 76ca8f562e..686ab5514d 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -15,59 +15,35 @@ #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLVertexGeoBuilder.h" -void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, - GrProcessorKeyBuilder* b) const { - int key = (int)fRenderPass << 2; - if (WindMethod::kInstanceData == fWindMethod) { - key |= 2; - } - if (Impl::kVertexShader == fImpl) { - key |= 1; - } -#ifdef SK_DEBUG - uint32_t bloatBits; - memcpy(&bloatBits, &fDebugBloat, 4); - b->add32(bloatBits); -#endif - b->add32(key); -} - -GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const { - std::unique_ptr<Shader> shader; - switch (fRenderPass) { - case RenderPass::kTriangles: - shader = skstd::make_unique<GrCCTriangleShader>(); - break; - case RenderPass::kTriangleCorners: - shader = skstd::make_unique<GrCCTriangleCornerShader>(); - break; - case RenderPass::kQuadratics: - shader = skstd::make_unique<GrCCQuadraticShader>(); - break; - case RenderPass::kCubics: - shader = skstd::make_unique<GrCCCubicShader>(); - break; - } - return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader)) - : this->createVSImpl(std::move(shader)); -} - void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc, GrGLSLFPFragmentBuilder* f, const char* skOutputColor, const char* skOutputCoverage) const { f->codeAppendf("half coverage = 0;"); - this->onEmitFragmentCode(proc, f, "coverage"); + this->onEmitFragmentCode(f, "coverage"); f->codeAppendf("%s.a = coverage;", skOutputColor); f->codeAppendf("%s = half4(1);", skOutputCoverage); #ifdef SK_DEBUG if (proc.debugVisualizationsEnabled()) { - f->codeAppendf("%s = half4(-%s.a, %s.a, 0, abs(%s.a));", - skOutputColor, skOutputColor, skOutputColor, skOutputColor); + f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);", + skOutputColor, skOutputColor, skOutputColor); } #endif } +void GrCCCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder* s, + const char* leftPt, + const char* rightPt, + const char* outputDistanceEquation) { + s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);", + rightPt, leftPt, leftPt, rightPt); + s->codeAppend ("float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);"); + // When nwidth=0, wind must also be 0 (and coverage * wind = 0). So it doesn't matter what we + // come up with here as long as it isn't NaN or Inf. + s->codeAppend ("n /= (0 != nwidth) ? nwidth : 1;"); + s->codeAppendf("%s = float3(-n, dot(n, %s) - .5);", outputDistanceEquation, leftPt); +} + void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s, const char* leftPt, const char* rightPt, @@ -102,3 +78,66 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGe // GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0. s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage); } + +int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f, + const char* samplesName) { + // Standard DX11 sample locations. +#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS) + f->defineConstant("float2[8]", samplesName, "float2[8](" + "float2(+1, -3)/16, float2(-1, +3)/16, float2(+5, +1)/16, float2(-3, -5)/16, " + "float2(-5, +5)/16, float2(-7, -1)/16, float2(+3, +7)/16, float2(+7, -7)/16." + ")"); + return 8; +#else + f->defineConstant("float2[16]", samplesName, "float2[16](" + "float2(+1, +1)/16, float2(-1, -3)/16, float2(-3, +2)/16, float2(+4, -1)/16, " + "float2(-5, -2)/16, float2(+2, +5)/16, float2(+5, +3)/16, float2(+3, -5)/16, " + "float2(-2, +6)/16, float2( 0, -7)/16, float2(-4, -6)/16, float2(-6, +4)/16, " + "float2(-8, 0)/16, float2(+7, -4)/16, float2(+6, +7)/16, float2(-7, -8)/16." + ")"); + return 16; +#endif +} + +void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, + GrProcessorKeyBuilder* b) const { + int key = (int)fRenderPass << 2; + if (WindMethod::kInstanceData == fWindMethod) { + key |= 2; + } + if (Impl::kVertexShader == fImpl) { + key |= 1; + } +#ifdef SK_DEBUG + uint32_t bloatBits; + memcpy(&bloatBits, &fDebugBloat, 4); + b->add32(bloatBits); +#endif + b->add32(key); +} + +GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const { + std::unique_ptr<Shader> shader; + switch (fRenderPass) { + case RenderPass::kTriangles: + shader = skstd::make_unique<GrCCTriangleShader>(); + break; + case RenderPass::kTriangleCorners: + shader = skstd::make_unique<GrCCTriangleCornerShader>(); + break; + case RenderPass::kQuadratics: + shader = skstd::make_unique<GrCCQuadraticHullShader>(); + break; + case RenderPass::kQuadraticCorners: + shader = skstd::make_unique<GrCCQuadraticCornerShader>(); + break; + case RenderPass::kCubics: + shader = skstd::make_unique<GrCCCubicHullShader>(); + break; + case RenderPass::kCubicCorners: + shader = skstd::make_unique<GrCCCubicCornerShader>(); + break; + } + return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader)) + : this->createVSImpl(std::move(shader)); +} diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index 7db424e219..c1f85993a1 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -53,15 +53,20 @@ public: void set(const SkPoint[4], float dx, float dy); void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w); }; - // Here we enumerate every render pass needed in order to produce a complete coverage count - // mask. Triangles require two render passes: One to draw a rough outline, and a second pass to - // touch up the corners. This is an exhaustive list of all ccpr coverage shaders. + + // All primitive shapes (triangles and closed, convex bezier curves) require two + // render passes: One to draw a rough outline of the shape, and a second pass to touch up the + // corners. Here we enumerate every render pass needed in order to produce a complete + // coverage count mask. This is an exhaustive list of all ccpr coverage shaders. enum class RenderPass { kTriangles, kTriangleCorners, kQuadratics, + kQuadraticCorners, kCubics, + kCubicCorners }; + static bool RenderPassIsCubic(RenderPass); static const char* RenderPassName(RenderPass); enum class WindMethod : bool { @@ -147,6 +152,13 @@ public: void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* skOutputColor, const char* skOutputCoverage) const; + // 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. + static void EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder*, const char* leftPt, + const char* rightPt, + const char* outputDistanceEquation); + // Calculates an edge's coverage at a conservative raster vertex. The edge is defined by two // clockwise-ordered points, 'leftPt' and 'rightPt'. 'rasterVertexDir' is a pair of +/-1 // values that point in the direction of conservative raster bloat, starting from an @@ -169,7 +181,7 @@ public: const char* wind) = 0; // Emits the fragment code that calculates a pixel's signed coverage value. - virtual void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, + virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; // Returns the name of a Shader's internal varying at the point where where its value is @@ -179,6 +191,12 @@ public: SkASSERT(Scope::kVertToGeo != varying.scope()); return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut(); } + + // Defines a global float2 array that contains MSAA sample locations as offsets from pixel + // center. Subclasses can use this for software multisampling. + // + // Returns the number of samples. + static int DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f, const char* samplesName); }; class GSImpl; @@ -190,7 +208,7 @@ private: static constexpr float kAABloatRadius = 0.491111f; // Number of bezier points for curves, or 3 for triangles. - int numInputPoints() const { return RenderPass::kCubics == fRenderPass ? 4 : 3; } + int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; } enum class Impl : bool { kGeometryShader, @@ -251,12 +269,29 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con Sk2f::Store4(this, P0, P1, P2, W); } +inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) { + switch (pass) { + case RenderPass::kTriangles: + case RenderPass::kTriangleCorners: + case RenderPass::kQuadratics: + case RenderPass::kQuadraticCorners: + return false; + case RenderPass::kCubics: + case RenderPass::kCubicCorners: + return true; + } + SK_ABORT("Invalid RenderPass"); + return false; +} + inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) { switch (pass) { case RenderPass::kTriangles: return "kTriangles"; case RenderPass::kTriangleCorners: return "kTriangleCorners"; case RenderPass::kQuadratics: return "kQuadratics"; + case RenderPass::kQuadraticCorners: return "kQuadraticCorners"; case RenderPass::kCubics: return "kCubics"; + case RenderPass::kCubicCorners: return "kCubicCorners"; } SK_ABORT("Invalid RenderPass"); return ""; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index e64b8c0838..d9febc0e66 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -76,7 +76,7 @@ protected: SkSTArray<2, GrShaderVar> emitArgs; const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str(); const char* coverage = nullptr; - if (RenderPass::kTriangleCorners != proc.fRenderPass) { + if (RenderPass::kTriangles == proc.fRenderPass) { coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); } g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { @@ -212,8 +212,7 @@ public: }; /** - * Generates a conservative raster hull around a convex quadrilateral that encloses a cubic or - * quadratic, as well as its shared edge. + * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. */ class GSHull4Impl : public GrCCCoverageProcessor::GSImpl { public: @@ -232,85 +231,54 @@ public: // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, // we can identify the points by their corresponding corner. // - // NOTE: For the hull we split the square down the diagonal from top-right to bottom-left, - // and generate it in two independent invocations. All invocations, including the shared - // edge, designate the corner they will begin with as top-left. - g->codeAppendf("bool is_shared_edge = (2 == sk_InvocationID);"); - g->codeAppendf("int i = !is_shared_edge ? sk_InvocationID * 2 : (%s > 0 ? 3 : 0);", - wind.c_str()); + // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate + // the hull in two independent invocations. Each invocation designates the corner it will + // begin with as top-left. + g->codeAppend ("int i = sk_InvocationID * 2;"); g->codeAppendf("float2 topleft = %s[i];", hullPts); - g->codeAppendf("float2 topright = %s[(i + (%s > 0 ? 1 : 3)) & 3];", hullPts, wind.c_str()); - g->codeAppendf("float2 bottomleft = %s[(i + (%s > 0 ? 3 : 1)) & 3];", - hullPts, wind.c_str()); - g->codeAppendf("float2 bottomright = %s[(i + 2) & 3];", hullPts); + g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str()); + g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str()); + g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts); // Determine how much to outset the conservative raster hull from the relevant edges. - g->codeAppend ("float2 leftbloat = sign(topleft - bottomleft) * bloat;"); - g->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " - "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);"); - - g->codeAppend ("float2 upbloat = sign(topright - topleft) * bloat;"); - g->codeAppend ("upbloat = float2(0 != upbloat.y ? upbloat.y : upbloat.x, " - "0 != upbloat.x ? -upbloat.x : -upbloat.y);"); - - g->codeAppend ("float2 rightbloat = sign(bottomright - topright) * bloat;"); - g->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " - "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);"); - - // The hull raster has a coverage of +1 all around. - g->codeAppend ("half2 coverages = half2(+1);"); - - g->codeAppend ("if (is_shared_edge) {"); - // On bloat vertices along the shared edge that fall outside the input - // points, ramp coverage to 0. We do this by using coverage=-1 to erase - // what the hull just wrote. - g->codeAppend ( "coverages = half2(-1, 0);"); - // Reassign bloats to characterize a conservative raster around just the - // shared edge, rather than the entire hull. - g->codeAppend ( "leftbloat = rightbloat = -upbloat;"); - g->codeAppend ("}"); + g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, " + "topleft.x > bottomleft.x ? -bloat : bloat);"); + g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, " + "topright.x > topleft.x ? -bloat : +bloat);"); + g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, " + "bottomright.x > topright.x ? -bloat : +bloat);"); // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size // boxes centered on the input points, split evenly between two invocations. This translates // to a polygon with either one, two, or three vertices at each input point, depending on - // how sharp the corner is. The shared edge raster is the convex hull of 2 pixel-size boxes, - // one at each endpoint. For more details on conservative raster, see: + // how sharp the corner is. For more details on conservative raster, see: // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);"); g->codeAppend ("if (all(left_up_notequal)) {"); // The top-left corner will have three conservative raster vertices. // Emit the middle one first to the triangle strip. - g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x), coverages[0]);", - emitVertexFn); + g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn); g->codeAppend ("}"); g->codeAppend ("if (any(left_up_notequal)) {"); // Second conservative raster vertex for the top-left corner. - g->codeAppendf( "%s(topleft + leftbloat, coverages[1]);", emitVertexFn); + g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn); g->codeAppend ("}"); - g->codeAppendf("%s(topleft + upbloat, coverages[0]);", emitVertexFn); - - g->codeAppend ("if (!is_shared_edge) {"); - // Main interior body of this invocation's half of the hull. - g->codeAppendf( "%s(bottomleft + leftbloat, +1);", emitVertexFn); - g->codeAppend ("}"); - - g->codeAppendf("%s(topright + (is_shared_edge ? rightbloat : upbloat), coverages[1]);", - emitVertexFn); + // Main interior body of this invocation's half of the hull. + g->codeAppendf("%s(topleft + upbloat);", emitVertexFn); + g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn); + g->codeAppendf("%s(topright + upbloat);", emitVertexFn); // Remaining two conservative raster vertices for the top-right corner. g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);"); g->codeAppend ("if (any(up_right_notequal)) {"); - g->codeAppendf( "%s(topright + (is_shared_edge ? upbloat : rightbloat), " - "coverages[0]);", emitVertexFn); + g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn); g->codeAppend ("}"); g->codeAppend ("if (all(up_right_notequal)) {"); - g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x), coverages[0]);", - emitVertexFn); + g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn); g->codeAppend ("}"); - // 3 invocations: 2 hull invocations and 1 shared edge. - g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 3); + g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2); } }; @@ -344,15 +312,17 @@ private: void GrCCCoverageProcessor::initGS() { SkASSERT(Impl::kGeometryShader == fImpl); - if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) { + 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); SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2); SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride()); + GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX)); } this->setWillUseGeoShader(); } @@ -378,6 +348,9 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Sh case RenderPass::kQuadratics: case RenderPass::kCubics: return new GSHull4Impl(std::move(shadr)); + case RenderPass::kQuadraticCorners: + case RenderPass::kCubicCorners: + return new GSCornerImpl(std::move(shadr), 2); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index 08398e1900..144a4a5d58 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -92,13 +92,13 @@ protected: static constexpr int kVertexData_LeftNeighborIdShift = 9; static constexpr int kVertexData_RightNeighborIdShift = 7; static constexpr int kVertexData_BloatIdxShift = 5; -static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 4; +static constexpr int kVertexData_InvertCoverageBit = 1 << 4; static constexpr int kVertexData_IsEdgeBit = 1 << 3; static constexpr int kVertexData_IsHullBit = 1 << 2; /** * Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to - * calculate coverage values. See VSHullAndEdgeImpl. + * calculate initial coverage values for edges. See VSHullAndEdgeImpl. */ static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID, int32_t bloatIdx, int32_t cornerID, @@ -114,12 +114,15 @@ static constexpr int32_t hull_vertex_data(int32_t cornerID, int32_t bloatIdx, in kVertexData_IsHullBit); } -static constexpr int32_t edge_vertex_data(int32_t leftID, int rightID, int32_t bloatIdx, - int32_t extraData = 0) { - return pack_vertex_data(leftID, leftID, bloatIdx, rightID, kVertexData_IsEdgeBit | extraData); +static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int32_t bloatIdx, + int n) { + return pack_vertex_data(0 == endptIdx ? (edgeID + 1) % n : edgeID, + 0 == endptIdx ? (edgeID + 1) % n : edgeID, + bloatIdx, 0 == endptIdx ? edgeID : (edgeID + 1) % n, + kVertexData_IsEdgeBit | + (!endptIdx ? kVertexData_InvertCoverageBit : 0)); } - static constexpr int32_t kHull3AndEdgeVertices[] = { hull_vertex_data(0, 0, 3), hull_vertex_data(0, 1, 3), @@ -131,26 +134,26 @@ static constexpr int32_t kHull3AndEdgeVertices[] = { hull_vertex_data(2, 1, 3), hull_vertex_data(2, 2, 3), - edge_vertex_data(0, 1, 0), - edge_vertex_data(0, 1, 1), - edge_vertex_data(0, 1, 2), - edge_vertex_data(1, 0, 0, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(1, 0, 1, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(1, 0, 2, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(0, 0, 0, 3), + edge_vertex_data(0, 0, 1, 3), + edge_vertex_data(0, 0, 2, 3), + edge_vertex_data(0, 1, 0, 3), + edge_vertex_data(0, 1, 1, 3), + edge_vertex_data(0, 1, 2, 3), - edge_vertex_data(1, 2, 0), - edge_vertex_data(1, 2, 1), - edge_vertex_data(1, 2, 2), - edge_vertex_data(2, 1, 0, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(2, 1, 1, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(2, 1, 2, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(1, 0, 0, 3), + edge_vertex_data(1, 0, 1, 3), + edge_vertex_data(1, 0, 2, 3), + edge_vertex_data(1, 1, 0, 3), + edge_vertex_data(1, 1, 1, 3), + edge_vertex_data(1, 1, 2, 3), - edge_vertex_data(2, 0, 0), - edge_vertex_data(2, 0, 1), - edge_vertex_data(2, 0, 2), - edge_vertex_data(0, 2, 0, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(0, 2, 1, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(0, 2, 2, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(2, 0, 0, 3), + edge_vertex_data(2, 0, 1, 3), + edge_vertex_data(2, 0, 2, 3), + edge_vertex_data(2, 1, 0, 3), + edge_vertex_data(2, 1, 1, 3), + edge_vertex_data(2, 1, 2, 3), }; GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); @@ -198,7 +201,7 @@ static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = { GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); -static constexpr int32_t kHull4AndEdgeVertices[] = { +static constexpr int32_t kHull4Vertices[] = { hull_vertex_data(0, 0, 4), hull_vertex_data(0, 1, 4), hull_vertex_data(0, 2, 4), @@ -212,23 +215,17 @@ static constexpr int32_t kHull4AndEdgeVertices[] = { hull_vertex_data(3, 1, 4), hull_vertex_data(3, 2, 4), - edge_vertex_data(0, 3, 0, kVertexData_InvertNegativeCoverageBit), - edge_vertex_data(0, 3, 1), - edge_vertex_data(0, 3, 2), - edge_vertex_data(3, 0, 0), - edge_vertex_data(3, 0, 1), - edge_vertex_data(3, 0, 2, kVertexData_InvertNegativeCoverageBit), + // No edges for now (beziers don't use edges). }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeVertexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); -static constexpr uint16_t kHull4AndEdgeIndicesAsStrips[] = { +static constexpr uint16_t kHull4IndicesAsStrips[] = { 1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally). - 7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull. - 13, 12, 14, 17, 15, 16 // Shared edge. + 7, 6, 8, 5, 9, 11, 10 // Second half of the hull. }; -static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = { +static constexpr uint16_t kHull4IndicesAsTris[] = { // First half of the hull (split diagonally). 1, 0, 2, 0, 11, 2, @@ -242,30 +239,23 @@ static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = { 8, 5, 9, 5, 11, 9, 9, 11, 10, - - // Shared edge. - 13, 12, 14, - 12, 17, 14, - 14, 17, 15, - 17, 16, 15, }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); /** - * Generates a conservative raster hull around a triangle or curve. For triangles we generate - * additional conservative rasters with coverage ramps around the edges. For curves we - * generate an additional raster with coverage ramps around its shared edge. + * Generates a conservative raster hull around a convex polygon. For triangles we generate + * additional conservative rasters around the edges and calculate coverage ramps. * - * Triangle rough outlines are drawn in two steps: (1) Draw a conservative raster of the entire - * triangle, with a coverage of +1. (2) Draw conservative rasters around each edge, with a + * Triangle rough outlines are drawn in two steps: (1) draw a conservative raster of the entire + * triangle, with a coverage of +1, and (2) draw conservative rasters around each edge, with a * coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges - * into smooth, antialiased ones. The final corners get touched up in a later step by VSCornerImpl. + * into smooth, antialiased ones. * - * Curves are drawn in two steps: (1) Draw a conservative raster around the input points, passing - * coverage=+1 to the Shader. (2) Draw an additional conservative raster around the curve's shared - * edge, using coverage=-1 at bloat vertices that fall outside the input points. This erases what - * the hull just wrote and ramps coverage to zero. + * Curve rough outlines are just the conservative raster of a convex quadrilateral that encloses the + * curve. The Shader takes care of everything else for now. + * + * The final corners get touched up in a later step by VSCornerImpl. */ class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl { public: @@ -294,9 +284,10 @@ public: // Here we generate conservative raster geometry for the input polygon. It is the convex // hull of N pixel-size boxes, one centered on each the input points. Each corner has three // vertices, where one or two may cause degenerate triangles. The vertex data tells us how - // to offset each vertex. Edges are also handled here using the same concept. For more - // details on conservative raster, see: + // to offset each vertex. For more details on conservative raster, see: // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html + // + // Triangle edges are also handled here using the same concept (see kHull3AndEdgeVertices). v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", hullPts, kVertexData_LeftNeighborIdShift); @@ -333,32 +324,29 @@ public: // fallthru. v->codeAppend ("}"); + // For triangles, we also emit coverage in order to handle edges and corners. + const char* coverage = nullptr; + if (3 == fNumSides) { + v->codeAppend ("half coverage;"); + Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage"); + v->codeAppendf("if (0 != (%s & %i)) {", // Are we the opposite endpoint of an edge? + proc.getAttrib(kAttribIdx_VertexData).fName, + kVertexData_InvertCoverageBit); + v->codeAppend ( "coverage = -1 - coverage;"); + v->codeAppend ("}"); + + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a hull vertex? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsHullBit); + v->codeAppend ( "coverage = +1;"); // Hull coverage is +1 all around. + v->codeAppend ("}"); + + coverage = "coverage"; + } + v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - // The hull has a coverage of +1 all around. - v->codeAppend ("half coverage = +1;"); - - if (3 == fNumSides) { - v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); - Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage"); - v->codeAppend ("}"); - } else { - SkASSERT(4 == fNumSides); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); - v->codeAppend ( "coverage = -1;"); - v->codeAppend ("}"); - } - - v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? - proc.getAttrib(kAttribIdx_VertexData).fName, - kVertexData_InvertNegativeCoverageBit); - v->codeAppend ( "coverage = -1 - coverage;"); - v->codeAppend ("}"); - - return "coverage"; + return coverage; } private: @@ -437,7 +425,31 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { break; } - case RenderPass::kTriangleCorners: { + case RenderPass::kQuadratics: + case RenderPass::kCubics: { + GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); + fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices), + kHull4Vertices, gHull4VertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); + if (caps.usePrimitiveRestart()) { + fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kHull4IndicesAsStrips), + kHull4IndicesAsStrips, + gHull4IndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips); + } else { + fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kHull4IndicesAsTris), + kHull4IndicesAsTris, + gHull4IndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris); + } + break; + } + + case RenderPass::kTriangleCorners: + case RenderPass::kQuadraticCorners: + case RenderPass::kCubicCorners: { GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); if (caps.usePrimitiveRestart()) { fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, @@ -452,35 +464,14 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { gCornerIndexBufferKey); fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris); } - break; - } - - case RenderPass::kQuadratics: - case RenderPass::kCubics: { - GR_DEFINE_STATIC_UNIQUE_KEY(gHull4AndEdgeVertexBufferKey); - fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, - sizeof(kHull4AndEdgeVertices), - kHull4AndEdgeVertices, - gHull4AndEdgeVertexBufferKey); - GR_DEFINE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey); - if (caps.usePrimitiveRestart()) { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull4AndEdgeIndicesAsStrips), - kHull4AndEdgeIndicesAsStrips, - gHull4AndEdgeIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4AndEdgeIndicesAsStrips); - } else { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull4AndEdgeIndicesAsTris), - kHull4AndEdgeIndicesAsTris, - gHull4AndEdgeIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4AndEdgeIndicesAsTris); + if (RenderPass::kTriangleCorners != fRenderPass) { + fNumIndicesPerInstance = fNumIndicesPerInstance * 2/3; } break; } } - if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) { + if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); SkASSERT(kAttribIdx_X == this->numAttribs()); @@ -534,11 +525,13 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Sh switch (fRenderPass) { case RenderPass::kTriangles: return new VSHullAndEdgeImpl(std::move(shadr), 3); - case RenderPass::kTriangleCorners: - return new VSCornerImpl(std::move(shadr)); case RenderPass::kQuadratics: case RenderPass::kCubics: return new VSHullAndEdgeImpl(std::move(shadr), 4); + case RenderPass::kTriangleCorners: + case RenderPass::kQuadraticCorners: + case RenderPass::kCubicCorners: + return new VSCornerImpl(std::move(shadr)); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp index 76d1646b65..5ae51c7d9b 100644 --- a/src/gpu/ccpr/GrCCCubicShader.cpp +++ b/src/gpu/ccpr/GrCCCubicShader.cpp @@ -13,8 +13,8 @@ using Shader = GrCCCoverageProcessor::Shader; void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* /*repetitionID*/, const char* /*wind*/, - GeometryVars*) const { + const char* repetitionID, const char* wind, + GeometryVars* vars) const { // Find the cubic's power basis coefficients. s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, " " 3, -6, 3, 0, " @@ -58,44 +58,118 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, // Evaluate the cubic at T=.5 for a mid-ish point. s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts); - // Orient the KLM matrix so L & M are both positive on the side of the curve we wish to fill. + // Orient the KLM matrix so L & M have matching signs on the side of the curve we wish to fill. + // We give L & M both the same sign as wind, in order to pass this value to the fragment shader. + // (Cubics are pre-chopped such that L & M do not change sign within any individual segment). s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));", fKLMMatrix.c_str(), fKLMMatrix.c_str()); s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, " - "0, orientation[0], 0, " - "0, 0, orientation[1]);", fKLMMatrix.c_str()); + "0, orientation[0] * %s, 0, " + "0, 0, orientation[1] * %s);", fKLMMatrix.c_str(), wind, wind); + + // Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0). + s->declareGlobal(fEdgeDistanceEquation); + s->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind); + s->codeAppendf("float2 edgept0 = %s[edgeidx0];", pts); + s->codeAppendf("float2 edgept1 = %s[3 - edgeidx0];", pts); + Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str()); + + this->onEmitSetupCode(s, pts, repetitionID, vars); } void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, const char* position, const char* inputCoverage, - const char* wind) { + const char* /*wind*/) { + SkASSERT(!inputCoverage); + + fKLMD.reset(kFloat4_GrSLType, scope); + varyingHandler->addVarying("klmd", &fKLMD); code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str()); + code->appendf("float d = dot(float3(%s, 1), %s);", position, fEdgeDistanceEquation.c_str()); + code->appendf("%s = float4(klm, d);", OutName(fKLMD)); - fKLMW.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("klmw", &fKLMW); - code->appendf("%s.xyz = klm;", OutName(fKLMW)); - code->appendf("%s.w = %s * %s;", OutName(fKLMW), inputCoverage, wind); + this->onEmitVaryings(varyingHandler, scope, code); +} +void GrCCCubicShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;", + fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn()); + + this->emitCoverage(f, outputCoverage); + + // Wind is the sign of both L and/or M. Take the sign of whichever has the larger magnitude. + // (In reality, either would be fine because we chop cubics with more than a half pixel of + // padding around the L & M lines, so neither should approach zero.) + f->codeAppend ("half wind = sign(l + m);"); + f->codeAppendf("%s *= wind;", outputCoverage); +} + +void GrCCCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code) { fGradMatrix.reset(kFloat2x2_GrSLType, scope); varyingHandler->addVarying("grad_matrix", &fGradMatrix); + // "klm" was just defined by the base class. code->appendf("%s[0] = 3 * klm[0] * %s[0].xy;", OutName(fGradMatrix), fKLMMatrix.c_str()); code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;", OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str()); } -void GrCCCubicShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, - GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;", - fKLMW.fsIn(), fKLMW.fsIn(), fKLMW.fsIn()); +void GrCCCubicHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + // k,l,m,d are defined by the base class. f->codeAppend ("float f = k*k*k - l*m;"); f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn()); - f->codeAppend ("float d = f * inversesqrt(dot(grad_f, grad_f));"); -#ifdef SK_DEBUG - if (proc.debugVisualizationsEnabled()) { - f->codeAppendf("d /= %f;", proc.debugBloat()); - } -#endif - f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s.w;", outputCoverage, fKLMW.fsIn()); + f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage); + f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat edge opposite the curve. +} + +void GrCCCubicCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, + const char* repetitionID, GeometryVars* vars) const { + s->codeAppendf("float2 corner = %s[%s * 3];", pts, repetitionID); + vars->fCornerVars.fPoint = "corner"; +} + +void GrCCCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code) { + using Interpolation = GrGLSLVaryingHandler::Interpolation; + + fdKLMDdx.reset(kFloat4_GrSLType, scope); + varyingHandler->addVarying("dklmddx", &fdKLMDdx, Interpolation::kCanBeFlat); + code->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);", + OutName(fdKLMDdx), fKLMMatrix.c_str(), fKLMMatrix.c_str(), + fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str()); + + fdKLMDdy.reset(kFloat4_GrSLType, scope); + varyingHandler->addVarying("dklmddy", &fdKLMDdy, Interpolation::kCanBeFlat); + code->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);", + OutName(fdKLMDdy), fKLMMatrix.c_str(), fKLMMatrix.c_str(), + fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str()); +} + +void GrCCCubicCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn()); + + // Erase what the previous hull shader wrote. We don't worry about the two corners falling on + // the same pixel because those cases should have been weeded out by this point. + // k,l,m,d are defined by the base class. + f->codeAppend ("float f = k*k*k - l*m;"); + f->codeAppend ("float2 grad_f = float3(3*k*k, -m, -l) * float2x3(grad_klmd);"); + f->codeAppendf("%s = -clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", + outputCoverage); + f->codeAppendf("%s -= d;", outputCoverage); + + // Use software msaa to estimate actual coverage at the corner pixels. + const int sampleCount = Shader::DefineSoftSampleLocations(f, "samples"); + f->codeAppendf("float4 klmd_center = float4(%s.xyz, %s.w + 0.5);", + fKLMD.fsIn(), fKLMD.fsIn()); + f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount); + f->codeAppend ( "float4 klmd = grad_klmd * samples[i] + klmd_center;"); + f->codeAppend ( "half f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;"); + f->codeAppendf( "%s += all(greaterThan(half4(f, klmd.y, klmd.z, klmd.w), " + "half4(0))) ? %f : 0;", + outputCoverage, 1.0 / sampleCount); + f->codeAppend ("}"); } diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h index 70d3300461..063549264a 100644 --- a/src/gpu/ccpr/GrCCCubicShader.h +++ b/src/gpu/ccpr/GrCCCubicShader.h @@ -24,17 +24,37 @@ class GrCCCubicShader : public GrCCCoverageProcessor::Shader { protected: void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - const char* wind, GeometryVars*) const override; + const char* wind, GeometryVars*) const final; + virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + GeometryVars*) const {} void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) override; + const char* position, const char* inputCoverage, const char* wind) final; + virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0; - void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, - const char* outputCoverage) const override; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; + virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType}; - GrGLSLVarying fKLMW; + GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType}; + GrGLSLVarying fKLMD; +}; + +class GrCCCubicHullShader : public GrCCCubicShader { + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; + void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; + GrGLSLVarying fGradMatrix; }; +class GrCCCubicCornerShader : public GrCCCubicShader { + void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + GeometryVars*) const override; + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; + void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; + + GrGLSLVarying fdKLMDdx; + GrGLSLVarying fdKLMDdy; +}; + #endif diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index e625c43743..43f5e6be6a 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -530,11 +530,15 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount if (batchTotalCounts.fQuadratics) { this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics, 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::kCubics, WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners, + WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); } } diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp index baa10fd34e..090e29f4c3 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.cpp +++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp @@ -14,7 +14,7 @@ using Shader = GrCCCoverageProcessor::Shader; void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* /*repetitionID*/, const char* /*wind*/, + const char* repetitionID, const char* wind, GeometryVars* vars) const { s->declareGlobal(fCanonicalMatrix); s->codeAppendf("%s = float3x3(0.0, 0, 1, " @@ -25,6 +25,41 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p "%s[2], 1));", fCanonicalMatrix.c_str(), pts, pts, pts); + s->declareGlobal(fEdgeDistanceEquation); + s->codeAppendf("float2 edgept0 = %s[%s > 0 ? 2 : 0];", pts, wind); + s->codeAppendf("float2 edgept1 = %s[%s > 0 ? 0 : 2];", pts, wind); + Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str()); + + this->onEmitSetupCode(s, pts, repetitionID, vars); +} + +void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code, + const char* position, const char* inputCoverage, + const char* wind) { + SkASSERT(!inputCoverage); + + fXYDW.reset(kFloat4_GrSLType, scope); + varyingHandler->addVarying("xydw", &fXYDW); + code->appendf("%s.xy = (%s * float3(%s, 1)).xy;", + OutName(fXYDW), fCanonicalMatrix.c_str(), position); + code->appendf("%s.z = dot(%s.xy, %s) + %s.z;", + OutName(fXYDW), fEdgeDistanceEquation.c_str(), position, + fEdgeDistanceEquation.c_str()); + code->appendf("%s.w = %s;", OutName(fXYDW), wind); + + this->onEmitVaryings(varyingHandler, scope, code); +} + +void GrCCQuadraticShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + this->emitCoverage(f, outputCoverage); + f->codeAppendf("%s *= %s.w;", outputCoverage, fXYDW.fsIn()); // Sign by wind. +} + +void GrCCQuadraticHullShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, + const char* /*repetitionID*/, + GeometryVars* vars) const { // Find the T value whose tangent is halfway between the tangents at the endpionts. s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts); s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts); @@ -41,31 +76,66 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p vars->fHullVars.fAlternatePoints = "quadratic_hull"; } -void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code, - const char* position, const char* inputCoverage, - const char* wind) { - fCoords.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("coords", &fCoords); - code->appendf("%s.xy = (%s * float3(%s, 1)).xy;", - OutName(fCoords), fCanonicalMatrix.c_str(), position); - code->appendf("%s.zw = float2(2 * %s.x, -1) * float2x2(%s);", - OutName(fCoords), OutName(fCoords), fCanonicalMatrix.c_str()); - - fCoverageTimesWind.reset(kHalf_GrSLType, scope); - varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); - code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); +void GrCCQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code) { + fGrad.reset(kFloat2_GrSLType, scope); + varyingHandler->addVarying("grad", &fGrad); + code->appendf("%s = float2(2 * %s.x, -1) * float2x2(%s);", + OutName(fGrad), OutName(fXYDW), fCanonicalMatrix.c_str()); } -void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, - GrGLSLFPFragmentBuilder* f, +void GrCCQuadraticHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));", + fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn(), fGrad.fsIn(), fGrad.fsIn()); + f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage); + f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYDW.fsIn()); // Flat closing edge. +} + +void GrCCQuadraticCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, + const char* repetitionID, + GeometryVars* vars) const { + s->codeAppendf("float2 corner = %s[%s * 2];", pts, repetitionID); + vars->fCornerVars.fPoint = "corner"; +} + +void GrCCQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code) { + using Interpolation = GrGLSLVaryingHandler::Interpolation; + + fdXYDdx.reset(kFloat3_GrSLType, scope); + varyingHandler->addVarying("dXYDdx", &fdXYDdx, Interpolation::kCanBeFlat); + code->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);", + OutName(fdXYDdx), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(), + fEdgeDistanceEquation.c_str()); + + fdXYDdy.reset(kFloat3_GrSLType, scope); + varyingHandler->addVarying("dXYDdy", &fdXYDdy, Interpolation::kCanBeFlat); + code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);", + OutName(fdXYDdy), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(), + fEdgeDistanceEquation.c_str()); +} + +void GrCCQuadraticCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { - f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s.zw, %s.zw));", - fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn()); -#ifdef SK_DEBUG - if (proc.debugVisualizationsEnabled()) { - f->codeAppendf("d /= %f;", proc.debugBloat()); - } -#endif - f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s;", outputCoverage, fCoverageTimesWind.fsIn()); + f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;", + fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn()); + f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn()); + + // Erase what the previous hull shader wrote. We don't worry about the two corners falling on + // the same pixel because those cases should have been weeded out by this point. + f->codeAppend ("float f = x*x - y;"); + f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);"); + f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage); + f->codeAppendf("%s -= d;", outputCoverage); + + // Use software msaa to approximate coverage at the corner pixels. + int sampleCount = Shader::DefineSoftSampleLocations(f, "samples"); + f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);", fXYDW.fsIn(), fXYDW.fsIn()); + f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount); + f->codeAppend ( "float3 xyd = grad_xyd * samples[i] + xyd_center;"); + f->codeAppend ( "half f = xyd.y - xyd.x * xyd.x;"); // f > 0 -> inside curve. + f->codeAppendf( "%s += all(greaterThan(float2(f,xyd.z), float2(0))) ? %f : 0;", + outputCoverage, 1.0 / sampleCount); + f->codeAppendf("}"); } diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h index d91f943471..0be03d33dd 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.h +++ b/src/gpu/ccpr/GrCCQuadraticShader.h @@ -23,17 +23,48 @@ class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader { protected: void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - const char* wind, GeometryVars*) const override; + const char* wind, GeometryVars*) const final; + virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + GeometryVars*) const = 0; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) override; + const char* position, const char* inputCoverage, const char* wind) final; + virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {} - void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, - const char* outputCoverage) const override; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; + virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType}; - GrGLSLVarying fCoords; - GrGLSLVarying fCoverageTimesWind; + const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType}; + GrGLSLVarying fXYDW; +}; + +/** + * This pass draws a conservative raster hull around the quadratic bezier curve, computes the + * curve's coverage using the gradient-based AA technique outlined in the Loop/Blinn paper, and + * uses simple distance-to-edge to subtract out coverage for the flat closing edge [P2 -> P0]. Since + * the provided curves are monotonic, this will get every pixel right except the two corners. + */ +class GrCCQuadraticHullShader : public GrCCQuadraticShader { + void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + GeometryVars*) const override; + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; + void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; + + GrGLSLVarying fGrad; +}; + +/** + * This pass fixes the corners of a closed quadratic segment with soft MSAA. + */ +class GrCCQuadraticCornerShader : public GrCCQuadraticShader { + void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + GeometryVars*) const override; + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; + void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; + + GrGLSLVarying fdXYDdx; + GrGLSLVarying fdXYDdy; }; #endif diff --git a/src/gpu/ccpr/GrCCTriangleShader.cpp b/src/gpu/ccpr/GrCCTriangleShader.cpp index 8135313965..e086201b42 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.cpp +++ b/src/gpu/ccpr/GrCCTriangleShader.cpp @@ -22,8 +22,7 @@ void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); } -void GrCCTriangleShader::onEmitFragmentCode(const GrCCCoverageProcessor&, - GrGLSLFPFragmentBuilder* f, +void GrCCTriangleShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); } @@ -107,8 +106,7 @@ void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandl code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind); } -void GrCCTriangleCornerShader::onEmitFragmentCode(const GrCCCoverageProcessor&, - GrGLSLFPFragmentBuilder* f, +void GrCCTriangleCornerShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { // By the time we reach this shader, the pixel is in the following state: // diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h index 6dae8df497..5f33b077cd 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.h +++ b/src/gpu/ccpr/GrCCTriangleShader.h @@ -19,8 +19,7 @@ class GrCCTriangleShader : public GrCCCoverageProcessor::Shader { void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, - const char* outputCoverage) const override; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; GrGLSLVarying fCoverageTimesWind; }; @@ -35,8 +34,7 @@ class GrCCTriangleCornerShader : public GrCCCoverageProcessor::Shader { const char* wind, GeometryVars*) const override; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, - const char* outputCoverage) const override; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override; GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2}; GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};