aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gn/gpu.gni13
-rw-r--r--samplecode/SampleCCPRGeometry.cpp52
-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
19 files changed, 1400 insertions, 1383 deletions
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 3cc064a05f..ef751dba4c 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -296,18 +296,17 @@ skia_gpu_sources = [
"$_src/gpu/ccpr/GrCCPRCoverageOp.cpp",
"$_src/gpu/ccpr/GrCCPRCoverageOp.h",
"$_src/gpu/ccpr/GrCCPRCoverageProcessor.cpp",
- "$_src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp",
"$_src/gpu/ccpr/GrCCPRCoverageProcessor.h",
- "$_src/gpu/ccpr/GrCCPRCubicShader.cpp",
- "$_src/gpu/ccpr/GrCCPRCubicShader.h",
+ "$_src/gpu/ccpr/GrCCPRCubicProcessor.cpp",
+ "$_src/gpu/ccpr/GrCCPRCubicProcessor.h",
"$_src/gpu/ccpr/GrCCPRGeometry.cpp",
"$_src/gpu/ccpr/GrCCPRGeometry.h",
"$_src/gpu/ccpr/GrCCPRPathProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRPathProcessor.h",
- "$_src/gpu/ccpr/GrCCPRQuadraticShader.cpp",
- "$_src/gpu/ccpr/GrCCPRQuadraticShader.h",
- "$_src/gpu/ccpr/GrCCPRTriangleShader.cpp",
- "$_src/gpu/ccpr/GrCCPRTriangleShader.h",
+ "$_src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp",
+ "$_src/gpu/ccpr/GrCCPRQuadraticProcessor.h",
+ "$_src/gpu/ccpr/GrCCPRTriangleProcessor.cpp",
+ "$_src/gpu/ccpr/GrCCPRTriangleProcessor.h",
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp",
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.h",
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index aa1fb8b8a4..e152e1ec62 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -27,16 +27,16 @@
using TriangleInstance = GrCCPRCoverageProcessor::TriangleInstance;
using CurveInstance = GrCCPRCoverageProcessor::CurveInstance;
-using RenderPass = GrCCPRCoverageProcessor::RenderPass;
+using Mode = GrCCPRCoverageProcessor::Mode;
static constexpr float kDebugBloat = 40;
-static int num_points(RenderPass renderPass) {
- return renderPass >= RenderPass::kSerpentineHulls ? 4 : 3;
+static int num_points(Mode mode) {
+ return mode >= Mode::kSerpentineHulls ? 4 : 3;
}
-static int is_quadratic(RenderPass renderPass) {
- return renderPass >= RenderPass::kQuadraticHulls && renderPass < RenderPass::kSerpentineHulls;
+static int is_quadratic(Mode mode) {
+ return mode >= Mode::kQuadraticHulls && mode < Mode::kSerpentineHulls;
}
/**
@@ -65,7 +65,7 @@ private:
void updateGpuData();
- RenderPass fRenderPass = RenderPass::kTriangleHulls;
+ Mode fMode = Mode::kTriangleHulls;
SkMatrix fCubicKLM;
SkPoint fPoints[4] = {
@@ -136,9 +136,9 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
SkPath outline;
outline.moveTo(fPoints[0]);
- if (4 == num_points(fRenderPass)) {
+ if (4 == num_points(fMode)) {
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
- } else if (is_quadratic(fRenderPass)) {
+ } else if (is_quadratic(fMode)) {
outline.quadTo(fPoints[1], fPoints[3]);
} else {
outline.lineTo(fPoints[1]);
@@ -172,7 +172,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
if (GrRenderTargetContext* rtc =
canvas->internal_private_accessTopLayerRenderTargetContext()) {
rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
- caption = GrCCPRCoverageProcessor::GetRenderPassName(fRenderPass);
+ caption = GrCCPRCoverageProcessor::GetProcessorName(fMode);
}
SkPaint pointsPaint;
@@ -180,7 +180,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
pointsPaint.setStrokeWidth(8);
pointsPaint.setAntiAlias(true);
- if (4 == num_points(fRenderPass)) {
+ if (4 == num_points(fMode)) {
int w = this->width(), h = this->height();
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
@@ -199,7 +199,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
}
void CCPRGeometryView::updateGpuData() {
- int vertexCount = num_points(fRenderPass);
+ int vertexCount = num_points(fMode);
fGpuPoints.reset();
fInstanceData.reset();
@@ -208,17 +208,17 @@ void CCPRGeometryView::updateGpuData() {
if (4 == vertexCount) {
double t[2], s[2];
SkCubicType type = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
- if (RenderPass::kSerpentineHulls == fRenderPass && SkCubicType::kLoop == type) {
- fRenderPass = RenderPass::kLoopHulls;
+ if (Mode::kSerpentineHulls == fMode && SkCubicType::kLoop == type) {
+ fMode = Mode::kLoopHulls;
}
- if (RenderPass::kSerpentineCorners == fRenderPass && SkCubicType::kLoop == type) {
- fRenderPass = RenderPass::kLoopCorners;
+ if (Mode::kSerpentineCorners == fMode && SkCubicType::kLoop == type) {
+ fMode = Mode::kLoopCorners;
}
- if (RenderPass::kLoopHulls == fRenderPass && SkCubicType::kLoop != type) {
- fRenderPass = RenderPass::kSerpentineHulls;
+ if (Mode::kLoopHulls == fMode && SkCubicType::kLoop != type) {
+ fMode = Mode::kSerpentineHulls;
}
- if (RenderPass::kLoopCorners == fRenderPass && SkCubicType::kLoop != type) {
- fRenderPass = RenderPass::kSerpentineCorners;
+ if (Mode::kLoopCorners == fMode && SkCubicType::kLoop != type) {
+ fMode = Mode::kSerpentineCorners;
}
GrCCPRGeometry geometry;
@@ -245,7 +245,7 @@ void CCPRGeometryView::updateGpuData() {
default: continue;
}
}
- } else if (is_quadratic(fRenderPass)) {
+ } else if (is_quadratic(fMode)) {
GrCCPRGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.quadraticTo(fPoints[1], fPoints[3]);
@@ -282,7 +282,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
GrContext* context = state->gpu()->getContext();
GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend() ?
static_cast<GrGLGpu*>(state->gpu()) : nullptr;
- int vertexCount = num_points(fView->fRenderPass);
+ int vertexCount = num_points(fView->fMode);
sk_sp<GrBuffer> pointsBuffer(rp->createBuffer(fView->fGpuPoints.count() * sizeof(SkPoint),
kTexel_GrBufferType, kDynamic_GrAccessPattern,
@@ -305,7 +305,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
SkBlendMode::kSrcOver);
- GrCCPRCoverageProcessor ccprProc(fView->fRenderPass, pointsBuffer.get());
+ GrCCPRCoverageProcessor ccprProc(fView->fMode, pointsBuffer.get());
SkDEBUGCODE(ccprProc.enableDebugVisualizations(kDebugBloat);)
GrMesh mesh(4 == vertexCount ? GrPrimitiveType::kLinesAdjacency : GrPrimitiveType::kTriangles);
@@ -349,7 +349,7 @@ private:
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
for (int i = 0; i < 4; ++i) {
- if (4 != num_points(fRenderPass) && 2 == i) {
+ if (4 != num_points(fMode) && 2 == i) {
continue;
}
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
@@ -374,11 +374,11 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
SkUnichar unichar;
if (SampleCode::CharQ(*evt, &unichar)) {
if (unichar >= '1' && unichar <= '7') {
- fRenderPass = RenderPass(unichar - '1');
- if (fRenderPass >= RenderPass::kLoopHulls) {
+ fMode = Mode(unichar - '1');
+ if (fMode >= Mode::kLoopHulls) {
// '6' -> kSerpentineHulls, '7' -> kSerpentineCorners. updateGpuData converts to
// kLoop* if needed.
- fRenderPass = RenderPass(int(fRenderPass) + 1);
+ fMode = Mode(int(fMode) + 1);
}
this->updateAndInval();
return true;
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
index 34e0410f9e..f37fcbef05 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 RenderPass = GrCCPRCoverageProcessor::RenderPass;
+ using Mode = GrCCPRCoverageProcessor::Mode;
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, RenderPass::kTriangleHulls,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleHulls,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleEdges,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleEdges,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleCorners,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleCorners,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
// Quadratics.
auto constexpr kQuadraticsGrPrimitiveType = GrCCPRCoverageProcessor::kQuadraticsGrPrimitiveType;
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticHulls,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticHulls,
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticCorners,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticCorners,
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
// Cubics.
auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineHulls,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineHulls,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopHulls,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopHulls,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineCorners,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineCorners,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
- this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopCorners,
+ this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopCorners,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
}
void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
- GrCCPRCoverageProcessor::RenderPass renderPass,
+ GrCCPRCoverageProcessor::Mode mode,
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(renderPass, fPointsBuffer.get());
+ GrCCPRCoverageProcessor proc(mode, 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 80b4cf9744..7d457fef33 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::RenderPass, GrPrimitiveType,
- int vertexCount, int PrimitiveTallies::* instanceType) const;
+ void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, const GrCCPRCoverageProcessor::Mode,
+ 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 6fa298722a..d27ea4c077 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
@@ -7,91 +7,323 @@
#include "GrCCPRCoverageProcessor.h"
-#include "SkMakeUnique.h"
-#include "ccpr/GrCCPRCubicShader.h"
-#include "ccpr/GrCCPRQuadraticShader.h"
-#include "ccpr/GrCCPRTriangleShader.h"
+#include "GrRenderTargetProxy.h"
+#include "ccpr/GrCCPRTriangleProcessor.h"
+#include "ccpr/GrCCPRQuadraticProcessor.h"
+#include "ccpr/GrCCPRCubicProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLGeometryShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
-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;
+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)";
}
- SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
- return kInt4_GrVertexAttribType;
+ SK_ABORT("Unexpected ccpr coverage processor mode.");
+ return nullptr;
}
-GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(RenderPass renderPass, GrBuffer* pointsBuffer)
+GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(Mode mode, GrBuffer* pointsBuffer)
: INHERITED(kGrCCPRCoverageProcessor_ClassID)
- , fRenderPass(renderPass)
- , fInstanceAttrib(this->addInstanceAttrib("instance", instance_array_format(fRenderPass))) {
+ , fMode(mode)
+ , fInstanceAttrib(this->addInstanceAttrib("instance", InstanceArrayFormat(mode))) {
fPointsBufferAccess.reset(kRG_float_GrPixelConfig, pointsBuffer, kVertex_GrShaderFlag);
this->addBufferAccess(&fPointsBufferAccess);
this->setWillUseGeoShader();
}
-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);
+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);
}
+ SK_ABORT("Unexpected ccpr coverage processor mode.");
+ return nullptr;
}
-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);
+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;
}
- f->codeAppendf("%s = half4(1);", skOutputCoverage);
+ 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);
+
#ifdef SK_DEBUG
if (proc.debugVisualizationsEnabled()) {
- f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);",
- skOutputColor, skOutputColor, skOutputColor);
+ g->codeAppendf("bloat *= %f;", proc.debugBloat());
}
#endif
+
+ return this->onEmitGeometryShader(g, emitVertexFn.c_str(), fGeomWind.c_str(), rtAdjust);
}
-void GrCCPRCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLShaderBuilder* s,
- const char* leftPt,
- const char* rightPt,
- const char* outputDistanceEquation) {
+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 {
// Which quadrant does the vector from left -> right fall into?
- s->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
- s->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
+ g->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
+ g->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
- s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
+ g->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
rightPt, leftPt, leftPt, rightPt);
- s->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
+ g->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.
- s->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
+ g->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
- s->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
+ g->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
}
-int GrCCPRCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentBuilder* f,
- const char* samplesName) {
+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 {
// Standard DX11 sample locations.
#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
f->defineConstant("float2[8]", samplesName, "float2[8]("
@@ -110,74 +342,9 @@ int GrCCPRCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentB
#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 "GrRenderTargetProxy.h"
+#include "GrRenderTarget.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 f2c5f435e0..f02b7422c8 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -12,22 +12,21 @@
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLVarying.h"
-class GrGLSLPPFragmentBuilder;
-class GrGLSLShaderBuilder;
+class GrGLSLFragmentBuilder;
/**
* 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 shapes and negative for counter-clockwise, that indicates coverage.
+ * positive for clockwise primitives and negative for counter-clockwise, that indicates coverage.
*
- * 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).
+ * 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).
*
- * 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.
+ * 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.
*
* Draw calls are instanced, with one vertex per bezier point (3 for triangles). They use the
* corresponding GrPrimitiveType as defined below.
@@ -57,12 +56,7 @@ public:
GR_STATIC_ASSERT(2 * 4 == sizeof(CurveInstance));
- /**
- * 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 {
+ enum class Mode {
// Triangles.
kTriangleHulls,
kTriangleEdges,
@@ -78,132 +72,22 @@ public:
kSerpentineCorners,
kLoopCorners
};
+ static constexpr GrVertexAttribType InstanceArrayFormat(Mode mode) {
+ return mode < Mode::kQuadraticHulls ? kInt4_GrVertexAttribType : kInt2_GrVertexAttribType;
+ }
+ static const char* GetProcessorName(Mode);
- 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);
+ GrCCPRCoverageProcessor(Mode, GrBuffer* pointsBuffer);
const char* instanceAttrib() const { return fInstanceAttrib.fName; }
- const char* name() const override { return GetRenderPassName(fRenderPass); }
+ int atlasOffsetIdx() const {
+ return kInt4_GrVertexAttribType == InstanceArrayFormat(fMode) ? 3 : 1;
+ }
+ const char* name() const override { return GetProcessorName(fMode); }
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;
@@ -217,27 +101,152 @@ public:
static void Validate(GrRenderTargetProxy* atlasProxy);
#endif
- class GSImpl;
+ class PrimitiveProcessor;
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;
- static GrGLSLPrimitiveProcessor* CreateGSImpl(std::unique_ptr<Shader>);
+ // 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.
+ };
- int atlasOffsetIdx() const {
- SkASSERT(kInt2_GrVertexAttribType == fInstanceAttrib.fType ||
- kInt4_GrVertexAttribType == fInstanceAttrib.fType);
- return kInt4_GrVertexAttribType == fInstanceAttrib.fType ? 3 : 1;
+ 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.");
}
- const RenderPass fRenderPass;
- const Attribute& fInstanceAttrib;
- BufferAccess fPointsBufferAccess;
- SkDEBUGCODE(float fDebugBloat = 0;)
+ // 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;
- typedef GrGeometryProcessor INHERITED;
+private:
+ void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+ FPCoordTransformIter&& transformIter) final {
+ this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+ }
+
+ 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;
+
+ typedef GrGLSLGeometryProcessor INHERITED;
};
#endif
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
deleted file mode 100644
index b9a38e732a..0000000000
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
+++ /dev/null
@@ -1,278 +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 "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
new file mode 100644
index 0000000000..8b4c405961
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRCubicProcessor.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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
new file mode 100644
index 0000000000..e44d8fb4b9
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRCubicProcessor.h
@@ -0,0 +1,120 @@
+/*
+ * 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
deleted file mode 100644
index 3a30cee8df..0000000000
--- a/src/gpu/ccpr/GrCCPRCubicShader.cpp
+++ /dev/null
@@ -1,203 +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 "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
deleted file mode 100644
index f9655be9c3..0000000000
--- a/src/gpu/ccpr/GrCCPRCubicShader.h
+++ /dev/null
@@ -1,94 +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 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
new file mode 100644
index 0000000000..2a56b22227
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRQuadraticProcessor.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 "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
new file mode 100644
index 0000000000..8975923c95
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRQuadraticProcessor.h
@@ -0,0 +1,117 @@
+/*
+ * 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
deleted file mode 100644
index 9dc0c726da..0000000000
--- a/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
+++ /dev/null
@@ -1,157 +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 "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
deleted file mode 100644
index 7441266fde..0000000000
--- a/src/gpu/ccpr/GrCCPRQuadraticShader.h
+++ /dev/null
@@ -1,87 +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 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
new file mode 100644
index 0000000000..83b5c7b608
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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
new file mode 100644
index 0000000000..ca143d1edb
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRTriangleProcessor.h
@@ -0,0 +1,103 @@
+/*
+ * 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
deleted file mode 100644
index 0bb85ac5bd..0000000000
--- a/src/gpu/ccpr/GrCCPRTriangleShader.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 "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
deleted file mode 100644
index efc25219f2..0000000000
--- a/src/gpu/ccpr/GrCCPRTriangleShader.h
+++ /dev/null
@@ -1,80 +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 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