aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageOp.cpp24
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageOp.h6
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageProcessor.cpp409
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageProcessor.h303
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp278
-rw-r--r--src/gpu/ccpr/GrCCPRCubicProcessor.cpp216
-rw-r--r--src/gpu/ccpr/GrCCPRCubicProcessor.h120
-rw-r--r--src/gpu/ccpr/GrCCPRCubicShader.cpp203
-rw-r--r--src/gpu/ccpr/GrCCPRCubicShader.h94
-rw-r--r--src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp168
-rw-r--r--src/gpu/ccpr/GrCCPRQuadraticProcessor.h117
-rw-r--r--src/gpu/ccpr/GrCCPRQuadraticShader.cpp157
-rw-r--r--src/gpu/ccpr/GrCCPRQuadraticShader.h87
-rw-r--r--src/gpu/ccpr/GrCCPRTriangleProcessor.cpp185
-rw-r--r--src/gpu/ccpr/GrCCPRTriangleProcessor.h103
-rw-r--r--src/gpu/ccpr/GrCCPRTriangleShader.cpp168
-rw-r--r--src/gpu/ccpr/GrCCPRTriangleShader.h80
17 files changed, 1350 insertions, 1368 deletions
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
index f37fcbef05..34e0410f9e 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
@@ -385,7 +385,7 @@ void GrCCPRCoverageOp::setBuffers(sk_sp<GrBuffer> pointsBuffer, sk_sp<GrBuffer>
}
void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
- using Mode = GrCCPRCoverageProcessor::Mode;
+ using RenderPass = GrCCPRCoverageProcessor::RenderPass;
SkDEBUGCODE(GrCCPRCoverageProcessor::Validate(flushState->drawOpArgs().fProxy));
SkASSERT(fPointsBuffer);
@@ -399,34 +399,34 @@ void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
// Triangles.
auto constexpr kTrianglesGrPrimitiveType = GrCCPRCoverageProcessor::kTrianglesGrPrimitiveType;
- this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleHulls,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleHulls,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
- this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleEdges,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleEdges,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
- this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleCorners,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleCorners,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
// Quadratics.
auto constexpr kQuadraticsGrPrimitiveType = GrCCPRCoverageProcessor::kQuadraticsGrPrimitiveType;
- this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticHulls,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticHulls,
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
- this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticCorners,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticCorners,
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
// Cubics.
auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
- this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineHulls,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineHulls,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
- this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopHulls,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopHulls,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
- this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineCorners,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineCorners,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
- this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopCorners,
+ this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopCorners,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
}
void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
- GrCCPRCoverageProcessor::Mode mode,
+ GrCCPRCoverageProcessor::RenderPass renderPass,
GrPrimitiveType primType, int vertexCount,
int PrimitiveTallies::* instanceType) const {
using ScissorMode = GrCCPRCoverageOpsBuilder::ScissorMode;
@@ -463,7 +463,7 @@ void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPi
SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
if (!fMeshesScratchBuffer.empty()) {
- GrCCPRCoverageProcessor proc(mode, fPointsBuffer.get());
+ GrCCPRCoverageProcessor proc(renderPass, fPointsBuffer.get());
SkASSERT(flushState->rtCommandBuffer());
flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
fDynamicStatesScratchBuffer.begin(),
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.h b/src/gpu/ccpr/GrCCPRCoverageOp.h
index 7d457fef33..80b4cf9744 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.h
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.h
@@ -149,9 +149,9 @@ private:
const PrimitiveTallies baseInstances[kNumScissorModes],
const PrimitiveTallies endInstances[kNumScissorModes]);
- void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, const GrCCPRCoverageProcessor::Mode,
- GrPrimitiveType, int vertexCount,
- int PrimitiveTallies::* instanceType) const;
+ void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&,
+ const GrCCPRCoverageProcessor::RenderPass, GrPrimitiveType,
+ int vertexCount, int PrimitiveTallies::* instanceType) const;
sk_sp<GrBuffer> fPointsBuffer;
sk_sp<GrBuffer> fInstanceBuffer;
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
index d27ea4c077..6fa298722a 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
@@ -7,323 +7,91 @@
#include "GrCCPRCoverageProcessor.h"
-#include "GrRenderTargetProxy.h"
-#include "ccpr/GrCCPRTriangleProcessor.h"
-#include "ccpr/GrCCPRQuadraticProcessor.h"
-#include "ccpr/GrCCPRCubicProcessor.h"
+#include "SkMakeUnique.h"
+#include "ccpr/GrCCPRCubicShader.h"
+#include "ccpr/GrCCPRQuadraticShader.h"
+#include "ccpr/GrCCPRTriangleShader.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
-#include "glsl/GrGLSLProgramBuilder.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
-const char* GrCCPRCoverageProcessor::GetProcessorName(Mode mode) {
- switch (mode) {
- case Mode::kTriangleHulls:
- return "GrCCPRTriangleHullAndEdgeProcessor (hulls)";
- case Mode::kTriangleEdges:
- return "GrCCPRTriangleHullAndEdgeProcessor (edges)";
- case Mode::kTriangleCorners:
- return "GrCCPRTriangleCornerProcessor";
- case Mode::kQuadraticHulls:
- return "GrCCPRQuadraticHullProcessor";
- case Mode::kQuadraticCorners:
- return "GrCCPRQuadraticCornerProcessor";
- case Mode::kSerpentineHulls:
- return "GrCCPRCubicHullProcessor (serpentine)";
- case Mode::kLoopHulls:
- return "GrCCPRCubicHullProcessor (loop)";
- case Mode::kSerpentineCorners:
- return "GrCCPRCubicCornerProcessor (serpentine)";
- case Mode::kLoopCorners:
- return "GrCCPRCubicCornerProcessor (loop)";
+static GrVertexAttribType instance_array_format(GrCCPRCoverageProcessor::RenderPass renderPass) {
+ switch (renderPass) {
+ case GrCCPRCoverageProcessor::RenderPass::kTriangleHulls:
+ case GrCCPRCoverageProcessor::RenderPass::kTriangleEdges:
+ case GrCCPRCoverageProcessor::RenderPass::kTriangleCorners:
+ return kInt4_GrVertexAttribType;
+ case GrCCPRCoverageProcessor::RenderPass::kQuadraticHulls:
+ case GrCCPRCoverageProcessor::RenderPass::kQuadraticCorners:
+ case GrCCPRCoverageProcessor::RenderPass::kSerpentineHulls:
+ case GrCCPRCoverageProcessor::RenderPass::kLoopHulls:
+ case GrCCPRCoverageProcessor::RenderPass::kSerpentineCorners:
+ case GrCCPRCoverageProcessor::RenderPass::kLoopCorners:
+ return kInt2_GrVertexAttribType;
}
- SK_ABORT("Unexpected ccpr coverage processor mode.");
- return nullptr;
+ SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
+ return kInt4_GrVertexAttribType;
}
-GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(Mode mode, GrBuffer* pointsBuffer)
+GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(RenderPass renderPass, GrBuffer* pointsBuffer)
: INHERITED(kGrCCPRCoverageProcessor_ClassID)
- , fMode(mode)
- , fInstanceAttrib(this->addInstanceAttrib("instance", InstanceArrayFormat(mode))) {
+ , fRenderPass(renderPass)
+ , fInstanceAttrib(this->addInstanceAttrib("instance", instance_array_format(fRenderPass))) {
fPointsBufferAccess.reset(kRG_float_GrPixelConfig, pointsBuffer, kVertex_GrShaderFlag);
this->addBufferAccess(&fPointsBufferAccess);
this->setWillUseGeoShader();
}
-void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
- GrProcessorKeyBuilder* b) const {
- b->add32(int(fMode));
-}
-
-GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
- switch (fMode) {
- using GeometryType = GrCCPRTriangleHullAndEdgeProcessor::GeometryType;
-
- case Mode::kTriangleHulls:
- return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kHulls);
- case Mode::kTriangleEdges:
- return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kEdges);
- case Mode::kTriangleCorners:
- return new GrCCPRTriangleCornerProcessor();
- case Mode::kQuadraticHulls:
- return new GrCCPRQuadraticHullProcessor();
- case Mode::kQuadraticCorners:
- return new GrCCPRQuadraticCornerProcessor();
- case Mode::kSerpentineHulls:
- return new GrCCPRCubicHullProcessor(GrCCPRCubicProcessor::CubicType::kSerpentine);
- case Mode::kLoopHulls:
- return new GrCCPRCubicHullProcessor(GrCCPRCubicProcessor::CubicType::kLoop);
- case Mode::kSerpentineCorners:
- return new GrCCPRCubicCornerProcessor(GrCCPRCubicProcessor::CubicType::kSerpentine);
- case Mode::kLoopCorners:
- return new GrCCPRCubicCornerProcessor(GrCCPRCubicProcessor::CubicType::kLoop);
+void GrCCPRCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler,
+ SkString* code, const char* position,
+ const char* coverage, const char* wind) {
+ WindHandling windHandling = this->onEmitVaryings(varyingHandler, code, position, coverage,
+ wind);
+ if (WindHandling::kNotHandled == windHandling) {
+ varyingHandler->addFlatVarying("wind", &fWind, kLow_GrSLPrecision);
+ code->appendf("%s = %s;", fWind.gsOut(), wind);
}
- SK_ABORT("Unexpected ccpr coverage processor mode.");
- return nullptr;
}
-using PrimitiveProcessor = GrCCPRCoverageProcessor::PrimitiveProcessor;
-
-void PrimitiveProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
- const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
-
- GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
- switch (fCoverageType) {
- case CoverageType::kOne:
- case CoverageType::kShader:
- varyingHandler->addFlatVarying("wind", &fFragWind, kLow_GrSLPrecision);
- break;
- case CoverageType::kInterpolated:
- varyingHandler->addVarying("coverage_times_wind", &fFragCoverageTimesWind,
- kMedium_GrSLPrecision);
- break;
+void GrCCPRCoverageProcessor::Shader::emitFragmentCode(const GrCCPRCoverageProcessor& proc,
+ GrGLSLPPFragmentBuilder* f,
+ const char* skOutputColor,
+ const char* skOutputCoverage) const {
+ f->codeAppendf("half coverage = 0;");
+ this->onEmitFragmentCode(f, "coverage");
+ if (fWind.fsIn()) {
+ f->codeAppendf("%s.a = coverage * %s;", skOutputColor, fWind.fsIn());
+ } else {
+ f->codeAppendf("%s.a = coverage;", skOutputColor);
}
- this->resetVaryings(varyingHandler);
-
- varyingHandler->emitAttributes(proc);
-
- this->emitVertexShader(proc, args.fVertBuilder, args.fTexelBuffers[0], args.fRTAdjustName,
- gpArgs);
- this->emitGeometryShader(proc, args.fGeomBuilder, args.fRTAdjustName);
- this->emitCoverage(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
-
- SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
-}
-
-void PrimitiveProcessor::emitVertexShader(const GrCCPRCoverageProcessor& proc,
- GrGLSLVertexBuilder* v,
- const TexelBufferHandle& pointsBuffer,
- const char* rtAdjust, GrGPArgs* gpArgs) const {
- v->codeAppendf("int packedoffset = %s[%i];", proc.instanceAttrib(), proc.atlasOffsetIdx());
- v->codeAppend ("float2 atlasoffset = float2((packedoffset<<16) >> 16, "
- "packedoffset >> 16);");
-
- this->onEmitVertexShader(proc, v, pointsBuffer, "atlasoffset", rtAdjust, gpArgs);
-}
-
-void PrimitiveProcessor::emitGeometryShader(const GrCCPRCoverageProcessor& proc,
- GrGLSLGeometryBuilder* g, const char* rtAdjust) const {
- g->declareGlobal(fGeomWind);
- this->emitWind(g, rtAdjust, fGeomWind.c_str());
-
- SkString emitVertexFn;
- SkSTArray<2, GrShaderVar> emitArgs;
- const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType,
- GrShaderVar::kNonArray).c_str();
- const char* coverage = emitArgs.emplace_back("coverage", kFloat_GrSLType,
- GrShaderVar::kNonArray).c_str();
- g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
- SkString fnBody;
- this->emitPerVertexGeometryCode(&fnBody, position, coverage, fGeomWind.c_str());
- if (fFragWind.gsOut()) {
- fnBody.appendf("%s = %s;", fFragWind.gsOut(), fGeomWind.c_str());
- }
- if (fFragCoverageTimesWind.gsOut()) {
- fnBody.appendf("%s = %s * %s;",
- fFragCoverageTimesWind.gsOut(), coverage, fGeomWind.c_str());
- }
- fnBody.append ("sk_Position = float4(position, 0, 1);");
- fnBody.append ("EmitVertex();");
- return fnBody;
- }().c_str(), &emitVertexFn);
-
- g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
-
+ f->codeAppendf("%s = half4(1);", skOutputCoverage);
#ifdef SK_DEBUG
if (proc.debugVisualizationsEnabled()) {
- g->codeAppendf("bloat *= %f;", proc.debugBloat());
+ f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);",
+ skOutputColor, skOutputColor, skOutputColor);
}
#endif
-
- return this->onEmitGeometryShader(g, emitVertexFn.c_str(), fGeomWind.c_str(), rtAdjust);
}
-int PrimitiveProcessor::emitHullGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
- const char* polygonPts, int numSides,
- const char* wedgeIdx, const char* midpoint) const {
- SkASSERT(numSides >= 3);
-
- if (!midpoint) {
- g->codeAppendf("float2 midpoint = %s * float%i(%f);",
- polygonPts, numSides, 1.0 / numSides);
- midpoint = "midpoint";
- }
-
- g->codeAppendf("int previdx = (%s + %i) %% %i, "
- "nextidx = (%s + 1) %% %i;",
- wedgeIdx, numSides - 1, numSides, wedgeIdx, numSides);
-
- g->codeAppendf("float2 self = %s[%s];"
- "int leftidx = %s > 0 ? previdx : nextidx;"
- "int rightidx = %s > 0 ? nextidx : previdx;",
- polygonPts, wedgeIdx, fGeomWind.c_str(), fGeomWind.c_str());
-
- // Which quadrant does the vector from self -> right fall into?
- g->codeAppendf("float2 right = %s[rightidx];", polygonPts);
- if (3 == numSides) {
- // TODO: evaluate perf gains.
- g->codeAppend ("float2 qsr = sign(right - self);");
- } else {
- SkASSERT(4 == numSides);
- g->codeAppendf("float2 diag = %s[(%s + 2) %% 4];", polygonPts, wedgeIdx);
- g->codeAppend ("float2 qsr = sign((right != self ? right : diag) - self);");
- }
-
- // Which quadrant does the vector from left -> self fall into?
- g->codeAppendf("float2 qls = sign(self - %s[leftidx]);", polygonPts);
-
- // d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
- // TODO: evaluate perf gains.
- const char* dr2 = "dr";
- if (3 == numSides) {
- // TODO: evaluate perf gains.
- g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
- "qsr.x != 0 ? -qsr.x : +qsr.y);");
- g->codeAppend ("float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
- "qsr.x != 0 ? -qsr.x : -qsr.y);");
- g->codeAppend ("float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
- "qls.x != 0 ? -qls.x : +qls.y);");
- dr2 = "dr2";
- } else {
- g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
- "qsr.x != 0 ? -qsr.x : 1);");
- g->codeAppend ("float2 dl = (qls == float2(0)) ? dr : "
- "float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
- }
- g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
-
- // Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
- // Each invocation emits a different third.
- g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
- g->codeAppendf("%s(%s, 1);", emitVertexFn, midpoint);
- g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
- g->codeAppend ("if (any(dnotequal)) {");
- g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
- g->codeAppend ("}");
- g->codeAppend ("if (all(dnotequal)) {");
- g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
- g->codeAppend ("}");
- g->codeAppend ("EndPrimitive();");
-
- return 5;
-}
-
-int PrimitiveProcessor::emitEdgeGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
- const char* leftPt, const char* rightPt,
- const char* distanceEquation) const {
- if (!distanceEquation) {
- this->emitEdgeDistanceEquation(g, leftPt, rightPt, "float3 edge_distance_equation");
- distanceEquation = "edge_distance_equation";
- }
-
- // qlr is defined in emitEdgeDistanceEquation.
- g->codeAppendf("float2x2 endpts = float2x2(%s - bloat * qlr, %s + bloat * qlr);",
- leftPt, rightPt);
- g->codeAppendf("half2 endpts_coverage = %s.xy * endpts + %s.z;",
- distanceEquation, distanceEquation);
-
- // d1 is defined in emitEdgeDistanceEquation.
- g->codeAppend ("float2 d2 = d1;");
- g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
- g->codeAppend ("if (aligned) {");
- g->codeAppend ( "d1 -= qlr;");
- g->codeAppend ( "d2 += qlr;");
- g->codeAppend ("}");
-
- // Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
- // invocation emits a different edge. Emit negative coverage that subtracts the appropiate
- // amount back out from the hull we drew above.
- g->codeAppend ("if (!aligned) {");
- g->codeAppendf( "%s(endpts[0], endpts_coverage[0]);", emitVertexFn);
- g->codeAppend ("}");
- g->codeAppendf("%s(%s + bloat * d1, -1);", emitVertexFn, leftPt);
- g->codeAppendf("%s(%s - bloat * d2, 0);", emitVertexFn, leftPt);
- g->codeAppendf("%s(%s + bloat * d2, -1);", emitVertexFn, rightPt);
- g->codeAppendf("%s(%s - bloat * d1, 0);", emitVertexFn, rightPt);
- g->codeAppend ("if (!aligned) {");
- g->codeAppendf( "%s(endpts[1], endpts_coverage[1]);", emitVertexFn);
- g->codeAppend ("}");
- g->codeAppend ("EndPrimitive();");
-
- return 6;
-}
-
-void PrimitiveProcessor::emitEdgeDistanceEquation(GrGLSLGeometryBuilder* g,
- const char* leftPt, const char* rightPt,
- const char* outputDistanceEquation) const {
+void GrCCPRCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLShaderBuilder* s,
+ const char* leftPt,
+ const char* rightPt,
+ const char* outputDistanceEquation) {
// Which quadrant does the vector from left -> right fall into?
- g->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
- g->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
+ s->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
+ s->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
- g->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
+ s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
rightPt, leftPt, leftPt, rightPt);
- g->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
+ s->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
leftPt, leftPt);
// Clamp for when n=0. wind=0 when n=0 so as long as we don't get Inf or NaN we are fine.
- g->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
+ s->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
- g->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
+ s->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
}
-int PrimitiveProcessor::emitCornerGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
- const char* pt) const {
- g->codeAppendf("%s(%s + float2(-bloat.x, -bloat.y), 1);", emitVertexFn, pt);
- g->codeAppendf("%s(%s + float2(-bloat.x, +bloat.y), 1);", emitVertexFn, pt);
- g->codeAppendf("%s(%s + float2(+bloat.x, -bloat.y), 1);", emitVertexFn, pt);
- g->codeAppendf("%s(%s + float2(+bloat.x, +bloat.y), 1);", emitVertexFn, pt);
- g->codeAppend ("EndPrimitive();");
-
- return 4;
-}
-
-void PrimitiveProcessor::emitCoverage(const GrCCPRCoverageProcessor& proc, GrGLSLFragmentBuilder* f,
- const char* outputColor, const char* outputCoverage) const {
- switch (fCoverageType) {
- case CoverageType::kOne:
- f->codeAppendf("%s.a = %s;", outputColor, fFragWind.fsIn());
- break;
- case CoverageType::kInterpolated:
- f->codeAppendf("%s.a = %s;", outputColor, fFragCoverageTimesWind.fsIn());
- break;
- case CoverageType::kShader:
- f->codeAppendf("half coverage = 0;");
- this->emitShaderCoverage(f, "coverage");
- f->codeAppendf("%s.a = coverage * %s;", outputColor, fFragWind.fsIn());
- break;
- }
-
- f->codeAppendf("%s = half4(1);", outputCoverage);
-
-#ifdef SK_DEBUG
- if (proc.debugVisualizationsEnabled()) {
- f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);", outputColor, outputColor, outputColor);
- }
-#endif
-}
-
-int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
- const char* samplesName) const {
+int GrCCPRCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentBuilder* 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]("
@@ -342,9 +110,74 @@ int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
#endif
}
+void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
+ GrProcessorKeyBuilder* b) const {
+ b->add32((int)fRenderPass);
+}
+
+GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
+ std::unique_ptr<Shader> shader;
+ switch (fRenderPass) {
+ using CubicType = GrCCPRCubicShader::CubicType;
+ case RenderPass::kTriangleHulls:
+ shader = skstd::make_unique<GrCCPRTriangleHullShader>();
+ break;
+ case RenderPass::kTriangleEdges:
+ shader = skstd::make_unique<GrCCPRTriangleEdgeShader>();
+ break;
+ case RenderPass::kTriangleCorners:
+ shader = skstd::make_unique<GrCCPRTriangleCornerShader>();
+ break;
+ case RenderPass::kQuadraticHulls:
+ shader = skstd::make_unique<GrCCPRQuadraticHullShader>();
+ break;
+ case RenderPass::kQuadraticCorners:
+ shader = skstd::make_unique<GrCCPRQuadraticCornerShader>();
+ break;
+ case RenderPass::kSerpentineHulls:
+ shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kSerpentine);
+ break;
+ case RenderPass::kLoopHulls:
+ shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kLoop);
+ break;
+ case RenderPass::kSerpentineCorners:
+ shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kSerpentine);
+ break;
+ case RenderPass::kLoopCorners:
+ shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kLoop);
+ break;
+ }
+ return CreateGSImpl(std::move(shader));
+}
+
+const char* GrCCPRCoverageProcessor::GetRenderPassName(RenderPass renderPass) {
+ switch (renderPass) {
+ case RenderPass::kTriangleHulls:
+ return "RenderPass::kTriangleHulls";
+ case RenderPass::kTriangleEdges:
+ return "RenderPass::kTriangleEdges";
+ case RenderPass::kTriangleCorners:
+ return "RenderPass::kTriangleCorners";
+ case RenderPass::kQuadraticHulls:
+ return "RenderPass::kQuadraticHulls";
+ case RenderPass::kQuadraticCorners:
+ return "RenderPass::kQuadraticCorners";
+ case RenderPass::kSerpentineHulls:
+ return "RenderPass::kSerpentineHulls";
+ case RenderPass::kLoopHulls:
+ return "RenderPass::kLoopHulls";
+ case RenderPass::kSerpentineCorners:
+ return "RenderPass::kSerpentineCorners";
+ case RenderPass::kLoopCorners:
+ return "RenderPass::kLoopCorners";
+ }
+ SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
+ return nullptr;
+}
+
#ifdef SK_DEBUG
-#include "GrRenderTarget.h"
+#include "GrRenderTargetProxy.h"
void GrCCPRCoverageProcessor::Validate(GrRenderTargetProxy* atlasProxy) {
SkASSERT(kAtlasOrigin == atlasProxy->origin());
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.h b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
index f02b7422c8..f2c5f435e0 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -12,21 +12,22 @@
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLVarying.h"
-class GrGLSLFragmentBuilder;
+class GrGLSLPPFragmentBuilder;
+class GrGLSLShaderBuilder;
/**
* This is the geometry processor for the simple convex primitive shapes (triangles and closed curve
* segments) from which ccpr paths are composed. The output is a single-channel alpha value,
- * positive for clockwise primitives and negative for counter-clockwise, that indicates coverage.
+ * positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
*
- * The caller is responsible to render all modes for all applicable primitives into a cleared,
- * floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's
- * primitives have been drawn, the render target contains a composite coverage count that can then
- * be used to draw the path (see GrCCPRPathProcessor).
+ * The caller is responsible to execute all render passes for all applicable primitives into a
+ * cleared, floating point, alpha-only render target using SkBlendMode::kPlus (see RenderPass
+ * below). Once all of a path's primitives have been drawn, the render target contains a composite
+ * coverage count that can then be used to draw the path (see GrCCPRPathProcessor).
*
- * Caller provides the primitives' (x,y) points in an fp32x2 (RG) texel buffer, and an instance
- * buffer with a single int32x4 attrib (for triangles) or int32x2 (for curves) defined below. There
- * are no vertex attribs.
+ * Caller provides the primitives' (x,y) input points in an fp32x2 (RG) texel buffer, and an
+ * instance buffer with a single int32x4 attrib (for triangles) or int32x2 (for curves) defined
+ * below. There are no vertex attribs.
*
* Draw calls are instanced, with one vertex per bezier point (3 for triangles). They use the
* corresponding GrPrimitiveType as defined below.
@@ -56,7 +57,12 @@ public:
GR_STATIC_ASSERT(2 * 4 == sizeof(CurveInstance));
- enum class Mode {
+ /**
+ * All primitive shapes (triangles and convex closed curve segments) require more than one
+ * render pass. 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 {
// Triangles.
kTriangleHulls,
kTriangleEdges,
@@ -72,22 +78,132 @@ public:
kSerpentineCorners,
kLoopCorners
};
- static constexpr GrVertexAttribType InstanceArrayFormat(Mode mode) {
- return mode < Mode::kQuadraticHulls ? kInt4_GrVertexAttribType : kInt2_GrVertexAttribType;
- }
- static const char* GetProcessorName(Mode);
- GrCCPRCoverageProcessor(Mode, GrBuffer* pointsBuffer);
+ static const char* GetRenderPassName(RenderPass);
+
+ /**
+ * This serves as the base class for each RenderPass's Shader. It indicates what type of
+ * geometry the Impl should generate and provides implementation-independent code to process
+ * the inputs and calculate coverage in the fragment Shader.
+ */
+ class Shader {
+ public:
+ using TexelBufferHandle = GrGLSLGeometryProcessor::TexelBufferHandle;
+
+ // This enum specifies the type of geometry that should be generated for a Shader instance.
+ // Subclasses are limited to three built-in types of geometry to choose from:
+ enum class GeometryType {
+ // Generates a conservative raster hull around the input points. This is the geometry
+ // that causes a pixel to be rasterized if it is touched anywhere by the input polygon.
+ // Coverage is +1 all around.
+ //
+ // Logically, the conservative raster hull is equivalent to the convex hull of pixel
+ // size boxes centered around each input point.
+ kHull,
+
+ // Generates the conservative rasters of the input edges (i.e. convex hull of two
+ // pixel-size boxes centered on both endpoints). Coverage is -1 on the outside border of
+ // the edge geometry and 0 on the inside. This is the only geometry type that associates
+ // coverage values with the output points. It effectively converts a jagged conservative
+ // raster edge into a smooth antialiased edge.
+ kEdges,
+
+ // Generates the conservative rasters of the corners specified by the geometry provider
+ // (i.e. pixel-size box centered on the corner point). Coverage is +1 all around.
+ kCorners
+ };
+
+ virtual GeometryType getGeometryType() const = 0;
+ virtual int getNumInputPoints() const = 0;
+
+ // Returns the number of independent geometric segments to generate for the render pass
+ // (number of wedges for a hull, number of edges, or number of corners.)
+ virtual int getNumSegments() const = 0;
+
+ // Appends an expression that fetches input point # "pointId" from the texel buffer.
+ virtual void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const = 0;
+
+ // Determines the winding direction of the primitive. The subclass must write a value of
+ // either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
+ virtual void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
+ const char* outputWind) const = 0;
+
+ union GeometryVars {
+ struct {
+ const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
+ const char* fAlternateMidpoint; // float2 (if left null, finds euclidean midpoint).
+ } fHullVars;
+
+ struct {
+ const char* fPoint; // float2
+ } fCornerVars;
+
+ GeometryVars() { memset(this, 0, sizeof(*this)); }
+ };
+
+ // Called before generating geometry. Subclasses must fill out the applicable fields in
+ // GeometryVars (if any), and may also use this opportunity to setup internal member
+ // variables that will be needed during onEmitVaryings (e.g. transformation matrices).
+ virtual void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
+ const char* bloat, const char* wind, const char* rtAdjust,
+ GeometryVars*) const {}
+
+ void emitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
+ const char* coverage, const char* wind);
+
+ void emitFragmentCode(const GrCCPRCoverageProcessor& proc, GrGLSLPPFragmentBuilder*,
+ 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 (see emitEdgeGeometry).
+ static void EmitEdgeDistanceEquation(GrGLSLShaderBuilder*, const char* leftPt,
+ const char* rightPt,
+ const char* outputDistanceEquation);
+
+ // 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(GrGLSLPPFragmentBuilder* f, const char* samplesName);
+
+ virtual ~Shader() {}
+
+ protected:
+ enum class WindHandling : bool {
+ kHandled,
+ kNotHandled
+ };
+
+ // Here the subclass adds its internal varyings to the handler and produces code to
+ // initialize those varyings from a given position, coverage value, and wind.
+ //
+ // Returns whether the subclass will handle wind modulation or if this base class should
+ // take charge of multiplying the final coverage output by "wind".
+ //
+ // NOTE: the coverage parameter is only relevant for edges (see comments in GeometryType).
+ // Otherwise it is +1 all around.
+ virtual WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
+ const char* position, const char* coverage,
+ const char* wind) = 0;
+
+ // Emits the fragment code that calculates a pixel's coverage value. If using
+ // WindHandling::kHandled, this value must be signed appropriately.
+ virtual void onEmitFragmentCode(GrGLSLPPFragmentBuilder*,
+ const char* outputCoverage) const = 0;
+
+ private:
+ GrGLSLGeoToFrag fWind{kHalf_GrSLType};
+ };
+
+ GrCCPRCoverageProcessor(RenderPass, GrBuffer* pointsBuffer);
const char* instanceAttrib() const { return fInstanceAttrib.fName; }
- int atlasOffsetIdx() const {
- return kInt4_GrVertexAttribType == InstanceArrayFormat(fMode) ? 3 : 1;
- }
- const char* name() const override { return GetProcessorName(fMode); }
+ const char* name() const override { return GetRenderPassName(fRenderPass); }
SkString dumpInfo() const override {
return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
}
-
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
@@ -101,152 +217,27 @@ public:
static void Validate(GrRenderTargetProxy* atlasProxy);
#endif
- class PrimitiveProcessor;
+ class GSImpl;
private:
- const Mode fMode;
- const Attribute& fInstanceAttrib;
- BufferAccess fPointsBufferAccess;
- SkDEBUGCODE(float fDebugBloat = false;)
-
- typedef GrGeometryProcessor INHERITED;
-};
-
-/**
- * This class represents the actual SKSL implementation for the various primitives and modes of
- * GrCCPRCoverageProcessor.
- */
-class GrCCPRCoverageProcessor::PrimitiveProcessor : public GrGLSLGeometryProcessor {
-protected:
// Slightly undershoot a bloat radius of 0.5 so vertices that fall on integer boundaries don't
// accidentally bleed into neighbor pixels.
static constexpr float kAABloatRadius = 0.491111f;
- // Specifies how the fragment shader should calculate sk_FragColor.a.
- enum class CoverageType {
- kOne, // Output +1 all around, modulated by wind.
- kInterpolated, // Interpolate the coverage values that the geometry shader associates with
- // each point, modulated by wind.
- kShader // Call emitShaderCoverage and let the subclass decide, then a modulate by wind.
- };
-
- PrimitiveProcessor(CoverageType coverageType)
- : fCoverageType(coverageType)
- , fGeomWind("wind", kHalf_GrSLType, GrShaderVar::kNonArray, kLow_GrSLPrecision)
- , fFragWind(kHalf_GrSLType)
- , fFragCoverageTimesWind(kHalf_GrSLType) {}
-
- // Called before generating shader code. Subclass should add its custom varyings to the handler
- // and update its corresponding internal member variables.
- virtual void resetVaryings(GrGLSLVaryingHandler*) {}
-
- // Here the subclass fetches its vertex from the texel buffer, translates by atlasOffset, and
- // sets "fPositionVar" in the GrGPArgs.
- virtual void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
- const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
- const char* rtAdjust, GrGPArgs*) const = 0;
-
- // Here the subclass determines the winding direction of its primitive. It must write a value of
- // either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
- virtual void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust,
- const char* outputWind) const = 0;
-
- // This is where the subclass generates the actual geometry to be rasterized by hardware:
- //
- // emitVertexFn(point1, coverage);
- // emitVertexFn(point2, coverage);
- // ...
- // EndPrimitive();
- //
- // Generally a subclass will want to use emitHullGeometry and/or emitEdgeGeometry rather than
- // calling emitVertexFn directly.
- //
- // Subclass must also call GrGLSLGeometryBuilder::configure.
- virtual void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* wind, const char* rtAdjust) const = 0;
-
- // This is a hook to inject code in the geometry shader's "emitVertex" function. Subclass
- // should use this to write values to its custom varyings.
- // NOTE: even flat varyings should be rewritten at each vertex.
- virtual void emitPerVertexGeometryCode(SkString* fnBody, const char* position,
- const char* coverage, const char* wind) const {}
-
- // Called when the subclass has selected CoverageType::kShader. Primitives should produce
- // coverage values between +0..1. Base class modulates the sign for wind.
- // TODO: subclasses might have good spots to stuff the winding information without burning a
- // whole new varying slot. Consider requiring them to generate the correct coverage sign.
- virtual void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const {
- SK_ABORT("Shader coverage not implemented when using CoverageType::kShader.");
- }
-
- // Emits one wedge of the conservative raster hull of a convex polygon. The complete hull has
- // one wedge for each side of the polygon (i.e. call this N times, generally from different
- // geometry shader invocations). Coverage is +1 all around.
- //
- // Logically, the conservative raster hull is equivalent to the convex hull of pixel-size boxes
- // centered on the vertices.
- //
- // Geometry shader must be configured to output triangle strips.
- //
- // Returns the maximum number of vertices that will be emitted.
- int emitHullGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* polygonPts,
- int numSides, const char* wedgeIdx, const char* midpoint = nullptr) const;
-
- // Emits the conservative raster of an edge (i.e. convex hull of two pixel-size boxes centered
- // on the endpoints). Coverage is -1 on the outside border of the edge geometry and 0 on the
- // inside. This effectively converts a jagged conservative raster edge into a smooth antialiased
- // edge when using CoverageType::kInterpolated.
- //
- // If the subclass has already called emitEdgeDistanceEquation, then provide the distance
- // equation. Otherwise this function will call emitEdgeDistanceEquation implicitly.
- //
- // Geometry shader must be configured to output triangle strips.
- //
- // Returns the maximum number of vertices that will be emitted.
- int emitEdgeGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* leftPt,
- const char* rightPt, const char* distanceEquation = nullptr) 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 (see emitEdgeGeometry).
- void emitEdgeDistanceEquation(GrGLSLGeometryBuilder*, const char* leftPt, const char* rightPt,
- const char* outputDistanceEquation) const;
-
- // Emits the conservative raster of a single point (i.e. pixel-size box centered on the point).
- // Coverage is +1 all around.
- //
- // Geometry shader must be configured to output triangle strips.
- //
- // Returns the number of vertices that were emitted.
- int emitCornerGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* pt) const;
-
- // 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.
- int defineSoftSampleLocations(GrGLSLFragmentBuilder*, const char* samplesName) const;
+ static GrGLSLPrimitiveProcessor* CreateGSImpl(std::unique_ptr<Shader>);
-private:
- void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
- FPCoordTransformIter&& transformIter) final {
- this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+ int atlasOffsetIdx() const {
+ SkASSERT(kInt2_GrVertexAttribType == fInstanceAttrib.fType ||
+ kInt4_GrVertexAttribType == fInstanceAttrib.fType);
+ return kInt4_GrVertexAttribType == fInstanceAttrib.fType ? 3 : 1;
}
- void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final;
-
- void emitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
- const TexelBufferHandle& pointsBuffer, const char* rtAdjust,
- GrGPArgs* gpArgs) const;
- void emitGeometryShader(const GrCCPRCoverageProcessor&, GrGLSLGeometryBuilder*,
- const char* rtAdjust) const;
- void emitCoverage(const GrCCPRCoverageProcessor&, GrGLSLFragmentBuilder*,
- const char* outputColor, const char* outputCoverage) const;
-
- const CoverageType fCoverageType;
- GrShaderVar fGeomWind;
- GrGLSLGeoToFrag fFragWind;
- GrGLSLGeoToFrag fFragCoverageTimesWind;
+ const RenderPass fRenderPass;
+ const Attribute& fInstanceAttrib;
+ BufferAccess fPointsBufferAccess;
+ SkDEBUGCODE(float fDebugBloat = 0;)
- typedef GrGLSLGeometryProcessor INHERITED;
+ typedef GrGeometryProcessor INHERITED;
};
#endif
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
new file mode 100644
index 0000000000..b9a38e732a
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCCPRCoverageProcessor.h"
+
+#include "glsl/GrGLSLGeometryShaderBuilder.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
+
+using Shader = GrCCPRCoverageProcessor::Shader;
+
+/**
+ * This class and its subclasses implement the coverage processor with geometry shaders.
+ */
+class GrCCPRCoverageProcessor::GSImpl : public GrGLSLGeometryProcessor {
+protected:
+ GSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
+
+ void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+ FPCoordTransformIter&& transformIter) final {
+ this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+ }
+
+ void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
+ const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
+
+ // Vertex shader.
+ GrGLSLVertexBuilder* v = args.fVertBuilder;
+ // The Intel GLSL compiler hits an internal assertion if we index the input attrib itself
+ // with sk_VertexID.
+ v->codeAppendf("int pointID = sk_VertexID;");
+ v->codeAppend ("float2 self = ");
+ fShader->appendInputPointFetch(proc, v, args.fTexelBuffers[0], "pointID");
+ v->codeAppend (".xy;");
+ v->codeAppendf("int packedoffset = %s[%i];",
+ proc.fInstanceAttrib.fName, proc.atlasOffsetIdx());
+ v->codeAppend ("float2 atlasoffset = float2((packedoffset << 16) >> 16, "
+ "packedoffset >> 16);");
+ v->codeAppend ("self += atlasoffset;");
+ gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
+
+ // Geometry shader.
+ GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+ this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName);
+ varyingHandler->emitAttributes(proc);
+ SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
+
+ // Fragment shader.
+ fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
+ }
+
+ void emitGeometryShader(const GrCCPRCoverageProcessor& proc,
+ GrGLSLVaryingHandler* varyingHandler, GrGLSLGeometryBuilder* g,
+ const char* rtAdjust) const {
+ using InputType = GrGLSLGeometryBuilder::InputType;
+ using OutputType = GrGLSLGeometryBuilder::OutputType;
+
+ int numPts = fShader->getNumInputPoints();
+ SkASSERT(3 == numPts || 4 == numPts);
+
+ g->codeAppendf("float%ix2 pts = float%ix2(", numPts, numPts);
+ for (int i = 0; i < numPts; ++i) {
+ g->codeAppend (i ? ", " : "");
+ g->codeAppendf("sk_in[%i].sk_Position.xy", i);
+ }
+ g->codeAppend (");");
+
+ GrShaderVar wind("wind", kHalf_GrSLType);
+ g->declareGlobal(wind);
+ fShader->emitWind(g, "pts", rtAdjust, wind.c_str());
+
+ SkString emitVertexFn;
+ SkSTArray<2, GrShaderVar> emitArgs;
+ const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
+ const char* coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
+ g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
+ SkString fnBody;
+ fShader->emitVaryings(varyingHandler, &fnBody, position, coverage, wind.c_str());
+ fnBody.append("sk_Position = float4(position, 0, 1);");
+ fnBody.append("EmitVertex();");
+ return fnBody;
+ }().c_str(), &emitVertexFn);
+
+ g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
+#ifdef SK_DEBUG
+ if (proc.debugVisualizationsEnabled()) {
+ g->codeAppendf("bloat *= %f;", proc.debugBloat());
+ }
+#endif
+
+ Shader::GeometryVars vars;
+ fShader->emitSetupCode(g, "pts", "sk_InvocationID", "bloat", wind.c_str(), rtAdjust, &vars);
+ int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), rtAdjust, vars);
+
+ int numInputPoints = fShader->getNumInputPoints();
+ SkASSERT(3 == numInputPoints || 4 == numInputPoints);
+ InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
+ : InputType::kLinesAdjacency;
+
+ g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
+ }
+
+ virtual int onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
+ const char* emitVertexFn, const char* rtAdjust,
+ const Shader::GeometryVars&) const = 0;
+
+ virtual ~GSImpl() {}
+
+ const std::unique_ptr<Shader> fShader;
+
+ typedef GrGLSLGeometryProcessor INHERITED;
+};
+
+class GSHullImpl : public GrCCPRCoverageProcessor::GSImpl {
+public:
+ GSHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+
+ int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
+ const char* emitVertexFn, const char* rtAdjust,
+ const Shader::GeometryVars& vars) const override {
+ int numSides = fShader->getNumSegments();
+ SkASSERT(numSides >= 3);
+
+ const char* hullPts = vars.fHullVars.fAlternatePoints;
+ if (!hullPts) {
+ SkASSERT(fShader->getNumInputPoints() == numSides);
+ hullPts = "pts";
+ }
+
+ const char* midpoint = vars.fHullVars.fAlternateMidpoint;
+ if (!midpoint) {
+ g->codeAppendf("float2 midpoint = %s * float%i(%f);", hullPts, numSides, 1.0/numSides);
+ midpoint = "midpoint";
+ }
+
+ g->codeAppendf("int previdx = (sk_InvocationID + %i) %% %i, "
+ "nextidx = (sk_InvocationID + 1) %% %i;",
+ numSides - 1, numSides, numSides);
+
+ g->codeAppendf("float2 self = %s[sk_InvocationID];"
+ "int leftidx = %s > 0 ? previdx : nextidx;"
+ "int rightidx = %s > 0 ? nextidx : previdx;",
+ hullPts, wind.c_str(), wind.c_str());
+
+ // Which quadrant does the vector from self -> right fall into?
+ g->codeAppendf("float2 right = %s[rightidx];", hullPts);
+ if (3 == numSides) {
+ // TODO: evaluate perf gains.
+ g->codeAppend ("float2 qsr = sign(right - self);");
+ } else {
+ SkASSERT(4 == numSides);
+ g->codeAppendf("float2 diag = %s[(sk_InvocationID + 2) %% 4];", hullPts);
+ g->codeAppend ("float2 qsr = sign((right != self ? right : diag) - self);");
+ }
+
+ // Which quadrant does the vector from left -> self fall into?
+ g->codeAppendf("float2 qls = sign(self - %s[leftidx]);", hullPts);
+
+ // d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
+ // TODO: evaluate perf gains.
+ const char* dr2 = "dr";
+ if (3 == numSides) {
+ // TODO: evaluate perf gains.
+ g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
+ "qsr.x != 0 ? -qsr.x : +qsr.y);");
+ g->codeAppend ("float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
+ "qsr.x != 0 ? -qsr.x : -qsr.y);");
+ g->codeAppend ("float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
+ "qls.x != 0 ? -qls.x : +qls.y);");
+ dr2 = "dr2";
+ } else {
+ g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
+ "qsr.x != 0 ? -qsr.x : 1);");
+ g->codeAppend ("float2 dl = (qls == float2(0)) ? dr : "
+ "float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
+ }
+ g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
+
+ // Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
+ // Each invocation emits a different third.
+ g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
+ g->codeAppendf("%s(%s, 1);", emitVertexFn, midpoint);
+ g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
+ g->codeAppend ("if (any(dnotequal)) {");
+ g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
+ g->codeAppend ("}");
+ g->codeAppend ("if (all(dnotequal)) {");
+ g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
+ g->codeAppend ("}");
+ g->codeAppend ("EndPrimitive();");
+
+ return 5;
+ }
+};
+
+class GSEdgeImpl : public GrCCPRCoverageProcessor::GSImpl {
+public:
+ GSEdgeImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+
+ int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
+ const char* emitVertexFn, const char* rtAdjust,
+ const Shader::GeometryVars&) const override {
+ int numSides = fShader->getNumSegments();
+
+ g->codeAppendf("int nextidx = (sk_InvocationID + 1) %% %i;", numSides);
+ g->codeAppendf("float2 left = pts[%s > 0 ? sk_InvocationID : nextidx], "
+ "right = pts[%s > 0 ? nextidx : sk_InvocationID];",
+ wind.c_str(), wind.c_str());
+
+ Shader::EmitEdgeDistanceEquation(g, "left", "right", "float3 edge_distance_equation");
+
+ // qlr is defined in EmitEdgeDistanceEquation. TODO: address in a followup CL!
+ g->codeAppend ("float2x2 outer_pts = float2x2(left - bloat * qlr, right + bloat * qlr);");
+ g->codeAppend ("half2 outer_coverage = edge_distance_equation.xy * outer_pts + "
+ "edge_distance_equation.z;");
+
+ // d1 is defined in EmitEdgeDistanceEquation. TODO: address in a followup CL!
+ g->codeAppend ("float2 d2 = d1;");
+ g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
+ g->codeAppend ("if (aligned) {");
+ g->codeAppend ( "d1 -= qlr;");
+ g->codeAppend ( "d2 += qlr;");
+ g->codeAppend ("}");
+
+ // Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
+ // invocation emits a different edge. Emit negative coverage that subtracts the appropiate
+ // amount back out from the hull we drew above.
+ g->codeAppend ("if (!aligned) {");
+ g->codeAppendf( "%s(outer_pts[0], outer_coverage[0]);", emitVertexFn);
+ g->codeAppend ("}");
+ g->codeAppendf("%s(left + bloat * d1, -1);", emitVertexFn);
+ g->codeAppendf("%s(left - bloat * d2, 0);", emitVertexFn);
+ g->codeAppendf("%s(right + bloat * d2, -1);", emitVertexFn);
+ g->codeAppendf("%s(right - bloat * d1, 0);", emitVertexFn);
+ g->codeAppend ("if (!aligned) {");
+ g->codeAppendf( "%s(outer_pts[1], outer_coverage[1]);", emitVertexFn);
+ g->codeAppend ("}");
+ g->codeAppend ("EndPrimitive();");
+
+ return 6;
+ }
+};
+
+class GSCornerImpl : public GrCCPRCoverageProcessor::GSImpl {
+public:
+ GSCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+
+ int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
+ const char* emitVertexFn, const char* rtAdjust,
+ const Shader::GeometryVars& vars) const override {
+ const char* corner = vars.fCornerVars.fPoint;
+ SkASSERT(corner);
+
+ g->codeAppendf("%s(%s + float2(-bloat.x, -bloat.y), 1);", emitVertexFn, corner);
+ g->codeAppendf("%s(%s + float2(-bloat.x, +bloat.y), 1);", emitVertexFn, corner);
+ g->codeAppendf("%s(%s + float2(+bloat.x, -bloat.y), 1);", emitVertexFn, corner);
+ g->codeAppendf("%s(%s + float2(+bloat.x, +bloat.y), 1);", emitVertexFn, corner);
+ g->codeAppend ("EndPrimitive();");
+
+ return 4;
+ }
+};
+
+GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::CreateGSImpl(std::unique_ptr<Shader> shader) {
+ switch (shader->getGeometryType()) {
+ case Shader::GeometryType::kHull:
+ return new GSHullImpl(std::move(shader));
+ case Shader::GeometryType::kEdges:
+ return new GSEdgeImpl(std::move(shader));
+ case Shader::GeometryType::kCorners:
+ return new GSCornerImpl(std::move(shader));
+ }
+ SK_ABORT("Unexpected Shader::GeometryType.");
+ return nullptr;
+}
diff --git a/src/gpu/ccpr/GrCCPRCubicProcessor.cpp b/src/gpu/ccpr/GrCCPRCubicProcessor.cpp
deleted file mode 100644
index 8b4c405961..0000000000
--- a/src/gpu/ccpr/GrCCPRCubicProcessor.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrCCPRCubicProcessor.h"
-
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
-
-void GrCCPRCubicProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
- GrGLSLVertexBuilder* v,
- const TexelBufferHandle& pointsBuffer,
- const char* atlasOffset, const char* rtAdjust,
- GrGPArgs* gpArgs) const {
- v->codeAppend ("float2 self = ");
- v->appendTexelFetch(pointsBuffer,
- SkStringPrintf("%s.x + sk_VertexID", proc.instanceAttrib()).c_str());
- v->codeAppendf(".xy + %s;", atlasOffset);
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
-}
-
-void GrCCPRCubicProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust,
- const char* outputWind) const {
- // We will define bezierpts in onEmitGeometryShader.
- g->codeAppend ("float area_times_2 = "
- "determinant(float3x3(1, bezierpts[0], "
- "1, bezierpts[2], "
- "0, bezierpts[3] - bezierpts[1]));");
- // Drop curves that are nearly flat. The KLM math becomes unstable in this case.
- g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[3] - bezierpts[0]) * %s.zx)) {",
- rtAdjust);
-#ifndef SK_BUILD_FOR_MAC
- g->codeAppend ( "return;");
-#else
- // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
- g->codeAppend ( "area_times_2 = 0;");
-#endif
- g->codeAppend ("}");
- g->codeAppendf("%s = sign(area_times_2);", outputWind);
-}
-
-void GrCCPRCubicProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
- const char* wind, const char* rtAdjust) const {
- // Prepend bezierpts at the start of the shader.
- g->codePrependf("float4x2 bezierpts = float4x2(sk_in[0].sk_Position.xy, "
- "sk_in[1].sk_Position.xy, "
- "sk_in[2].sk_Position.xy, "
- "sk_in[3].sk_Position.xy);");
-
- // Evaluate the cubic at T=.5 for an mid-ish point.
- g->codeAppendf("float2 midpoint = bezierpts * float4(.125, .375, .375, .125);");
-
- // Find the cubic's power basis coefficients.
- g->codeAppend ("float2x4 C = float4x4(-1, 3, -3, 1, "
- " 3, -6, 3, 0, "
- "-3, 3, 0, 0, "
- " 1, 0, 0, 0) * transpose(bezierpts);");
-
- // Find the cubic's inflection function.
- g->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
- g->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
- g->codeAppend ("float D1 = +determinant(float2x2(C));");
-
- // Calculate the KLM matrix.
- g->declareGlobal(fKLMMatrix);
- g->codeAppend ("float4 K, L, M;");
- g->codeAppend ("float2 l, m;");
- g->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
- if (CubicType::kSerpentine == fCubicType) {
- // This math also works out for the "cusp" and "cusp at infinity" cases.
- g->codeAppend ("float q = sqrt(max(3*discr, 0));");
- g->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
- g->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
- g->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
- g->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
- g->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
- g->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
- } else {
- g->codeAppend ("float q = sqrt(max(-discr, 0));");
- g->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
- g->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
- g->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
- g->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
- g->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
- g->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
- g->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
- }
- g->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
- g->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
- "C[1][0], C[1][middlerow], C[1][3], "
- " 0, 0, 1));");
- g->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
- "L[0], L[middlerow], L[3], "
- "M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
-
- // Orient the KLM matrix so we fill the correct side of the curve.
- g->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
- fKLMMatrix.c_str(), fKLMMatrix.c_str());
- g->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
- "0, orientation[0], 0, "
- "0, 0, orientation[1]);", fKLMMatrix.c_str());
-
- g->declareGlobal(fKLMDerivatives);
- g->codeAppendf("%s[0] = %s[0].xy * %s.xz;",
- fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
- g->codeAppendf("%s[1] = %s[1].xy * %s.xz;",
- fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
- g->codeAppendf("%s[2] = %s[2].xy * %s.xz;",
- fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
-
- // Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
- g->declareGlobal(fEdgeDistanceEquation);
- g->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind);
- g->codeAppendf("float2 edgept0 = bezierpts[edgeidx0];");
- g->codeAppendf("float2 edgept1 = bezierpts[3 - edgeidx0];");
- this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
-
- this->emitCubicGeometry(g, emitVertexFn, wind, rtAdjust);
-}
-
-void GrCCPRCubicProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position,
- const char* /*coverage*/,
- const char* /*wind*/) const {
- fnBody->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
- fnBody->appendf("float d = dot(float3(%s, 1), %s);",
- position, fEdgeDistanceEquation.c_str());
- fnBody->appendf("%s = float4(klm, d);", fKLMD.gsOut());
- this->onEmitPerVertexGeometryCode(fnBody);
-}
-
-void GrCCPRCubicHullProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
- const char* wind, const char* rtAdjust) const {
- // FIXME: we should clip this geometry at the tip of the curve.
- int maxVertices = this->emitHullGeometry(g, emitVertexFn, "bezierpts", 4, "sk_InvocationID",
- "midpoint");
-
- g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency,
- GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
- maxVertices, 4);
-}
-
-void GrCCPRCubicHullProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
- // "klm" was just defined by the base class.
- fnBody->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str());
- fnBody->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
- fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str());
-}
-
-void GrCCPRCubicHullProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* 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());
- f->codeAppend ("float f = k*k*k - l*m;");
- f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.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 closing edge.
-}
-
-void GrCCPRCubicCornerProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g,
- const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const {
- // We defined bezierpts in onEmitGeometryShader.
- g->declareGlobal(fEdgeDistanceDerivatives);
- g->codeAppendf("%s = %s.xy * %s.xz;",
- fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
-
- g->codeAppendf("float2 corner = bezierpts[sk_InvocationID * 3];");
- int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
-
- g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency,
- GrGLSLGeometryBuilder::OutputType::kTriangleStrip, numVertices, 2);
-}
-
-void GrCCPRCubicCornerProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
- fnBody->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
- fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
- fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
- fnBody->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
- fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
- fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
-
- // Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z).
- GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
-}
-
-void GrCCPRCubicCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* 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.
- f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
- fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
- 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 = this->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/GrCCPRCubicProcessor.h b/src/gpu/ccpr/GrCCPRCubicProcessor.h
deleted file mode 100644
index e44d8fb4b9..0000000000
--- a/src/gpu/ccpr/GrCCPRCubicProcessor.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCPRCubicProcessor_DEFINED
-#define GrCCPRCubicProcessor_DEFINED
-
-#include "ccpr/GrCCPRCoverageProcessor.h"
-
-class GrGLSLGeometryBuilder;
-
-/**
- * This class renders the coverage of convex closed cubic segments using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curves must be convex, monotonic with respect to the vector of their closing edge
- * [P3 - P0], and must not contain or be near any inflection points or loop intersections.
- * (Use GrCCPRGeometry.)
- */
-class GrCCPRCubicProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
-public:
- enum class CubicType {
- kSerpentine,
- kLoop
- };
-
- GrCCPRCubicProcessor(CubicType cubicType)
- : INHERITED(CoverageType::kShader)
- , fCubicType(cubicType)
- , fKLMMatrix("klm_matrix", kFloat3x3_GrSLType, GrShaderVar::kNonArray,
- kHigh_GrSLPrecision)
- , fKLMDerivatives("klm_derivatives", kFloat2_GrSLType, 3, kHigh_GrSLPrecision)
- , fEdgeDistanceEquation("edge_distance_equation", kFloat3_GrSLType,
- GrShaderVar::kNonArray, kHigh_GrSLPrecision)
- , fKLMD(kFloat4_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- varyingHandler->addVarying("klmd", &fKLMD, kHigh_GrSLPrecision);
- }
-
- void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
- const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
- const char* rtAdjust, GrGPArgs*) const override;
- void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
- void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const final;
- void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
- const char* wind) const final;
-
-protected:
- virtual void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* wind, const char* rtAdjust) const = 0;
- virtual void onEmitPerVertexGeometryCode(SkString* fnBody) const = 0;
-
- const CubicType fCubicType;
- GrShaderVar fKLMMatrix;
- GrShaderVar fKLMDerivatives;
- GrShaderVar fEdgeDistanceEquation;
- GrGLSLGeoToFrag fKLMD;
-
- typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
-};
-
-class GrCCPRCubicHullProcessor : public GrCCPRCubicProcessor {
-public:
- GrCCPRCubicHullProcessor(CubicType cubicType)
- : INHERITED(cubicType)
- , fGradMatrix(kFloat2x2_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- this->INHERITED::resetVaryings(varyingHandler);
- varyingHandler->addVarying("grad_matrix", &fGradMatrix, kHigh_GrSLPrecision);
- }
-
- void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* wind, const char* rtAdjust) const override;
- void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
- void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
-
-protected:
- GrGLSLGeoToFrag fGradMatrix;
-
- typedef GrCCPRCubicProcessor INHERITED;
-};
-
-class GrCCPRCubicCornerProcessor : public GrCCPRCubicProcessor {
-public:
- GrCCPRCubicCornerProcessor(CubicType cubicType)
- : INHERITED(cubicType)
- , fEdgeDistanceDerivatives("edge_distance_derivatives", kFloat2_GrSLType,
- GrShaderVar::kNonArray, kHigh_GrSLPrecision)
- , fdKLMDdx(kFloat4_GrSLType)
- , fdKLMDdy(kFloat4_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- this->INHERITED::resetVaryings(varyingHandler);
- varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx, kHigh_GrSLPrecision);
- varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy, kHigh_GrSLPrecision);
- }
-
- void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* wind, const char* rtAdjust) const override;
- void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
- void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
-
-protected:
- GrShaderVar fEdgeDistanceDerivatives;
- GrGLSLGeoToFrag fdKLMDdx;
- GrGLSLGeoToFrag fdKLMDdy;
-
- typedef GrCCPRCubicProcessor INHERITED;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.cpp b/src/gpu/ccpr/GrCCPRCubicShader.cpp
new file mode 100644
index 0000000000..3a30cee8df
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRCubicShader.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCCPRCubicShader.h"
+
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+void GrCCPRCubicShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
+ GrGLSLShaderBuilder* s,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const {
+ s->appendTexelFetch(pointsBuffer,
+ SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
+}
+
+void GrCCPRCubicShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
+ const char* rtAdjust, const char* outputWind) const {
+
+ s->codeAppendf("float area_times_2 = determinant(float3x3(1, %s[0], "
+ "1, %s[2], "
+ "0, %s[3] - %s[1]));",
+ pts, pts, pts, pts);
+ // Drop curves that are nearly flat. The KLM math becomes unstable in this case.
+ s->codeAppendf("if (2 * abs(area_times_2) < length((%s[3] - %s[0]) * %s.zx)) {",
+ pts, pts, rtAdjust);
+#ifndef SK_BUILD_FOR_MAC
+ s->codeAppend ( "return;");
+#else
+ // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
+ s->codeAppend ( "area_times_2 = 0;");
+#endif
+ s->codeAppend ("}");
+ s->codeAppendf("%s = sign(area_times_2);", outputWind);
+}
+
+void GrCCPRCubicShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
+ const char* segmentId, const char* bloat, const char* wind,
+ const char* rtAdjust, GeometryVars* vars) const {
+ // Evaluate the cubic at T=.5 for an mid-ish point.
+ s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
+
+ // Find the cubic's power basis coefficients.
+ s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
+ " 3, -6, 3, 0, "
+ "-3, 3, 0, 0, "
+ " 1, 0, 0, 0) * transpose(%s);", pts);
+
+ // Find the cubic's inflection function.
+ s->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
+ s->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
+ s->codeAppend ("float D1 = +determinant(float2x2(C));");
+
+ // Calculate the KLM matrix.
+ s->declareGlobal(fKLMMatrix);
+ s->codeAppend ("float4 K, L, M;");
+ s->codeAppend ("float2 l, m;");
+ s->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
+ if (CubicType::kSerpentine == fCubicType) {
+ // This math also works out for the "cusp" and "cusp at infinity" cases.
+ s->codeAppend ("float q = sqrt(max(3*discr, 0));");
+ s->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
+ s->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
+ s->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
+ s->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
+ s->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
+ s->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
+ } else {
+ s->codeAppend ("float q = sqrt(max(-discr, 0));");
+ s->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
+ s->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
+ s->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
+ s->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
+ s->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
+ s->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
+ s->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
+ }
+ s->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
+ s->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
+ "C[1][0], C[1][middlerow], C[1][3], "
+ " 0, 0, 1));");
+ s->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
+ "L[0], L[middlerow], L[3], "
+ "M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
+
+ // Orient the KLM matrix so we fill the correct side of the curve.
+ 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());
+
+ s->declareGlobal(fKLMDerivatives);
+ s->codeAppendf("%s[0] = %s[0].xy * %s.xz;",
+ fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
+ s->codeAppendf("%s[1] = %s[1].xy * %s.xz;",
+ fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
+ s->codeAppendf("%s[2] = %s[2].xy * %s.xz;",
+ fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
+
+ // 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, segmentId, rtAdjust, vars);
+}
+
+GrCCPRCubicShader::WindHandling
+GrCCPRCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
+ const char* position, const char* /*coverage*/,
+ const char* /*wind*/) {
+ varyingHandler->addVarying("klmd", &fKLMD, kHigh_GrSLPrecision);
+ 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);", fKLMD.gsOut());
+
+ this->onEmitVaryings(varyingHandler, code);
+ return WindHandling::kNotHandled;
+}
+
+void GrCCPRCubicHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* /*pts*/,
+ const char* /*wedgeId*/, const char* /*rtAdjust*/,
+ GeometryVars* vars) const {
+ // "midpoint" was just defined by the base class.
+ vars->fHullVars.fAlternateMidpoint = "midpoint";
+}
+
+void GrCCPRCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code) {
+ // "klm" was just defined by the base class.
+ varyingHandler->addVarying("grad_matrix", &fGradMatrix, kHigh_GrSLPrecision);
+ code->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str());
+ code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
+ fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str());
+}
+
+void GrCCPRCubicHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* 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());
+ f->codeAppend ("float f = k*k*k - l*m;");
+ f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.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 closing edge.
+}
+
+void GrCCPRCubicCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
+ const char* cornerId, const char* rtAdjust,
+ GeometryVars* vars) const {
+ s->declareGlobal(fEdgeDistanceDerivatives);
+ s->codeAppendf("%s = %s.xy * %s.xz;",
+ fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
+
+ s->codeAppendf("float2 corner = %s[%s * 3];", pts, cornerId);
+ vars->fCornerVars.fPoint = "corner";
+}
+
+void GrCCPRCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code) {
+ varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx, kHigh_GrSLPrecision);
+ code->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
+ fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
+ fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
+
+ varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy, kHigh_GrSLPrecision);
+ code->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
+ fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
+ fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
+
+ // Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z).
+ GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
+}
+
+void GrCCPRCubicCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* 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.
+ f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
+ fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
+ 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/GrCCPRCubicShader.h b/src/gpu/ccpr/GrCCPRCubicShader.h
new file mode 100644
index 0000000000..f9655be9c3
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRCubicShader.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCCPRCubicShader_DEFINED
+#define GrCCPRCubicShader_DEFINED
+
+#include "ccpr/GrCCPRCoverageProcessor.h"
+
+/**
+ * This class renders the coverage of convex closed cubic segments using the techniques outlined in
+ * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
+ * Jim Blinn:
+ *
+ * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
+ *
+ * The provided curve segments must be convex, monotonic with respect to the vector of their closing
+ * edge [P3 - P0], and must not contain or be near any inflection points or loop intersections.
+ * (Use GrCCPRGeometry.)
+ */
+class GrCCPRCubicShader : public GrCCPRCoverageProcessor::Shader {
+public:
+ enum class CubicType {
+ kSerpentine,
+ kLoop
+ };
+
+protected:
+ GrCCPRCubicShader(CubicType cubicType) : fCubicType(cubicType) {}
+
+ int getNumInputPoints() const final { return 4; }
+
+ void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const final;
+
+ void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
+ const char* outputWind) const final;
+
+ void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
+ const char* bloat, const char* wind, const char* rtAdjust,
+ GeometryVars*) const final;
+
+ virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
+ const char* rtAdjust, GeometryVars*) const = 0;
+
+ WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
+ const char* coverage, const char* wind) final;
+
+ virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
+
+ const CubicType fCubicType;
+ GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
+ GrShaderVar fKLMDerivatives{"klm_derivatives", kFloat2_GrSLType, 3};
+ GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
+ GrGLSLGeoToFrag fKLMD{kFloat4_GrSLType};
+};
+
+class GrCCPRCubicHullShader : public GrCCPRCubicShader {
+public:
+ GrCCPRCubicHullShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
+
+private:
+ GeometryType getGeometryType() const override { return GeometryType::kHull; }
+ int getNumSegments() const override { return 4; } // 4 wedges.
+ void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
+ const char* rtAdjust, GeometryVars*) const override;
+ void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
+
+ GrGLSLGeoToFrag fGradMatrix{kFloat2x2_GrSLType};
+};
+
+class GrCCPRCubicCornerShader : public GrCCPRCubicShader {
+public:
+ GrCCPRCubicCornerShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
+
+private:
+ GeometryType getGeometryType() const override { return GeometryType::kCorners; }
+ int getNumSegments() const override { return 2; } // 2 corners.
+ void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
+ const char* rtAdjust, GeometryVars*) const override;
+ void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
+
+ GrShaderVar fEdgeDistanceDerivatives{"edge_distance_derivatives", kFloat2_GrSLType};
+ GrGLSLGeoToFrag fdKLMDdx{kFloat4_GrSLType};
+ GrGLSLGeoToFrag fdKLMDdy{kFloat4_GrSLType};
+};
+
+#endif
diff --git a/src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp b/src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp
deleted file mode 100644
index 2a56b22227..0000000000
--- a/src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrCCPRQuadraticProcessor.h"
-
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
-
-void GrCCPRQuadraticProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
- GrGLSLVertexBuilder* v,
- const TexelBufferHandle& pointsBuffer,
- const char* atlasOffset, const char* rtAdjust,
- GrGPArgs* gpArgs) const {
- v->codeAppend ("float2 self = ");
- v->appendTexelFetch(pointsBuffer,
- SkStringPrintf("%s.x + sk_VertexID", proc.instanceAttrib()).c_str());
- v->codeAppendf(".xy + %s;", atlasOffset);
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
-}
-
-void GrCCPRQuadraticProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust,
- const char* outputWind) const {
- // We will define bezierpts in onEmitGeometryShader.
- g->codeAppend ("float area_times_2 = determinant(float2x2(bezierpts[1] - bezierpts[0], "
- "bezierpts[2] - bezierpts[0]));");
- // Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
- g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[2] - bezierpts[0]) * %s.zx)) {",
- rtAdjust);
-#ifndef SK_BUILD_FOR_MAC
- g->codeAppend ( "return;");
-#else
- // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
- g->codeAppend ( "area_times_2 = 0;");
-#endif
- g->codeAppend ("}");
- g->codeAppendf("%s = sign(area_times_2);", outputWind);
-}
-
-void GrCCPRQuadraticProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
- const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const {
- // Prepend bezierpts at the start of the shader.
- g->codePrependf("float3x2 bezierpts = float3x2(sk_in[0].sk_Position.xy, "
- "sk_in[1].sk_Position.xy, "
- "sk_in[2].sk_Position.xy);");
-
- g->declareGlobal(fCanonicalMatrix);
- g->codeAppendf("%s = float3x3(0.0, 0, 1, "
- "0.5, 0, 1, "
- "1.0, 1, 1) * "
- "inverse(float3x3(bezierpts[0], 1, "
- "bezierpts[1], 1, "
- "bezierpts[2], 1));",
- fCanonicalMatrix.c_str());
-
- g->declareGlobal(fCanonicalDerivatives);
- g->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
- fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
-
- g->declareGlobal(fEdgeDistanceEquation);
- g->codeAppendf("float2 edgept0 = bezierpts[%s > 0 ? 2 : 0];", wind);
- g->codeAppendf("float2 edgept1 = bezierpts[%s > 0 ? 0 : 2];", wind);
- this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
-
- this->emitQuadraticGeometry(g, emitVertexFn, rtAdjust);
-}
-
-void GrCCPRQuadraticProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position,
- const char* /*coverage*/,
- const char* /*wind*/) const {
- fnBody->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
- fXYD.gsOut(), fCanonicalMatrix.c_str(), position);
- fnBody->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
- fXYD.gsOut(), fEdgeDistanceEquation.c_str(), position,
- fEdgeDistanceEquation.c_str());
- this->onEmitPerVertexGeometryCode(fnBody);
-}
-
-void GrCCPRQuadraticHullProcessor::emitQuadraticGeometry(GrGLSLGeometryBuilder* g,
- const char* emitVertexFn,
- const char* /*rtAdjust*/) const {
- // Find the t value whose tangent is halfway between the tangents at the endpionts.
- // (We defined bezierpts in onEmitGeometryShader.)
- g->codeAppend ("float2 tan0 = bezierpts[1] - bezierpts[0];");
- g->codeAppend ("float2 tan1 = bezierpts[2] - bezierpts[1];");
- g->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
- g->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
- g->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s=0 is weeded out by this point.
-
- // Clip the bezier triangle by the tangent at our new t value. This is a simple application for
- // De Casteljau's algorithm.
- g->codeAppendf("float4x2 quadratic_hull = float4x2(bezierpts[0], "
- "bezierpts[0] + tan0 * t, "
- "bezierpts[1] + tan1 * t, "
- "bezierpts[2]);");
-
- int maxVerts = this->emitHullGeometry(g, emitVertexFn, "quadratic_hull", 4, "sk_InvocationID");
-
- g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
- GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
- maxVerts, 4);
-}
-
-void GrCCPRQuadraticHullProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
- fnBody->appendf("%s = float2(2 * %s.x, -1) * %s;",
- fGradXY.gsOut(), fXYD.gsOut(), fCanonicalDerivatives.c_str());
-}
-
-void GrCCPRQuadraticHullProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
- const char* outputCoverage) const {
- f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
- fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn(), fGradXY.fsIn(), fGradXY.fsIn());
- f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
- f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYD.fsIn()); // Flat closing edge.
-}
-
-void GrCCPRQuadraticCornerProcessor::emitQuadraticGeometry(GrGLSLGeometryBuilder* g,
- const char* emitVertexFn,
- const char* rtAdjust) const {
- g->declareGlobal(fEdgeDistanceDerivatives);
- g->codeAppendf("%s = %s.xy * %s.xz;",
- fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
-
- g->codeAppendf("float2 corner = bezierpts[sk_InvocationID * 2];");
- int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
-
- g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
- GrGLSLGeometryBuilder::OutputType::kTriangleStrip, numVertices, 2);
-}
-
-void GrCCPRQuadraticCornerProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
- fnBody->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
- fdXYDdx.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
- fEdgeDistanceDerivatives.c_str());
- fnBody->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
- fdXYDdy.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
- fEdgeDistanceDerivatives.c_str());
-}
-
-void GrCCPRQuadraticCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
- const char* outputCoverage) const {
- f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
- fXYD.fsIn(), fXYD.fsIn(), fXYD.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 = this->defineSoftSampleLocations(f, "samples");
- f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);",
- fXYD.fsIn(), fXYD.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/GrCCPRQuadraticProcessor.h b/src/gpu/ccpr/GrCCPRQuadraticProcessor.h
deleted file mode 100644
index 8975923c95..0000000000
--- a/src/gpu/ccpr/GrCCPRQuadraticProcessor.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCPRQuadraticProcessor_DEFINED
-#define GrCCPRQuadraticProcessor_DEFINED
-
-#include "ccpr/GrCCPRCoverageProcessor.h"
-
-/**
- * This class renders the coverage of closed quadratic curves using the techniques outlined in
- * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
- * Jim Blinn:
- *
- * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
- *
- * The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
- * (Use GrCCPRGeometry.)
- */
-class GrCCPRQuadraticProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
-public:
- GrCCPRQuadraticProcessor()
- : INHERITED(CoverageType::kShader)
- , fCanonicalMatrix("canonical_matrix", kFloat3x3_GrSLType, GrShaderVar::kNonArray)
- , fCanonicalDerivatives("canonical_derivatives", kFloat2x2_GrSLType,
- GrShaderVar::kNonArray)
- , fEdgeDistanceEquation("edge_distance_equation", kFloat3_GrSLType,
- GrShaderVar::kNonArray)
- , fXYD(kFloat3_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- varyingHandler->addVarying("xyd", &fXYD, kHigh_GrSLPrecision);
- }
-
- void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
- const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
- const char* rtAdjust, GrGPArgs*) const override;
- void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
- void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const final;
- void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
- const char* wind) const final;
-
-protected:
- virtual void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* rtAdjust) const = 0;
- virtual void onEmitPerVertexGeometryCode(SkString* fnBody) const = 0;
-
- GrShaderVar fCanonicalMatrix;
- GrShaderVar fCanonicalDerivatives;
- GrShaderVar fEdgeDistanceEquation;
- GrGLSLGeoToFrag fXYD;
-
- typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
-};
-
-/**
- * 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 GrCCPRQuadraticHullProcessor : public GrCCPRQuadraticProcessor {
-public:
- GrCCPRQuadraticHullProcessor()
- : fGradXY(kFloat2_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- this->INHERITED::resetVaryings(varyingHandler);
- varyingHandler->addVarying("grad_xy", &fGradXY, kHigh_GrSLPrecision);
- }
-
- void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* rtAdjust) const override;
- void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
- void emitShaderCoverage(GrGLSLFragmentBuilder* f, const char* outputCoverage) const override;
-
-private:
- GrGLSLGeoToFrag fGradXY;
-
- typedef GrCCPRQuadraticProcessor INHERITED;
-};
-
-/**
- * This pass fixes the corners of a closed quadratic segment with soft MSAA.
- */
-class GrCCPRQuadraticCornerProcessor : public GrCCPRQuadraticProcessor {
-public:
- GrCCPRQuadraticCornerProcessor()
- : fEdgeDistanceDerivatives("edge_distance_derivatives", kFloat2_GrSLType,
- GrShaderVar::kNonArray)
- , fdXYDdx(kFloat3_GrSLType)
- , fdXYDdy(kFloat3_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- this->INHERITED::resetVaryings(varyingHandler);
- varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx, kHigh_GrSLPrecision);
- varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy, kHigh_GrSLPrecision);
- }
-
- void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
- const char* rtAdjust) const override;
- void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
- void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
-
-private:
- GrShaderVar fEdgeDistanceDerivatives;
- GrGLSLGeoToFrag fdXYDdx;
- GrGLSLGeoToFrag fdXYDdy;
-
- typedef GrCCPRQuadraticProcessor INHERITED;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPRQuadraticShader.cpp b/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
new file mode 100644
index 0000000000..9dc0c726da
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCCPRQuadraticShader.h"
+
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+void GrCCPRQuadraticShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
+ GrGLSLShaderBuilder* s,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const {
+ s->appendTexelFetch(pointsBuffer,
+ SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
+}
+
+void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
+ const char* outputWind) const {
+ s->codeAppendf("float area_times_2 = determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0]));",
+ pts, pts, pts, pts);
+ // Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
+ s->codeAppendf("if (2 * abs(area_times_2) < length((%s[2] - %s[0]) * %s.zx)) {",
+ pts, pts, rtAdjust);
+#ifndef SK_BUILD_FOR_MAC
+ s->codeAppend ( "return;");
+#else
+ // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
+ s->codeAppend ( "area_times_2 = 0;");
+#endif
+ s->codeAppend ("}");
+ s->codeAppendf("%s = sign(area_times_2);", outputWind);
+}
+
+void GrCCPRQuadraticShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
+ const char* segmentId, const char* bloat,
+ const char* wind, const char* rtAdjust,
+ GeometryVars* vars) const {
+ s->declareGlobal(fCanonicalMatrix);
+ s->codeAppendf("%s = float3x3(0.0, 0, 1, "
+ "0.5, 0, 1, "
+ "1.0, 1, 1) * "
+ "inverse(float3x3(%s[0], 1, "
+ "%s[1], 1, "
+ "%s[2], 1));",
+ fCanonicalMatrix.c_str(), pts, pts, pts);
+
+ s->declareGlobal(fCanonicalDerivatives);
+ s->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
+ fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
+
+ 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, segmentId, rtAdjust, vars);
+}
+
+GrCCPRQuadraticShader::WindHandling
+GrCCPRQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
+ const char* position, const char* /*coverage*/,
+ const char* /*wind*/) {
+ varyingHandler->addVarying("xyd", &fXYD, kHigh_GrSLPrecision);
+ code->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
+ fXYD.gsOut(), fCanonicalMatrix.c_str(), position);
+ code->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
+ fXYD.gsOut(), fEdgeDistanceEquation.c_str(), position,
+ fEdgeDistanceEquation.c_str());
+
+ this->onEmitVaryings(varyingHandler, code);
+ return WindHandling::kNotHandled;
+}
+
+void GrCCPRQuadraticHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
+ const char* /*wedgeId*/, const char* /*rtAdjust*/,
+ 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);
+ s->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
+ s->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
+ s->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s != 0; we cull flat curves on CPU.
+
+ // Clip the bezier triangle by the tangent at our new t value. This is a simple application for
+ // De Casteljau's algorithm.
+ s->codeAppendf("float4x2 quadratic_hull = float4x2(%s[0], "
+ "%s[0] + tan0 * t, "
+ "%s[1] + tan1 * t, "
+ "%s[2]);", pts, pts, pts, pts);
+ vars->fHullVars.fAlternatePoints = "quadratic_hull";
+}
+
+void GrCCPRQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
+ SkString* code) {
+ varyingHandler->addVarying("grad", &fGrad, kHigh_GrSLPrecision);
+ code->appendf("%s = float2(2 * %s.x, -1) * %s;",
+ fGrad.gsOut(), fXYD.gsOut(), fCanonicalDerivatives.c_str());
+}
+
+void GrCCPRQuadraticHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
+ const char* outputCoverage) const {
+ f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
+ fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn(), fGrad.fsIn(), fGrad.fsIn());
+ f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
+ f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYD.fsIn()); // Flat closing edge.
+}
+
+void GrCCPRQuadraticCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
+ const char* cornerId, const char* rtAdjust,
+ GeometryVars* vars) const {
+ s->declareGlobal(fEdgeDistanceDerivatives);
+ s->codeAppendf("%s = %s.xy * %s.xz;",
+ fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
+
+ s->codeAppendf("float2 corner = %s[%s * 2];", pts, cornerId);
+ vars->fCornerVars.fPoint = "corner";
+}
+
+void GrCCPRQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
+ SkString* code) {
+ varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx, kHigh_GrSLPrecision);
+ code->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
+ fdXYDdx.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
+ fEdgeDistanceDerivatives.c_str());
+
+ varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy, kHigh_GrSLPrecision);
+ code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
+ fdXYDdy.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
+ fEdgeDistanceDerivatives.c_str());
+}
+
+void GrCCPRQuadraticCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
+ const char* outputCoverage) const {
+ f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
+ fXYD.fsIn(), fXYD.fsIn(), fXYD.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);", fXYD.fsIn(), fXYD.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/GrCCPRQuadraticShader.h b/src/gpu/ccpr/GrCCPRQuadraticShader.h
new file mode 100644
index 0000000000..7441266fde
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRQuadraticShader.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCCPRQuadraticShader_DEFINED
+#define GrCCPRQuadraticShader_DEFINED
+
+#include "ccpr/GrCCPRCoverageProcessor.h"
+
+/**
+ * This class renders the coverage of closed quadratic curves using the techniques outlined in
+ * "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
+ * Jim Blinn:
+ *
+ * https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
+ *
+ * The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
+ * (Use GrCCPRGeometry.)
+ */
+class GrCCPRQuadraticShader : public GrCCPRCoverageProcessor::Shader {
+protected:
+ int getNumInputPoints() const final { return 3; }
+
+ void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const override;
+
+ void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
+ const char* outputWind) const final;
+
+ void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
+ const char* bloat, const char* wind, const char* rtAdjust,
+ GeometryVars*) const final;
+
+ virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
+ const char* rtAdjust, GeometryVars*) const = 0;
+
+ WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
+ const char* coverage, const char* wind) final;
+
+ virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
+
+ const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType};
+ const GrShaderVar fCanonicalDerivatives{"canonical_derivatives", kFloat2x2_GrSLType};
+ const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
+ GrGLSLGeoToFrag fXYD{kFloat3_GrSLType};
+};
+
+/**
+ * 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 GrCCPRQuadraticHullShader : public GrCCPRQuadraticShader {
+ int getNumSegments() const final { return 4; } // 4 wedges.
+
+ GeometryType getGeometryType() const override { return GeometryType::kHull; }
+ void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
+ const char* rtAdjust, GeometryVars*) const override;
+ void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
+
+ GrGLSLGeoToFrag fGrad{kFloat2_GrSLType};
+};
+
+/**
+ * This pass fixes the corners of a closed quadratic segment with soft MSAA.
+ */
+class GrCCPRQuadraticCornerShader : public GrCCPRQuadraticShader {
+ int getNumSegments() const final { return 2; } // 2 corners.
+
+ GeometryType getGeometryType() const override { return GeometryType::kCorners; }
+ void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
+ const char* rtAdjust, GeometryVars*) const override;
+ void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
+
+ const GrShaderVar fEdgeDistanceDerivatives{"edge_distance_derivatives", kFloat2_GrSLType};
+ GrGLSLGeoToFrag fdXYDdx{kFloat3_GrSLType};
+ GrGLSLGeoToFrag fdXYDdy{kFloat3_GrSLType};
+};
+
+#endif
diff --git a/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp b/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
deleted file mode 100644
index 83b5c7b608..0000000000
--- a/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrCCPRTriangleProcessor.h"
-
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
-
-void GrCCPRTriangleProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
- GrGLSLVertexBuilder* v,
- const TexelBufferHandle& pointsBuffer,
- const char* atlasOffset, const char* rtAdjust,
- GrGPArgs* gpArgs) const {
- // Copy the input attrib to an intermediate array. The Intel GLSL compiler hits an internal
- // assertion if we index the input attrib itself with sk_VertexID.
- v->codeAppendf("int indices[3] = int[3](%s.x, %s.y, %s.z);",
- proc.instanceAttrib(), proc.instanceAttrib(), proc.instanceAttrib());
- v->codeAppend ("float2 self = ");
- v->appendTexelFetch(pointsBuffer, "indices[sk_VertexID]");
- v->codeAppendf(".xy + %s;", atlasOffset);
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
-}
-
-void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const {
- // Prepend in_vertices at the start of the shader.
- g->codePrependf("float3x2 in_vertices = float3x2(sk_in[0].sk_Position.xy, "
- "sk_in[1].sk_Position.xy, "
- "sk_in[2].sk_Position.xy);");
-}
-
-void GrCCPRTriangleProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* /*rtAdjust*/,
- const char* outputWind) const {
- // We will define in_vertices in defineInputVertices.
- g->codeAppendf("%s = sign(determinant(float2x2(in_vertices[1] - in_vertices[0], "
- "in_vertices[2] - in_vertices[0])));",
- outputWind);
-}
-
-void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
- const char* emitVertexFn,
- const char* wind,
- const char* rtAdjust) const {
- this->defineInputVertices(g);
- int maxOutputVertices = 0;
-
- if (GeometryType::kEdges != fGeometryType) {
- maxOutputVertices += this->emitHullGeometry(g, emitVertexFn, "in_vertices", 3,
- "sk_InvocationID");
- }
-
- if (GeometryType::kHulls != fGeometryType) {
- g->codeAppend ("int edgeidx0 = sk_InvocationID, "
- "edgeidx1 = (edgeidx0 + 1) % 3;");
- g->codeAppendf("float2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind);
- g->codeAppendf("float2 edgept1 = in_vertices[%s > 0 ? edgeidx1 : edgeidx0];", wind);
-
- maxOutputVertices += this->emitEdgeGeometry(g, emitVertexFn, "edgept0", "edgept1");
- }
-
- g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
- GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
- maxOutputVertices, 3);
-}
-
-void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
- const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const {
- this->defineInputVertices(g);
-
- g->codeAppend ("float2 corner = in_vertices[sk_InvocationID];");
- g->codeAppend ("float2x2 vectors = float2x2(corner - in_vertices[(sk_InvocationID + 2) % 3], "
- "corner - in_vertices[(sk_InvocationID + 1) % 3]);");
-
- // Make sure neither vector is 0 in order to avoid a divide-by-zero. Wind will be zero anyway if
- // this is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
- g->codeAppendf("for (int i = 0; i < 2; ++i) {");
- g->codeAppendf( "vectors[i] = any(notEqual(vectors[i], float2(0))) ? "
- "vectors[i] : float2(1);");
- g->codeAppendf("}");
-
- // Find the vector that bisects the region outside the incoming edges. Each edge is responsible
- // to subtract the outside region on its own the side of the bisector.
- g->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
- g->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
- g->codeAppendf("float2 bisect = dot(leftdir, rightdir) >= 0 ? leftdir + rightdir : "
- "float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
-
- // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge method
- // actually finds is coverage inside a logical "AA box", one that is rotated inline with the
- // edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set up
- // transformations into normalized logical AA box space for both incoming edges. These will tell
- // the fragment shader where the corner is located within each edge's AA box.
- g->declareGlobal(fAABoxMatrices);
- g->declareGlobal(fAABoxTranslates);
- g->declareGlobal(fGeoShaderBisects);
- g->codeAppendf("for (int i = 0; i < 2; ++i) {");
- // The X component runs parallel to the edge (i.e. distance to the corner).
- g->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
- g->codeAppendf( "float nwidth = dot(abs(n), bloat) * 2;");
- g->codeAppendf( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
- g->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
- g->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
-
- // The Y component runs perpendicular to the edge (i.e. distance-to-edge).
- // NOTE: once we are back in device space and bloat.x == bloat.y, we will not need to find and
- // divide by nwidth a second time.
- g->codeAppendf( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
- g->codeAppendf( "nwidth = dot(abs(n), bloat) * 2;");
- g->codeAppendf( "n /= nwidth;");
- g->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
- g->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
-
- // Translate the bisector into logical AA box space.
- // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
- // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
- g->codeAppendf( "%s[i] = -bisect * %s[i];",
- fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
- g->codeAppendf("}");
-
- int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
-
- g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
- GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
- numVertices, 3);
-}
-
-void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody,
- const char* position,
- const char* /*coverage*/,
- const char* wind) const {
- fnBody->appendf("for (int i = 0; i < 2; ++i) {");
- fnBody->appendf( "%s[i] = %s * %s[i] + %s[i];",
- fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
- fAABoxTranslates.c_str());
- fnBody->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
- fnBody->appendf("}");
-}
-
-void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
- const char* outputCoverage) const {
- // By the time we reach this shader, the pixel is in the following state:
- //
- // 1. The hull shader has emitted a coverage of 1.
- // 2. Both edges have subtracted the area on their outside.
- //
- // This generally works, but it is a problem for corner pixels. There is a region within corner
- // pixels that is outside both edges at the same time. This means the region has been double
- // subtracted (once by each edge). The purpose of this shader is to fix these corner pixels.
- //
- // More specifically, each edge redoes its coverage analysis so that it only subtracts the
- // outside area that falls on its own side of the bisector line.
- //
- // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have different
- // AA boxes. (For an explanation of AA boxes, see comments in onEmitGeometryShader.) This means
- // the coverage analysis will only be approximate. It seems acceptable, but if we want exact
- // coverage we will need to switch to a more expensive model.
- f->codeAppendf("%s = 0;", outputCoverage);
-
- // Loop through both edges.
- f->codeAppendf("for (int i = 0; i < 2; ++i) {");
- f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
- f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
-
- // Find the point at which the bisector exits the logical AA box.
- // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
- f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
- f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
- ": d.x / bisect.x;");
- f->codeAppendf( "half2 exit = corner + bisect * T;");
-
- // These lines combined (and the final multiply by .5) accomplish the following:
- // 1. Add back the area beyond the corner that was subtracted out previously.
- // 2. Subtract out the area beyond the corner, but under the bisector.
- // The other edge will take care of the area on its own side of the bisector.
- f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
- f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
- f->codeAppendf("}");
-
- f->codeAppendf("%s *= .5;", outputCoverage);
-}
diff --git a/src/gpu/ccpr/GrCCPRTriangleProcessor.h b/src/gpu/ccpr/GrCCPRTriangleProcessor.h
deleted file mode 100644
index ca143d1edb..0000000000
--- a/src/gpu/ccpr/GrCCPRTriangleProcessor.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCPRTriangleProcessor_DEFINED
-#define GrCCPRTriangleProcessor_DEFINED
-
-#include "ccpr/GrCCPRCoverageProcessor.h"
-
-/**
- * This class renders the coverage of triangles.
- *
- * Triangles are rendered in three passes:
- *
- * Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
- * is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
- * convex hull of those boxes.)
- *
- * Pass 2: Smooth the edges that were over-rendered during Pass 1. Draw the conservative raster of
- * each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
- * coverage=-1 on the outside edge to coverage=0 on the inside edge.
- *
- * Pass 3: Touch up the corner pixels to have the correct coverage.
- */
-class GrCCPRTriangleProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
-public:
- GrCCPRTriangleProcessor(CoverageType initialCoverage) : INHERITED(initialCoverage) {}
-
- void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
- const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
- const char* rtAdjust, GrGPArgs*) const override;
- void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
-
-protected:
- void defineInputVertices(GrGLSLGeometryBuilder*) const;
-
-private:
- typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
-};
-
-class GrCCPRTriangleHullAndEdgeProcessor : public GrCCPRTriangleProcessor {
-public:
- enum class GeometryType {
- kHulls,
- kEdges,
- kHullsAndEdges
- };
-
- GrCCPRTriangleHullAndEdgeProcessor(GeometryType geometryType)
- : INHERITED(GeometryType::kHulls == geometryType ?
- CoverageType::kOne : CoverageType::kInterpolated)
- , fGeometryType(geometryType) {}
-
- void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const override;
-
-private:
- const GeometryType fGeometryType;
-
- typedef GrCCPRTriangleProcessor INHERITED;
-};
-
-/**
- * This pass fixes the corner pixels of a triangle. It touches up the simple distance-to-edge
- * coverage analysis done previously so that it takes into account the region that is outside both
- * edges at the same time.
- */
-class GrCCPRTriangleCornerProcessor : public GrCCPRTriangleProcessor {
-public:
- GrCCPRTriangleCornerProcessor()
- : INHERITED(CoverageType::kShader)
- , fAABoxMatrices("aa_box_matrices", kFloat2x2_GrSLType, 2)
- , fAABoxTranslates("aa_box_translates", kFloat2_GrSLType, 2)
- , fGeoShaderBisects("bisects", kFloat2_GrSLType, 2)
- , fCornerLocationInAABoxes(kFloat2x2_GrSLType)
- , fBisectInAABoxes(kFloat2x2_GrSLType) {}
-
- void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
- this->INHERITED::resetVaryings(varyingHandler);
- varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
- varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
- }
-
- void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
- const char* rtAdjust) const override;
- void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
- const char* wind) const override;
- void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
-
-private:
- GrShaderVar fAABoxMatrices;
- GrShaderVar fAABoxTranslates;
- GrShaderVar fGeoShaderBisects;
- GrGLSLGeoToFrag fCornerLocationInAABoxes;
- GrGLSLGeoToFrag fBisectInAABoxes;
-
- typedef GrCCPRTriangleProcessor INHERITED;
-};
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPRTriangleShader.cpp b/src/gpu/ccpr/GrCCPRTriangleShader.cpp
new file mode 100644
index 0000000000..0bb85ac5bd
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRTriangleShader.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCCPRTriangleShader.h"
+
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLGeometryShaderBuilder.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
+
+void GrCCPRTriangleShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
+ GrGLSLShaderBuilder* s,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const {
+ s->appendTexelFetch(pointsBuffer,
+ SkStringPrintf("%s[%s]", proc.instanceAttrib(), pointId).c_str());
+}
+
+void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
+ const char* outputWind) const {
+ s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));",
+ outputWind, pts, pts, pts, pts);
+}
+
+GrCCPRTriangleHullShader::WindHandling
+GrCCPRTriangleHullShader::onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
+ const char* /*position*/, const char* /*coverage*/,
+ const char* /*wind*/) {
+ return WindHandling::kNotHandled; // No varyings.Let the base class handle wind.
+}
+
+void GrCCPRTriangleHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
+ const char* outputCoverage) const {
+ f->codeAppendf("%s = 1;", outputCoverage);
+}
+
+GrCCPRTriangleEdgeShader::WindHandling
+GrCCPRTriangleEdgeShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
+ const char* position, const char* coverage,
+ const char* wind) {
+ varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind, kLow_GrSLPrecision);
+ code->appendf("%s = %s * %s;", fCoverageTimesWind.gsOut(), coverage, wind);
+ return WindHandling::kHandled;
+}
+
+void GrCCPRTriangleEdgeShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
+ const char* outputCoverage) const {
+ f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
+}
+
+void GrCCPRTriangleCornerShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
+ const char* cornerId, const char* bloat,
+ const char* wind, const char* rtAdjust,
+ GeometryVars* vars) const {
+ s->codeAppendf("float2 corner = %s[sk_InvocationID];", pts);
+ vars->fCornerVars.fPoint = "corner";
+
+ s->codeAppendf("float2x2 vectors = float2x2(corner - %s[(sk_InvocationID + 2) %% 3], "
+ "corner - %s[(sk_InvocationID + 1) %% 3]);",
+ pts, pts);
+
+ // Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this
+ // is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
+ s->codeAppend ("for (int i = 0; i < 2; ++i) {");
+ s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);");
+ s->codeAppend ("}");
+
+ // Find the vector that bisects the region outside the incoming edges. Each edge is
+ // responsible to subtract the outside region on its own the side of the bisector.
+ s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
+ s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
+ s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? "
+ "leftdir + rightdir : "
+ "float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
+
+ // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge
+ // method actually finds is coverage inside a logical "AA box", one that is rotated inline
+ // with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set
+ // up transformations into normalized logical AA box space for both incoming edges. These
+ // will tell the fragment shader where the corner is located within each edge's AA box.
+ s->declareGlobal(fAABoxMatrices);
+ s->declareGlobal(fAABoxTranslates);
+ s->declareGlobal(fGeoShaderBisects);
+ s->codeAppendf("for (int i = 0; i < 2; ++i) {");
+ // The X component runs parallel to the edge (i.e. distance to the corner).
+ s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
+ s->codeAppend ( "float nwidth = dot(abs(n), bloat) * 2;");
+ s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
+ s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
+ s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
+
+ // The Y component runs perpendicular to the edge (i.e. distance-to-edge).
+ // NOTE: if we are back in device space and bloat.x == bloat.y, we will not need to find and
+ // divide by nwidth a second time.
+ s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
+ s->codeAppend ( "nwidth = dot(abs(n), bloat) * 2;");
+ s->codeAppend ( "n /= nwidth;");
+ s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
+ s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
+
+ // Translate the bisector into logical AA box space.
+ // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
+ // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
+ s->codeAppendf( "%s[i] = -bisect * %s[i];",
+ fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
+ s->codeAppend ("}");
+}
+
+GrCCPRTriangleCornerShader::WindHandling
+GrCCPRTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
+ const char* position, const char* /*coverage*/,
+ const char* /*wind*/) {
+ varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
+ varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
+ code->appendf("for (int i = 0; i < 2; ++i) {");
+ code->appendf( "%s[i] = %s * %s[i] + %s[i];",
+ fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
+ fAABoxTranslates.c_str());
+ code->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
+ code->appendf("}");
+
+ return WindHandling::kNotHandled;
+}
+
+void GrCCPRTriangleCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
+ const char* outputCoverage) const {
+ // By the time we reach this shader, the pixel is in the following state:
+ //
+ // 1. The hull shader has emitted a coverage of 1.
+ // 2. Both edges have subtracted the area on their outside.
+ //
+ // This generally works, but it is a problem for corner pixels. There is a region within
+ // corner pixels that is outside both edges at the same time. This means the region has been
+ // double subtracted (once by each edge). The purpose of this shader is to fix these corner
+ // pixels.
+ //
+ // More specifically, each edge redoes its coverage analysis so that it only subtracts the
+ // outside area that falls on its own side of the bisector line.
+ //
+ // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have
+ // different AA boxes. (For an explanation of AA boxes, see comments in
+ // onEmitGeometryShader.) This means the coverage analysis will only be approximate. It
+ // seems acceptable, but if we want exact coverage we will need to switch to a more
+ // expensive model.
+ f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges.
+ f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
+ f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
+
+ // Find the point at which the bisector exits the logical AA box.
+ // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
+ f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
+ f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
+ ": d.x / bisect.x;");
+ f->codeAppendf( "half2 exit = corner + bisect * T;");
+
+ // These lines combined (and the final multiply by .5) accomplish the following:
+ // 1. Add back the area beyond the corner that was subtracted out previously.
+ // 2. Subtract out the area beyond the corner, but under the bisector.
+ // The other edge will take care of the area on its own side of the bisector.
+ f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
+ f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
+ f->codeAppendf("}");
+
+ f->codeAppendf("%s *= .5;", outputCoverage);
+}
diff --git a/src/gpu/ccpr/GrCCPRTriangleShader.h b/src/gpu/ccpr/GrCCPRTriangleShader.h
new file mode 100644
index 0000000000..efc25219f2
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRTriangleShader.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCCPRTriangleShader_DEFINED
+#define GrCCPRTriangleShader_DEFINED
+
+#include "ccpr/GrCCPRCoverageProcessor.h"
+
+/**
+ * This class renders the coverage of triangles. Triangles are rendered in three passes, as
+ * described below.
+ */
+class GrCCPRTriangleShader : public GrCCPRCoverageProcessor::Shader {
+public:
+ int getNumInputPoints() const final { return 3; }
+ int getNumSegments() const final { return 3; } // 3 wedges, 3 edges, 3 corners.
+
+ void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
+ const TexelBufferHandle& pointsBuffer,
+ const char* pointId) const final;
+
+ void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
+ const char* outputWind) const final;
+};
+
+/**
+ * Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
+ * is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
+ * convex hull of those boxes.)
+ */
+class GrCCPRTriangleHullShader : public GrCCPRTriangleShader {
+ GeometryType getGeometryType() const override { return GeometryType::kHull; }
+
+ WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
+ const char* coverage, const char* wind) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, const char* outputCoverage) const override;
+};
+
+/**
+ * Pass 2: Smooth the edges that were over-rendered during Pass 1. Draw the conservative raster of
+ * each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
+ * coverage=-1 on the outside edge to coverage=0 on the inside edge.
+ */
+class GrCCPRTriangleEdgeShader : public GrCCPRTriangleShader {
+ GeometryType getGeometryType() const override { return GeometryType::kEdges; }
+
+ WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
+ const char* coverage, const char* wind) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
+
+ GrGLSLGeoToFrag fCoverageTimesWind{kHalf_GrSLType};
+};
+
+/**
+ * Pass 3: Touch up the corner pixels. Here we fix the simple distance-to-edge coverage analysis
+ * done previously so that it takes into account the region that is outside both edges at
+ * the same time.
+ */
+class GrCCPRTriangleCornerShader : public GrCCPRTriangleShader {
+ GeometryType getGeometryType() const override { return GeometryType::kCorners; }
+
+ void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
+ const char* bloat, const char* wind, const char* rtAdjust,
+ GeometryVars*) const override;
+ WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
+ const char* coverage, const char* wind) override;
+ void onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, const char* outputCoverage) const override;
+
+ GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2};
+ GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};
+ GrShaderVar fGeoShaderBisects{"bisects", kFloat2_GrSLType, 2};
+ GrGLSLGeoToFrag fCornerLocationInAABoxes{kFloat2x2_GrSLType};
+ GrGLSLGeoToFrag fBisectInAABoxes{kFloat2x2_GrSLType};
+};
+
+#endif