Refactor CCPR coverage shaders for a vertex impl

Decouples geometry generation and analytic coverage. This paves the
way for a vertex shader implementation.

TBR=egdaniel@google.com

Bug: skia:
Change-Id: I2a183401bfe70b9f14b9b1cf035de6020a2135fa
Reviewed-on: https://skia-review.googlesource.com/60103
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2017-10-16 10:44:41 -06:00 committed by Skia Commit-Bot
parent 6376517472
commit 6a3dbeed00
19 changed files with 1383 additions and 1400 deletions

View File

@ -296,17 +296,18 @@ 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/GrCCPRCubicProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRCubicProcessor.h",
"$_src/gpu/ccpr/GrCCPRCubicShader.cpp",
"$_src/gpu/ccpr/GrCCPRCubicShader.h",
"$_src/gpu/ccpr/GrCCPRGeometry.cpp",
"$_src/gpu/ccpr/GrCCPRGeometry.h",
"$_src/gpu/ccpr/GrCCPRPathProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRPathProcessor.h",
"$_src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRQuadraticProcessor.h",
"$_src/gpu/ccpr/GrCCPRTriangleProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRTriangleProcessor.h",
"$_src/gpu/ccpr/GrCCPRQuadraticShader.cpp",
"$_src/gpu/ccpr/GrCCPRQuadraticShader.h",
"$_src/gpu/ccpr/GrCCPRTriangleShader.cpp",
"$_src/gpu/ccpr/GrCCPRTriangleShader.h",
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp",
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.h",

View File

@ -27,16 +27,16 @@
using TriangleInstance = GrCCPRCoverageProcessor::TriangleInstance;
using CurveInstance = GrCCPRCoverageProcessor::CurveInstance;
using Mode = GrCCPRCoverageProcessor::Mode;
using RenderPass = GrCCPRCoverageProcessor::RenderPass;
static constexpr float kDebugBloat = 40;
static int num_points(Mode mode) {
return mode >= Mode::kSerpentineHulls ? 4 : 3;
static int num_points(RenderPass renderPass) {
return renderPass >= RenderPass::kSerpentineHulls ? 4 : 3;
}
static int is_quadratic(Mode mode) {
return mode >= Mode::kQuadraticHulls && mode < Mode::kSerpentineHulls;
static int is_quadratic(RenderPass renderPass) {
return renderPass >= RenderPass::kQuadraticHulls && renderPass < RenderPass::kSerpentineHulls;
}
/**
@ -65,7 +65,7 @@ private:
void updateGpuData();
Mode fMode = Mode::kTriangleHulls;
RenderPass fRenderPass = RenderPass::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(fMode)) {
if (4 == num_points(fRenderPass)) {
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
} else if (is_quadratic(fMode)) {
} else if (is_quadratic(fRenderPass)) {
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::GetProcessorName(fMode);
caption = GrCCPRCoverageProcessor::GetRenderPassName(fRenderPass);
}
SkPaint pointsPaint;
@ -180,7 +180,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
pointsPaint.setStrokeWidth(8);
pointsPaint.setAntiAlias(true);
if (4 == num_points(fMode)) {
if (4 == num_points(fRenderPass)) {
int w = this->width(), h = this->height();
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
@ -199,7 +199,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
}
void CCPRGeometryView::updateGpuData() {
int vertexCount = num_points(fMode);
int vertexCount = num_points(fRenderPass);
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 (Mode::kSerpentineHulls == fMode && SkCubicType::kLoop == type) {
fMode = Mode::kLoopHulls;
if (RenderPass::kSerpentineHulls == fRenderPass && SkCubicType::kLoop == type) {
fRenderPass = RenderPass::kLoopHulls;
}
if (Mode::kSerpentineCorners == fMode && SkCubicType::kLoop == type) {
fMode = Mode::kLoopCorners;
if (RenderPass::kSerpentineCorners == fRenderPass && SkCubicType::kLoop == type) {
fRenderPass = RenderPass::kLoopCorners;
}
if (Mode::kLoopHulls == fMode && SkCubicType::kLoop != type) {
fMode = Mode::kSerpentineHulls;
if (RenderPass::kLoopHulls == fRenderPass && SkCubicType::kLoop != type) {
fRenderPass = RenderPass::kSerpentineHulls;
}
if (Mode::kLoopCorners == fMode && SkCubicType::kLoop != type) {
fMode = Mode::kSerpentineCorners;
if (RenderPass::kLoopCorners == fRenderPass && SkCubicType::kLoop != type) {
fRenderPass = RenderPass::kSerpentineCorners;
}
GrCCPRGeometry geometry;
@ -245,7 +245,7 @@ void CCPRGeometryView::updateGpuData() {
default: continue;
}
}
} else if (is_quadratic(fMode)) {
} else if (is_quadratic(fRenderPass)) {
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->fMode);
int vertexCount = num_points(fView->fRenderPass);
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->fMode, pointsBuffer.get());
GrCCPRCoverageProcessor ccprProc(fView->fRenderPass, 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(fMode) && 2 == i) {
if (4 != num_points(fRenderPass) && 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') {
fMode = Mode(unichar - '1');
if (fMode >= Mode::kLoopHulls) {
fRenderPass = RenderPass(unichar - '1');
if (fRenderPass >= RenderPass::kLoopHulls) {
// '6' -> kSerpentineHulls, '7' -> kSerpentineCorners. updateGpuData converts to
// kLoop* if needed.
fMode = Mode(int(fMode) + 1);
fRenderPass = RenderPass(int(fRenderPass) + 1);
}
this->updateAndInval();
return true;

View File

@ -385,7 +385,7 @@ void GrCCPRCoverageOp::setBuffers(sk_sp<GrBuffer> pointsBuffer, sk_sp<GrBuffer>
}
void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
using Mode = GrCCPRCoverageProcessor::Mode;
using RenderPass = GrCCPRCoverageProcessor::RenderPass;
SkDEBUGCODE(GrCCPRCoverageProcessor::Validate(flushState->drawOpArgs().fProxy));
SkASSERT(fPointsBuffer);
@ -399,34 +399,34 @@ void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
// Triangles.
auto constexpr kTrianglesGrPrimitiveType = GrCCPRCoverageProcessor::kTrianglesGrPrimitiveType;
this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleHulls,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleHulls,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleEdges,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleEdges,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleCorners,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleCorners,
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
// Quadratics.
auto constexpr kQuadraticsGrPrimitiveType = GrCCPRCoverageProcessor::kQuadraticsGrPrimitiveType;
this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticHulls,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticHulls,
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticCorners,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticCorners,
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
// Cubics.
auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineHulls,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineHulls,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopHulls,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopHulls,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineCorners,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineCorners,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopCorners,
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopCorners,
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
}
void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
GrCCPRCoverageProcessor::Mode mode,
GrCCPRCoverageProcessor::RenderPass renderPass,
GrPrimitiveType primType, int vertexCount,
int PrimitiveTallies::* instanceType) const {
using ScissorMode = GrCCPRCoverageOpsBuilder::ScissorMode;
@ -463,7 +463,7 @@ void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPi
SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
if (!fMeshesScratchBuffer.empty()) {
GrCCPRCoverageProcessor proc(mode, fPointsBuffer.get());
GrCCPRCoverageProcessor proc(renderPass, fPointsBuffer.get());
SkASSERT(flushState->rtCommandBuffer());
flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
fDynamicStatesScratchBuffer.begin(),

View File

@ -149,9 +149,9 @@ private:
const PrimitiveTallies baseInstances[kNumScissorModes],
const PrimitiveTallies endInstances[kNumScissorModes]);
void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, const GrCCPRCoverageProcessor::Mode,
GrPrimitiveType, int vertexCount,
int PrimitiveTallies::* instanceType) const;
void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&,
const GrCCPRCoverageProcessor::RenderPass, GrPrimitiveType,
int vertexCount, int PrimitiveTallies::* instanceType) const;
sk_sp<GrBuffer> fPointsBuffer;
sk_sp<GrBuffer> fInstanceBuffer;

View File

@ -7,323 +7,91 @@
#include "GrCCPRCoverageProcessor.h"
#include "GrRenderTargetProxy.h"
#include "ccpr/GrCCPRTriangleProcessor.h"
#include "ccpr/GrCCPRQuadraticProcessor.h"
#include "ccpr/GrCCPRCubicProcessor.h"
#include "SkMakeUnique.h"
#include "ccpr/GrCCPRCubicShader.h"
#include "ccpr/GrCCPRQuadraticShader.h"
#include "ccpr/GrCCPRTriangleShader.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLProgramBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
const char* GrCCPRCoverageProcessor::GetProcessorName(Mode mode) {
switch (mode) {
case Mode::kTriangleHulls:
return "GrCCPRTriangleHullAndEdgeProcessor (hulls)";
case Mode::kTriangleEdges:
return "GrCCPRTriangleHullAndEdgeProcessor (edges)";
case Mode::kTriangleCorners:
return "GrCCPRTriangleCornerProcessor";
case Mode::kQuadraticHulls:
return "GrCCPRQuadraticHullProcessor";
case Mode::kQuadraticCorners:
return "GrCCPRQuadraticCornerProcessor";
case Mode::kSerpentineHulls:
return "GrCCPRCubicHullProcessor (serpentine)";
case Mode::kLoopHulls:
return "GrCCPRCubicHullProcessor (loop)";
case Mode::kSerpentineCorners:
return "GrCCPRCubicCornerProcessor (serpentine)";
case Mode::kLoopCorners:
return "GrCCPRCubicCornerProcessor (loop)";
static GrVertexAttribType instance_array_format(GrCCPRCoverageProcessor::RenderPass renderPass) {
switch (renderPass) {
case GrCCPRCoverageProcessor::RenderPass::kTriangleHulls:
case GrCCPRCoverageProcessor::RenderPass::kTriangleEdges:
case GrCCPRCoverageProcessor::RenderPass::kTriangleCorners:
return kInt4_GrVertexAttribType;
case GrCCPRCoverageProcessor::RenderPass::kQuadraticHulls:
case GrCCPRCoverageProcessor::RenderPass::kQuadraticCorners:
case GrCCPRCoverageProcessor::RenderPass::kSerpentineHulls:
case GrCCPRCoverageProcessor::RenderPass::kLoopHulls:
case GrCCPRCoverageProcessor::RenderPass::kSerpentineCorners:
case GrCCPRCoverageProcessor::RenderPass::kLoopCorners:
return kInt2_GrVertexAttribType;
}
SK_ABORT("Unexpected ccpr coverage processor mode.");
return nullptr;
SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
return kInt4_GrVertexAttribType;
}
GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(Mode mode, GrBuffer* pointsBuffer)
GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(RenderPass renderPass, GrBuffer* pointsBuffer)
: INHERITED(kGrCCPRCoverageProcessor_ClassID)
, fMode(mode)
, fInstanceAttrib(this->addInstanceAttrib("instance", InstanceArrayFormat(mode))) {
, fRenderPass(renderPass)
, fInstanceAttrib(this->addInstanceAttrib("instance", instance_array_format(fRenderPass))) {
fPointsBufferAccess.reset(kRG_float_GrPixelConfig, pointsBuffer, kVertex_GrShaderFlag);
this->addBufferAccess(&fPointsBufferAccess);
this->setWillUseGeoShader();
}
void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
GrProcessorKeyBuilder* b) const {
b->add32(int(fMode));
}
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
switch (fMode) {
using GeometryType = GrCCPRTriangleHullAndEdgeProcessor::GeometryType;
case Mode::kTriangleHulls:
return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kHulls);
case Mode::kTriangleEdges:
return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kEdges);
case Mode::kTriangleCorners:
return new GrCCPRTriangleCornerProcessor();
case Mode::kQuadraticHulls:
return new GrCCPRQuadraticHullProcessor();
case Mode::kQuadraticCorners:
return new GrCCPRQuadraticCornerProcessor();
case Mode::kSerpentineHulls:
return new GrCCPRCubicHullProcessor(GrCCPRCubicProcessor::CubicType::kSerpentine);
case Mode::kLoopHulls:
return new GrCCPRCubicHullProcessor(GrCCPRCubicProcessor::CubicType::kLoop);
case Mode::kSerpentineCorners:
return new GrCCPRCubicCornerProcessor(GrCCPRCubicProcessor::CubicType::kSerpentine);
case Mode::kLoopCorners:
return new GrCCPRCubicCornerProcessor(GrCCPRCubicProcessor::CubicType::kLoop);
void GrCCPRCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler,
SkString* code, const char* position,
const char* coverage, const char* wind) {
WindHandling windHandling = this->onEmitVaryings(varyingHandler, code, position, coverage,
wind);
if (WindHandling::kNotHandled == windHandling) {
varyingHandler->addFlatVarying("wind", &fWind, kLow_GrSLPrecision);
code->appendf("%s = %s;", fWind.gsOut(), wind);
}
SK_ABORT("Unexpected ccpr coverage processor mode.");
return nullptr;
}
using PrimitiveProcessor = GrCCPRCoverageProcessor::PrimitiveProcessor;
void PrimitiveProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
switch (fCoverageType) {
case CoverageType::kOne:
case CoverageType::kShader:
varyingHandler->addFlatVarying("wind", &fFragWind, kLow_GrSLPrecision);
break;
case CoverageType::kInterpolated:
varyingHandler->addVarying("coverage_times_wind", &fFragCoverageTimesWind,
kMedium_GrSLPrecision);
break;
void GrCCPRCoverageProcessor::Shader::emitFragmentCode(const GrCCPRCoverageProcessor& proc,
GrGLSLPPFragmentBuilder* f,
const char* skOutputColor,
const char* skOutputCoverage) const {
f->codeAppendf("half coverage = 0;");
this->onEmitFragmentCode(f, "coverage");
if (fWind.fsIn()) {
f->codeAppendf("%s.a = coverage * %s;", skOutputColor, fWind.fsIn());
} else {
f->codeAppendf("%s.a = coverage;", skOutputColor);
}
this->resetVaryings(varyingHandler);
varyingHandler->emitAttributes(proc);
this->emitVertexShader(proc, args.fVertBuilder, args.fTexelBuffers[0], args.fRTAdjustName,
gpArgs);
this->emitGeometryShader(proc, args.fGeomBuilder, args.fRTAdjustName);
this->emitCoverage(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
}
void PrimitiveProcessor::emitVertexShader(const GrCCPRCoverageProcessor& proc,
GrGLSLVertexBuilder* v,
const TexelBufferHandle& pointsBuffer,
const char* rtAdjust, GrGPArgs* gpArgs) const {
v->codeAppendf("int packedoffset = %s[%i];", proc.instanceAttrib(), proc.atlasOffsetIdx());
v->codeAppend ("float2 atlasoffset = float2((packedoffset<<16) >> 16, "
"packedoffset >> 16);");
this->onEmitVertexShader(proc, v, pointsBuffer, "atlasoffset", rtAdjust, gpArgs);
}
void PrimitiveProcessor::emitGeometryShader(const GrCCPRCoverageProcessor& proc,
GrGLSLGeometryBuilder* g, const char* rtAdjust) const {
g->declareGlobal(fGeomWind);
this->emitWind(g, rtAdjust, fGeomWind.c_str());
SkString emitVertexFn;
SkSTArray<2, GrShaderVar> emitArgs;
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType,
GrShaderVar::kNonArray).c_str();
const char* coverage = emitArgs.emplace_back("coverage", kFloat_GrSLType,
GrShaderVar::kNonArray).c_str();
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
SkString fnBody;
this->emitPerVertexGeometryCode(&fnBody, position, coverage, fGeomWind.c_str());
if (fFragWind.gsOut()) {
fnBody.appendf("%s = %s;", fFragWind.gsOut(), fGeomWind.c_str());
}
if (fFragCoverageTimesWind.gsOut()) {
fnBody.appendf("%s = %s * %s;",
fFragCoverageTimesWind.gsOut(), coverage, fGeomWind.c_str());
}
fnBody.append ("sk_Position = float4(position, 0, 1);");
fnBody.append ("EmitVertex();");
return fnBody;
}().c_str(), &emitVertexFn);
g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
f->codeAppendf("%s = half4(1);", skOutputCoverage);
#ifdef SK_DEBUG
if (proc.debugVisualizationsEnabled()) {
g->codeAppendf("bloat *= %f;", proc.debugBloat());
f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);",
skOutputColor, skOutputColor, skOutputColor);
}
#endif
return this->onEmitGeometryShader(g, emitVertexFn.c_str(), fGeomWind.c_str(), rtAdjust);
}
int PrimitiveProcessor::emitHullGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
const char* polygonPts, int numSides,
const char* wedgeIdx, const char* midpoint) const {
SkASSERT(numSides >= 3);
if (!midpoint) {
g->codeAppendf("float2 midpoint = %s * float%i(%f);",
polygonPts, numSides, 1.0 / numSides);
midpoint = "midpoint";
}
g->codeAppendf("int previdx = (%s + %i) %% %i, "
"nextidx = (%s + 1) %% %i;",
wedgeIdx, numSides - 1, numSides, wedgeIdx, numSides);
g->codeAppendf("float2 self = %s[%s];"
"int leftidx = %s > 0 ? previdx : nextidx;"
"int rightidx = %s > 0 ? nextidx : previdx;",
polygonPts, wedgeIdx, fGeomWind.c_str(), fGeomWind.c_str());
// Which quadrant does the vector from self -> right fall into?
g->codeAppendf("float2 right = %s[rightidx];", polygonPts);
if (3 == numSides) {
// TODO: evaluate perf gains.
g->codeAppend ("float2 qsr = sign(right - self);");
} else {
SkASSERT(4 == numSides);
g->codeAppendf("float2 diag = %s[(%s + 2) %% 4];", polygonPts, wedgeIdx);
g->codeAppend ("float2 qsr = sign((right != self ? right : diag) - self);");
}
// Which quadrant does the vector from left -> self fall into?
g->codeAppendf("float2 qls = sign(self - %s[leftidx]);", polygonPts);
// d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
// TODO: evaluate perf gains.
const char* dr2 = "dr";
if (3 == numSides) {
// TODO: evaluate perf gains.
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
"qsr.x != 0 ? -qsr.x : +qsr.y);");
g->codeAppend ("float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
"qsr.x != 0 ? -qsr.x : -qsr.y);");
g->codeAppend ("float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
"qls.x != 0 ? -qls.x : +qls.y);");
dr2 = "dr2";
} else {
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
"qsr.x != 0 ? -qsr.x : 1);");
g->codeAppend ("float2 dl = (qls == float2(0)) ? dr : "
"float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
}
g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
// Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
// Each invocation emits a different third.
g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
g->codeAppendf("%s(%s, 1);", emitVertexFn, midpoint);
g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
g->codeAppend ("if (any(dnotequal)) {");
g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
g->codeAppend ("}");
g->codeAppend ("if (all(dnotequal)) {");
g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
g->codeAppend ("}");
g->codeAppend ("EndPrimitive();");
return 5;
}
int PrimitiveProcessor::emitEdgeGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
const char* leftPt, const char* rightPt,
const char* distanceEquation) const {
if (!distanceEquation) {
this->emitEdgeDistanceEquation(g, leftPt, rightPt, "float3 edge_distance_equation");
distanceEquation = "edge_distance_equation";
}
// qlr is defined in emitEdgeDistanceEquation.
g->codeAppendf("float2x2 endpts = float2x2(%s - bloat * qlr, %s + bloat * qlr);",
leftPt, rightPt);
g->codeAppendf("half2 endpts_coverage = %s.xy * endpts + %s.z;",
distanceEquation, distanceEquation);
// d1 is defined in emitEdgeDistanceEquation.
g->codeAppend ("float2 d2 = d1;");
g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
g->codeAppend ("if (aligned) {");
g->codeAppend ( "d1 -= qlr;");
g->codeAppend ( "d2 += qlr;");
g->codeAppend ("}");
// Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
// invocation emits a different edge. Emit negative coverage that subtracts the appropiate
// amount back out from the hull we drew above.
g->codeAppend ("if (!aligned) {");
g->codeAppendf( "%s(endpts[0], endpts_coverage[0]);", emitVertexFn);
g->codeAppend ("}");
g->codeAppendf("%s(%s + bloat * d1, -1);", emitVertexFn, leftPt);
g->codeAppendf("%s(%s - bloat * d2, 0);", emitVertexFn, leftPt);
g->codeAppendf("%s(%s + bloat * d2, -1);", emitVertexFn, rightPt);
g->codeAppendf("%s(%s - bloat * d1, 0);", emitVertexFn, rightPt);
g->codeAppend ("if (!aligned) {");
g->codeAppendf( "%s(endpts[1], endpts_coverage[1]);", emitVertexFn);
g->codeAppend ("}");
g->codeAppend ("EndPrimitive();");
return 6;
}
void PrimitiveProcessor::emitEdgeDistanceEquation(GrGLSLGeometryBuilder* g,
const char* leftPt, const char* rightPt,
const char* outputDistanceEquation) const {
void GrCCPRCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLShaderBuilder* s,
const char* leftPt,
const char* rightPt,
const char* outputDistanceEquation) {
// Which quadrant does the vector from left -> right fall into?
g->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
g->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
s->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
s->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
g->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
rightPt, leftPt, leftPt, rightPt);
g->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
s->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
leftPt, leftPt);
// Clamp for when n=0. wind=0 when n=0 so as long as we don't get Inf or NaN we are fine.
g->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
s->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
g->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
s->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
}
int PrimitiveProcessor::emitCornerGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
const char* pt) const {
g->codeAppendf("%s(%s + float2(-bloat.x, -bloat.y), 1);", emitVertexFn, pt);
g->codeAppendf("%s(%s + float2(-bloat.x, +bloat.y), 1);", emitVertexFn, pt);
g->codeAppendf("%s(%s + float2(+bloat.x, -bloat.y), 1);", emitVertexFn, pt);
g->codeAppendf("%s(%s + float2(+bloat.x, +bloat.y), 1);", emitVertexFn, pt);
g->codeAppend ("EndPrimitive();");
return 4;
}
void PrimitiveProcessor::emitCoverage(const GrCCPRCoverageProcessor& proc, GrGLSLFragmentBuilder* f,
const char* outputColor, const char* outputCoverage) const {
switch (fCoverageType) {
case CoverageType::kOne:
f->codeAppendf("%s.a = %s;", outputColor, fFragWind.fsIn());
break;
case CoverageType::kInterpolated:
f->codeAppendf("%s.a = %s;", outputColor, fFragCoverageTimesWind.fsIn());
break;
case CoverageType::kShader:
f->codeAppendf("half coverage = 0;");
this->emitShaderCoverage(f, "coverage");
f->codeAppendf("%s.a = coverage * %s;", outputColor, fFragWind.fsIn());
break;
}
f->codeAppendf("%s = half4(1);", outputCoverage);
#ifdef SK_DEBUG
if (proc.debugVisualizationsEnabled()) {
f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);", outputColor, outputColor, outputColor);
}
#endif
}
int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
const char* samplesName) const {
int GrCCPRCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentBuilder* f,
const char* samplesName) {
// Standard DX11 sample locations.
#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
f->defineConstant("float2[8]", samplesName, "float2[8]("
@ -342,9 +110,74 @@ int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
#endif
}
void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
GrProcessorKeyBuilder* b) const {
b->add32((int)fRenderPass);
}
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
std::unique_ptr<Shader> shader;
switch (fRenderPass) {
using CubicType = GrCCPRCubicShader::CubicType;
case RenderPass::kTriangleHulls:
shader = skstd::make_unique<GrCCPRTriangleHullShader>();
break;
case RenderPass::kTriangleEdges:
shader = skstd::make_unique<GrCCPRTriangleEdgeShader>();
break;
case RenderPass::kTriangleCorners:
shader = skstd::make_unique<GrCCPRTriangleCornerShader>();
break;
case RenderPass::kQuadraticHulls:
shader = skstd::make_unique<GrCCPRQuadraticHullShader>();
break;
case RenderPass::kQuadraticCorners:
shader = skstd::make_unique<GrCCPRQuadraticCornerShader>();
break;
case RenderPass::kSerpentineHulls:
shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kSerpentine);
break;
case RenderPass::kLoopHulls:
shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kLoop);
break;
case RenderPass::kSerpentineCorners:
shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kSerpentine);
break;
case RenderPass::kLoopCorners:
shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kLoop);
break;
}
return CreateGSImpl(std::move(shader));
}
const char* GrCCPRCoverageProcessor::GetRenderPassName(RenderPass renderPass) {
switch (renderPass) {
case RenderPass::kTriangleHulls:
return "RenderPass::kTriangleHulls";
case RenderPass::kTriangleEdges:
return "RenderPass::kTriangleEdges";
case RenderPass::kTriangleCorners:
return "RenderPass::kTriangleCorners";
case RenderPass::kQuadraticHulls:
return "RenderPass::kQuadraticHulls";
case RenderPass::kQuadraticCorners:
return "RenderPass::kQuadraticCorners";
case RenderPass::kSerpentineHulls:
return "RenderPass::kSerpentineHulls";
case RenderPass::kLoopHulls:
return "RenderPass::kLoopHulls";
case RenderPass::kSerpentineCorners:
return "RenderPass::kSerpentineCorners";
case RenderPass::kLoopCorners:
return "RenderPass::kLoopCorners";
}
SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
return nullptr;
}
#ifdef SK_DEBUG
#include "GrRenderTarget.h"
#include "GrRenderTargetProxy.h"
void GrCCPRCoverageProcessor::Validate(GrRenderTargetProxy* atlasProxy) {
SkASSERT(kAtlasOrigin == atlasProxy->origin());

View File

@ -12,21 +12,22 @@
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLVarying.h"
class GrGLSLFragmentBuilder;
class GrGLSLPPFragmentBuilder;
class GrGLSLShaderBuilder;
/**
* This is the geometry processor for the simple convex primitive shapes (triangles and closed curve
* segments) from which ccpr paths are composed. The output is a single-channel alpha value,
* positive for clockwise primitives and negative for counter-clockwise, that indicates coverage.
* positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
*
* The caller is responsible to render all modes for all applicable primitives into a cleared,
* floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's
* primitives have been drawn, the render target contains a composite coverage count that can then
* be used to draw the path (see GrCCPRPathProcessor).
* The caller is responsible to execute all render passes for all applicable primitives into a
* cleared, floating point, alpha-only render target using SkBlendMode::kPlus (see RenderPass
* below). Once all of a path's primitives have been drawn, the render target contains a composite
* coverage count that can then be used to draw the path (see GrCCPRPathProcessor).
*
* Caller provides the primitives' (x,y) points in an fp32x2 (RG) texel buffer, and an instance
* buffer with a single int32x4 attrib (for triangles) or int32x2 (for curves) defined below. There
* are no vertex attribs.
* Caller provides the primitives' (x,y) input points in an fp32x2 (RG) texel buffer, and an
* instance buffer with a single int32x4 attrib (for triangles) or int32x2 (for curves) defined
* below. There are no vertex attribs.
*
* Draw calls are instanced, with one vertex per bezier point (3 for triangles). They use the
* corresponding GrPrimitiveType as defined below.
@ -56,7 +57,12 @@ public:
GR_STATIC_ASSERT(2 * 4 == sizeof(CurveInstance));
enum class Mode {
/**
* All primitive shapes (triangles and convex closed curve segments) require more than one
* render pass. Here we enumerate every render pass needed in order to produce a complete
* coverage count mask. This is an exhaustive list of all ccpr coverage shaders.
*/
enum class RenderPass {
// Triangles.
kTriangleHulls,
kTriangleEdges,
@ -72,22 +78,132 @@ public:
kSerpentineCorners,
kLoopCorners
};
static constexpr GrVertexAttribType InstanceArrayFormat(Mode mode) {
return mode < Mode::kQuadraticHulls ? kInt4_GrVertexAttribType : kInt2_GrVertexAttribType;
}
static const char* GetProcessorName(Mode);
GrCCPRCoverageProcessor(Mode, GrBuffer* pointsBuffer);
static const char* GetRenderPassName(RenderPass);
/**
* This serves as the base class for each RenderPass's Shader. It indicates what type of
* geometry the Impl should generate and provides implementation-independent code to process
* the inputs and calculate coverage in the fragment Shader.
*/
class Shader {
public:
using TexelBufferHandle = GrGLSLGeometryProcessor::TexelBufferHandle;
// This enum specifies the type of geometry that should be generated for a Shader instance.
// Subclasses are limited to three built-in types of geometry to choose from:
enum class GeometryType {
// Generates a conservative raster hull around the input points. This is the geometry
// that causes a pixel to be rasterized if it is touched anywhere by the input polygon.
// Coverage is +1 all around.
//
// Logically, the conservative raster hull is equivalent to the convex hull of pixel
// size boxes centered around each input point.
kHull,
// Generates the conservative rasters of the input edges (i.e. convex hull of two
// pixel-size boxes centered on both endpoints). Coverage is -1 on the outside border of
// the edge geometry and 0 on the inside. This is the only geometry type that associates
// coverage values with the output points. It effectively converts a jagged conservative
// raster edge into a smooth antialiased edge.
kEdges,
// Generates the conservative rasters of the corners specified by the geometry provider
// (i.e. pixel-size box centered on the corner point). Coverage is +1 all around.
kCorners
};
virtual GeometryType getGeometryType() const = 0;
virtual int getNumInputPoints() const = 0;
// Returns the number of independent geometric segments to generate for the render pass
// (number of wedges for a hull, number of edges, or number of corners.)
virtual int getNumSegments() const = 0;
// Appends an expression that fetches input point # "pointId" from the texel buffer.
virtual void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const = 0;
// Determines the winding direction of the primitive. The subclass must write a value of
// either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
virtual void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
const char* outputWind) const = 0;
union GeometryVars {
struct {
const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
const char* fAlternateMidpoint; // float2 (if left null, finds euclidean midpoint).
} fHullVars;
struct {
const char* fPoint; // float2
} fCornerVars;
GeometryVars() { memset(this, 0, sizeof(*this)); }
};
// Called before generating geometry. Subclasses must fill out the applicable fields in
// GeometryVars (if any), and may also use this opportunity to setup internal member
// variables that will be needed during onEmitVaryings (e.g. transformation matrices).
virtual void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* bloat, const char* wind, const char* rtAdjust,
GeometryVars*) const {}
void emitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind);
void emitFragmentCode(const GrCCPRCoverageProcessor& proc, GrGLSLPPFragmentBuilder*,
const char* skOutputColor, const char* skOutputCoverage) const;
// Defines an equation ("dot(float3(pt, 1), distance_equation)") that is -1 on the outside
// border of a conservative raster edge and 0 on the inside (see emitEdgeGeometry).
static void EmitEdgeDistanceEquation(GrGLSLShaderBuilder*, const char* leftPt,
const char* rightPt,
const char* outputDistanceEquation);
// Defines a global float2 array that contains MSAA sample locations as offsets from pixel
// center. Subclasses can use this for software multisampling.
//
// Returns the number of samples.
static int DefineSoftSampleLocations(GrGLSLPPFragmentBuilder* f, const char* samplesName);
virtual ~Shader() {}
protected:
enum class WindHandling : bool {
kHandled,
kNotHandled
};
// Here the subclass adds its internal varyings to the handler and produces code to
// initialize those varyings from a given position, coverage value, and wind.
//
// Returns whether the subclass will handle wind modulation or if this base class should
// take charge of multiplying the final coverage output by "wind".
//
// NOTE: the coverage parameter is only relevant for edges (see comments in GeometryType).
// Otherwise it is +1 all around.
virtual WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
const char* position, const char* coverage,
const char* wind) = 0;
// Emits the fragment code that calculates a pixel's coverage value. If using
// WindHandling::kHandled, this value must be signed appropriately.
virtual void onEmitFragmentCode(GrGLSLPPFragmentBuilder*,
const char* outputCoverage) const = 0;
private:
GrGLSLGeoToFrag fWind{kHalf_GrSLType};
};
GrCCPRCoverageProcessor(RenderPass, GrBuffer* pointsBuffer);
const char* instanceAttrib() const { return fInstanceAttrib.fName; }
int atlasOffsetIdx() const {
return kInt4_GrVertexAttribType == InstanceArrayFormat(fMode) ? 3 : 1;
}
const char* name() const override { return GetProcessorName(fMode); }
const char* name() const override { return GetRenderPassName(fRenderPass); }
SkString dumpInfo() const override {
return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
}
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
@ -101,152 +217,27 @@ public:
static void Validate(GrRenderTargetProxy* atlasProxy);
#endif
class PrimitiveProcessor;
class GSImpl;
private:
const Mode fMode;
const Attribute& fInstanceAttrib;
BufferAccess fPointsBufferAccess;
SkDEBUGCODE(float fDebugBloat = false;)
typedef GrGeometryProcessor INHERITED;
};
/**
* This class represents the actual SKSL implementation for the various primitives and modes of
* GrCCPRCoverageProcessor.
*/
class GrCCPRCoverageProcessor::PrimitiveProcessor : public GrGLSLGeometryProcessor {
protected:
// Slightly undershoot a bloat radius of 0.5 so vertices that fall on integer boundaries don't
// accidentally bleed into neighbor pixels.
static constexpr float kAABloatRadius = 0.491111f;
// Specifies how the fragment shader should calculate sk_FragColor.a.
enum class CoverageType {
kOne, // Output +1 all around, modulated by wind.
kInterpolated, // Interpolate the coverage values that the geometry shader associates with
// each point, modulated by wind.
kShader // Call emitShaderCoverage and let the subclass decide, then a modulate by wind.
};
static GrGLSLPrimitiveProcessor* CreateGSImpl(std::unique_ptr<Shader>);
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.");
int atlasOffsetIdx() const {
SkASSERT(kInt2_GrVertexAttribType == fInstanceAttrib.fType ||
kInt4_GrVertexAttribType == fInstanceAttrib.fType);
return kInt4_GrVertexAttribType == fInstanceAttrib.fType ? 3 : 1;
}
// 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;
const RenderPass fRenderPass;
const Attribute& fInstanceAttrib;
BufferAccess fPointsBufferAccess;
SkDEBUGCODE(float fDebugBloat = 0;)
// 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;
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;
typedef GrGeometryProcessor INHERITED;
};
#endif

View File

@ -0,0 +1,278 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRCoverageProcessor.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
using Shader = GrCCPRCoverageProcessor::Shader;
/**
* This class and its subclasses implement the coverage processor with geometry shaders.
*/
class GrCCPRCoverageProcessor::GSImpl : public GrGLSLGeometryProcessor {
protected:
GSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
FPCoordTransformIter&& transformIter) final {
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
}
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
// Vertex shader.
GrGLSLVertexBuilder* v = args.fVertBuilder;
// The Intel GLSL compiler hits an internal assertion if we index the input attrib itself
// with sk_VertexID.
v->codeAppendf("int pointID = sk_VertexID;");
v->codeAppend ("float2 self = ");
fShader->appendInputPointFetch(proc, v, args.fTexelBuffers[0], "pointID");
v->codeAppend (".xy;");
v->codeAppendf("int packedoffset = %s[%i];",
proc.fInstanceAttrib.fName, proc.atlasOffsetIdx());
v->codeAppend ("float2 atlasoffset = float2((packedoffset << 16) >> 16, "
"packedoffset >> 16);");
v->codeAppend ("self += atlasoffset;");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
// Geometry shader.
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName);
varyingHandler->emitAttributes(proc);
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
// Fragment shader.
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
}
void emitGeometryShader(const GrCCPRCoverageProcessor& proc,
GrGLSLVaryingHandler* varyingHandler, GrGLSLGeometryBuilder* g,
const char* rtAdjust) const {
using InputType = GrGLSLGeometryBuilder::InputType;
using OutputType = GrGLSLGeometryBuilder::OutputType;
int numPts = fShader->getNumInputPoints();
SkASSERT(3 == numPts || 4 == numPts);
g->codeAppendf("float%ix2 pts = float%ix2(", numPts, numPts);
for (int i = 0; i < numPts; ++i) {
g->codeAppend (i ? ", " : "");
g->codeAppendf("sk_in[%i].sk_Position.xy", i);
}
g->codeAppend (");");
GrShaderVar wind("wind", kHalf_GrSLType);
g->declareGlobal(wind);
fShader->emitWind(g, "pts", rtAdjust, wind.c_str());
SkString emitVertexFn;
SkSTArray<2, GrShaderVar> emitArgs;
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
const char* coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
SkString fnBody;
fShader->emitVaryings(varyingHandler, &fnBody, position, coverage, wind.c_str());
fnBody.append("sk_Position = float4(position, 0, 1);");
fnBody.append("EmitVertex();");
return fnBody;
}().c_str(), &emitVertexFn);
g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
#ifdef SK_DEBUG
if (proc.debugVisualizationsEnabled()) {
g->codeAppendf("bloat *= %f;", proc.debugBloat());
}
#endif
Shader::GeometryVars vars;
fShader->emitSetupCode(g, "pts", "sk_InvocationID", "bloat", wind.c_str(), rtAdjust, &vars);
int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), rtAdjust, vars);
int numInputPoints = fShader->getNumInputPoints();
SkASSERT(3 == numInputPoints || 4 == numInputPoints);
InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
: InputType::kLinesAdjacency;
g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
}
virtual int onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
const char* emitVertexFn, const char* rtAdjust,
const Shader::GeometryVars&) const = 0;
virtual ~GSImpl() {}
const std::unique_ptr<Shader> fShader;
typedef GrGLSLGeometryProcessor INHERITED;
};
class GSHullImpl : public GrCCPRCoverageProcessor::GSImpl {
public:
GSHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
const char* emitVertexFn, const char* rtAdjust,
const Shader::GeometryVars& vars) const override {
int numSides = fShader->getNumSegments();
SkASSERT(numSides >= 3);
const char* hullPts = vars.fHullVars.fAlternatePoints;
if (!hullPts) {
SkASSERT(fShader->getNumInputPoints() == numSides);
hullPts = "pts";
}
const char* midpoint = vars.fHullVars.fAlternateMidpoint;
if (!midpoint) {
g->codeAppendf("float2 midpoint = %s * float%i(%f);", hullPts, numSides, 1.0/numSides);
midpoint = "midpoint";
}
g->codeAppendf("int previdx = (sk_InvocationID + %i) %% %i, "
"nextidx = (sk_InvocationID + 1) %% %i;",
numSides - 1, numSides, numSides);
g->codeAppendf("float2 self = %s[sk_InvocationID];"
"int leftidx = %s > 0 ? previdx : nextidx;"
"int rightidx = %s > 0 ? nextidx : previdx;",
hullPts, wind.c_str(), wind.c_str());
// Which quadrant does the vector from self -> right fall into?
g->codeAppendf("float2 right = %s[rightidx];", hullPts);
if (3 == numSides) {
// TODO: evaluate perf gains.
g->codeAppend ("float2 qsr = sign(right - self);");
} else {
SkASSERT(4 == numSides);
g->codeAppendf("float2 diag = %s[(sk_InvocationID + 2) %% 4];", hullPts);
g->codeAppend ("float2 qsr = sign((right != self ? right : diag) - self);");
}
// Which quadrant does the vector from left -> self fall into?
g->codeAppendf("float2 qls = sign(self - %s[leftidx]);", hullPts);
// d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
// TODO: evaluate perf gains.
const char* dr2 = "dr";
if (3 == numSides) {
// TODO: evaluate perf gains.
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
"qsr.x != 0 ? -qsr.x : +qsr.y);");
g->codeAppend ("float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
"qsr.x != 0 ? -qsr.x : -qsr.y);");
g->codeAppend ("float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
"qls.x != 0 ? -qls.x : +qls.y);");
dr2 = "dr2";
} else {
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
"qsr.x != 0 ? -qsr.x : 1);");
g->codeAppend ("float2 dl = (qls == float2(0)) ? dr : "
"float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
}
g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
// Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
// Each invocation emits a different third.
g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
g->codeAppendf("%s(%s, 1);", emitVertexFn, midpoint);
g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
g->codeAppend ("if (any(dnotequal)) {");
g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
g->codeAppend ("}");
g->codeAppend ("if (all(dnotequal)) {");
g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
g->codeAppend ("}");
g->codeAppend ("EndPrimitive();");
return 5;
}
};
class GSEdgeImpl : public GrCCPRCoverageProcessor::GSImpl {
public:
GSEdgeImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
const char* emitVertexFn, const char* rtAdjust,
const Shader::GeometryVars&) const override {
int numSides = fShader->getNumSegments();
g->codeAppendf("int nextidx = (sk_InvocationID + 1) %% %i;", numSides);
g->codeAppendf("float2 left = pts[%s > 0 ? sk_InvocationID : nextidx], "
"right = pts[%s > 0 ? nextidx : sk_InvocationID];",
wind.c_str(), wind.c_str());
Shader::EmitEdgeDistanceEquation(g, "left", "right", "float3 edge_distance_equation");
// qlr is defined in EmitEdgeDistanceEquation. TODO: address in a followup CL!
g->codeAppend ("float2x2 outer_pts = float2x2(left - bloat * qlr, right + bloat * qlr);");
g->codeAppend ("half2 outer_coverage = edge_distance_equation.xy * outer_pts + "
"edge_distance_equation.z;");
// d1 is defined in EmitEdgeDistanceEquation. TODO: address in a followup CL!
g->codeAppend ("float2 d2 = d1;");
g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
g->codeAppend ("if (aligned) {");
g->codeAppend ( "d1 -= qlr;");
g->codeAppend ( "d2 += qlr;");
g->codeAppend ("}");
// Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
// invocation emits a different edge. Emit negative coverage that subtracts the appropiate
// amount back out from the hull we drew above.
g->codeAppend ("if (!aligned) {");
g->codeAppendf( "%s(outer_pts[0], outer_coverage[0]);", emitVertexFn);
g->codeAppend ("}");
g->codeAppendf("%s(left + bloat * d1, -1);", emitVertexFn);
g->codeAppendf("%s(left - bloat * d2, 0);", emitVertexFn);
g->codeAppendf("%s(right + bloat * d2, -1);", emitVertexFn);
g->codeAppendf("%s(right - bloat * d1, 0);", emitVertexFn);
g->codeAppend ("if (!aligned) {");
g->codeAppendf( "%s(outer_pts[1], outer_coverage[1]);", emitVertexFn);
g->codeAppend ("}");
g->codeAppend ("EndPrimitive();");
return 6;
}
};
class GSCornerImpl : public GrCCPRCoverageProcessor::GSImpl {
public:
GSCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
const char* emitVertexFn, const char* rtAdjust,
const Shader::GeometryVars& vars) const override {
const char* corner = vars.fCornerVars.fPoint;
SkASSERT(corner);
g->codeAppendf("%s(%s + float2(-bloat.x, -bloat.y), 1);", emitVertexFn, corner);
g->codeAppendf("%s(%s + float2(-bloat.x, +bloat.y), 1);", emitVertexFn, corner);
g->codeAppendf("%s(%s + float2(+bloat.x, -bloat.y), 1);", emitVertexFn, corner);
g->codeAppendf("%s(%s + float2(+bloat.x, +bloat.y), 1);", emitVertexFn, corner);
g->codeAppend ("EndPrimitive();");
return 4;
}
};
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::CreateGSImpl(std::unique_ptr<Shader> shader) {
switch (shader->getGeometryType()) {
case Shader::GeometryType::kHull:
return new GSHullImpl(std::move(shader));
case Shader::GeometryType::kEdges:
return new GSEdgeImpl(std::move(shader));
case Shader::GeometryType::kCorners:
return new GSCornerImpl(std::move(shader));
}
SK_ABORT("Unexpected Shader::GeometryType.");
return nullptr;
}

View File

@ -1,216 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRCubicProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
void GrCCPRCubicProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
GrGLSLVertexBuilder* v,
const TexelBufferHandle& pointsBuffer,
const char* atlasOffset, const char* rtAdjust,
GrGPArgs* gpArgs) const {
v->codeAppend ("float2 self = ");
v->appendTexelFetch(pointsBuffer,
SkStringPrintf("%s.x + sk_VertexID", proc.instanceAttrib()).c_str());
v->codeAppendf(".xy + %s;", atlasOffset);
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
}
void GrCCPRCubicProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust,
const char* outputWind) const {
// We will define bezierpts in onEmitGeometryShader.
g->codeAppend ("float area_times_2 = "
"determinant(float3x3(1, bezierpts[0], "
"1, bezierpts[2], "
"0, bezierpts[3] - bezierpts[1]));");
// Drop curves that are nearly flat. The KLM math becomes unstable in this case.
g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[3] - bezierpts[0]) * %s.zx)) {",
rtAdjust);
#ifndef SK_BUILD_FOR_MAC
g->codeAppend ( "return;");
#else
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
g->codeAppend ( "area_times_2 = 0;");
#endif
g->codeAppend ("}");
g->codeAppendf("%s = sign(area_times_2);", outputWind);
}
void GrCCPRCubicProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
const char* wind, const char* rtAdjust) const {
// Prepend bezierpts at the start of the shader.
g->codePrependf("float4x2 bezierpts = float4x2(sk_in[0].sk_Position.xy, "
"sk_in[1].sk_Position.xy, "
"sk_in[2].sk_Position.xy, "
"sk_in[3].sk_Position.xy);");
// Evaluate the cubic at T=.5 for an mid-ish point.
g->codeAppendf("float2 midpoint = bezierpts * float4(.125, .375, .375, .125);");
// Find the cubic's power basis coefficients.
g->codeAppend ("float2x4 C = float4x4(-1, 3, -3, 1, "
" 3, -6, 3, 0, "
"-3, 3, 0, 0, "
" 1, 0, 0, 0) * transpose(bezierpts);");
// Find the cubic's inflection function.
g->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
g->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
g->codeAppend ("float D1 = +determinant(float2x2(C));");
// Calculate the KLM matrix.
g->declareGlobal(fKLMMatrix);
g->codeAppend ("float4 K, L, M;");
g->codeAppend ("float2 l, m;");
g->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
if (CubicType::kSerpentine == fCubicType) {
// This math also works out for the "cusp" and "cusp at infinity" cases.
g->codeAppend ("float q = sqrt(max(3*discr, 0));");
g->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
g->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
g->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
g->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
g->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
g->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
} else {
g->codeAppend ("float q = sqrt(max(-discr, 0));");
g->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
g->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
g->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
g->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
g->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
g->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
g->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
}
g->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
g->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
"C[1][0], C[1][middlerow], C[1][3], "
" 0, 0, 1));");
g->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
"L[0], L[middlerow], L[3], "
"M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
// Orient the KLM matrix so we fill the correct side of the curve.
g->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
fKLMMatrix.c_str(), fKLMMatrix.c_str());
g->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
"0, orientation[0], 0, "
"0, 0, orientation[1]);", fKLMMatrix.c_str());
g->declareGlobal(fKLMDerivatives);
g->codeAppendf("%s[0] = %s[0].xy * %s.xz;",
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
g->codeAppendf("%s[1] = %s[1].xy * %s.xz;",
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
g->codeAppendf("%s[2] = %s[2].xy * %s.xz;",
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
// Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
g->declareGlobal(fEdgeDistanceEquation);
g->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind);
g->codeAppendf("float2 edgept0 = bezierpts[edgeidx0];");
g->codeAppendf("float2 edgept1 = bezierpts[3 - edgeidx0];");
this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
this->emitCubicGeometry(g, emitVertexFn, wind, rtAdjust);
}
void GrCCPRCubicProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position,
const char* /*coverage*/,
const char* /*wind*/) const {
fnBody->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
fnBody->appendf("float d = dot(float3(%s, 1), %s);",
position, fEdgeDistanceEquation.c_str());
fnBody->appendf("%s = float4(klm, d);", fKLMD.gsOut());
this->onEmitPerVertexGeometryCode(fnBody);
}
void GrCCPRCubicHullProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
const char* wind, const char* rtAdjust) const {
// FIXME: we should clip this geometry at the tip of the curve.
int maxVertices = this->emitHullGeometry(g, emitVertexFn, "bezierpts", 4, "sk_InvocationID",
"midpoint");
g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
maxVertices, 4);
}
void GrCCPRCubicHullProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
// "klm" was just defined by the base class.
fnBody->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str());
fnBody->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str());
}
void GrCCPRCubicHullProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppend ("float f = k*k*k - l*m;");
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage);
f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat closing edge.
}
void GrCCPRCubicCornerProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g,
const char* emitVertexFn, const char* wind,
const char* rtAdjust) const {
// We defined bezierpts in onEmitGeometryShader.
g->declareGlobal(fEdgeDistanceDerivatives);
g->codeAppendf("%s = %s.xy * %s.xz;",
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
g->codeAppendf("float2 corner = bezierpts[sk_InvocationID * 3];");
int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip, numVertices, 2);
}
void GrCCPRCubicCornerProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
fnBody->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
fnBody->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
// Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z).
GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
}
void GrCCPRCubicCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);",
fdKLMDdx.fsIn(), fdKLMDdy.fsIn());
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
// the same pixel because those cases should have been weeded out by this point.
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppend ("float f = k*k*k - l*m;");
f->codeAppend ("float2 grad_f = float3(3*k*k, -m, -l) * float2x3(grad_klmd);");
f->codeAppendf("%s = -clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);",
outputCoverage);
f->codeAppendf("%s -= d;", outputCoverage);
// Use software msaa to estimate actual coverage at the corner pixels.
const int sampleCount = this->defineSoftSampleLocations(f, "samples");
f->codeAppendf("float4 klmd_center = float4(%s.xyz, %s.w + 0.5);",
fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
f->codeAppend ( "float4 klmd = grad_klmd * samples[i] + klmd_center;");
f->codeAppend ( "half f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;");
f->codeAppendf( "%s += all(greaterThan(half4(f, klmd.y, klmd.z, klmd.w), "
"half4(0))) ? %f : 0;",
outputCoverage, 1.0 / sampleCount);
f->codeAppend ("}");
}

View File

@ -1,120 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCCPRCubicProcessor_DEFINED
#define GrCCPRCubicProcessor_DEFINED
#include "ccpr/GrCCPRCoverageProcessor.h"
class GrGLSLGeometryBuilder;
/**
* This class renders the coverage of convex closed cubic segments using the techniques outlined in
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
* Jim Blinn:
*
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
*
* The provided curves must be convex, monotonic with respect to the vector of their closing edge
* [P3 - P0], and must not contain or be near any inflection points or loop intersections.
* (Use GrCCPRGeometry.)
*/
class GrCCPRCubicProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
public:
enum class CubicType {
kSerpentine,
kLoop
};
GrCCPRCubicProcessor(CubicType cubicType)
: INHERITED(CoverageType::kShader)
, fCubicType(cubicType)
, fKLMMatrix("klm_matrix", kFloat3x3_GrSLType, GrShaderVar::kNonArray,
kHigh_GrSLPrecision)
, fKLMDerivatives("klm_derivatives", kFloat2_GrSLType, 3, kHigh_GrSLPrecision)
, fEdgeDistanceEquation("edge_distance_equation", kFloat3_GrSLType,
GrShaderVar::kNonArray, kHigh_GrSLPrecision)
, fKLMD(kFloat4_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
varyingHandler->addVarying("klmd", &fKLMD, kHigh_GrSLPrecision);
}
void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
const char* rtAdjust, GrGPArgs*) const override;
void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
const char* rtAdjust) const final;
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
const char* wind) const final;
protected:
virtual void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
const char* wind, const char* rtAdjust) const = 0;
virtual void onEmitPerVertexGeometryCode(SkString* fnBody) const = 0;
const CubicType fCubicType;
GrShaderVar fKLMMatrix;
GrShaderVar fKLMDerivatives;
GrShaderVar fEdgeDistanceEquation;
GrGLSLGeoToFrag fKLMD;
typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
};
class GrCCPRCubicHullProcessor : public GrCCPRCubicProcessor {
public:
GrCCPRCubicHullProcessor(CubicType cubicType)
: INHERITED(cubicType)
, fGradMatrix(kFloat2x2_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
this->INHERITED::resetVaryings(varyingHandler);
varyingHandler->addVarying("grad_matrix", &fGradMatrix, kHigh_GrSLPrecision);
}
void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
const char* wind, const char* rtAdjust) const override;
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
protected:
GrGLSLGeoToFrag fGradMatrix;
typedef GrCCPRCubicProcessor INHERITED;
};
class GrCCPRCubicCornerProcessor : public GrCCPRCubicProcessor {
public:
GrCCPRCubicCornerProcessor(CubicType cubicType)
: INHERITED(cubicType)
, fEdgeDistanceDerivatives("edge_distance_derivatives", kFloat2_GrSLType,
GrShaderVar::kNonArray, kHigh_GrSLPrecision)
, fdKLMDdx(kFloat4_GrSLType)
, fdKLMDdy(kFloat4_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
this->INHERITED::resetVaryings(varyingHandler);
varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx, kHigh_GrSLPrecision);
varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy, kHigh_GrSLPrecision);
}
void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
const char* wind, const char* rtAdjust) const override;
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
protected:
GrShaderVar fEdgeDistanceDerivatives;
GrGLSLGeoToFrag fdKLMDdx;
GrGLSLGeoToFrag fdKLMDdy;
typedef GrCCPRCubicProcessor INHERITED;
};
#endif

View File

@ -0,0 +1,203 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRCubicShader.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
void GrCCPRCubicShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
GrGLSLShaderBuilder* s,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const {
s->appendTexelFetch(pointsBuffer,
SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
}
void GrCCPRCubicShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
const char* rtAdjust, const char* outputWind) const {
s->codeAppendf("float area_times_2 = determinant(float3x3(1, %s[0], "
"1, %s[2], "
"0, %s[3] - %s[1]));",
pts, pts, pts, pts);
// Drop curves that are nearly flat. The KLM math becomes unstable in this case.
s->codeAppendf("if (2 * abs(area_times_2) < length((%s[3] - %s[0]) * %s.zx)) {",
pts, pts, rtAdjust);
#ifndef SK_BUILD_FOR_MAC
s->codeAppend ( "return;");
#else
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
s->codeAppend ( "area_times_2 = 0;");
#endif
s->codeAppend ("}");
s->codeAppendf("%s = sign(area_times_2);", outputWind);
}
void GrCCPRCubicShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* segmentId, const char* bloat, const char* wind,
const char* rtAdjust, GeometryVars* vars) const {
// Evaluate the cubic at T=.5 for an mid-ish point.
s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
// Find the cubic's power basis coefficients.
s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
" 3, -6, 3, 0, "
"-3, 3, 0, 0, "
" 1, 0, 0, 0) * transpose(%s);", pts);
// Find the cubic's inflection function.
s->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
s->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
s->codeAppend ("float D1 = +determinant(float2x2(C));");
// Calculate the KLM matrix.
s->declareGlobal(fKLMMatrix);
s->codeAppend ("float4 K, L, M;");
s->codeAppend ("float2 l, m;");
s->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
if (CubicType::kSerpentine == fCubicType) {
// This math also works out for the "cusp" and "cusp at infinity" cases.
s->codeAppend ("float q = sqrt(max(3*discr, 0));");
s->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
s->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
s->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
s->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
s->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
s->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
} else {
s->codeAppend ("float q = sqrt(max(-discr, 0));");
s->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
s->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
s->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
s->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
s->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
s->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
s->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
}
s->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
s->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
"C[1][0], C[1][middlerow], C[1][3], "
" 0, 0, 1));");
s->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
"L[0], L[middlerow], L[3], "
"M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
// Orient the KLM matrix so we fill the correct side of the curve.
s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
fKLMMatrix.c_str(), fKLMMatrix.c_str());
s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
"0, orientation[0], 0, "
"0, 0, orientation[1]);", fKLMMatrix.c_str());
s->declareGlobal(fKLMDerivatives);
s->codeAppendf("%s[0] = %s[0].xy * %s.xz;",
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
s->codeAppendf("%s[1] = %s[1].xy * %s.xz;",
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
s->codeAppendf("%s[2] = %s[2].xy * %s.xz;",
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
// Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
s->declareGlobal(fEdgeDistanceEquation);
s->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind);
s->codeAppendf("float2 edgept0 = %s[edgeidx0];", pts);
s->codeAppendf("float2 edgept1 = %s[3 - edgeidx0];", pts);
Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
this->onEmitSetupCode(s, pts, segmentId, rtAdjust, vars);
}
GrCCPRCubicShader::WindHandling
GrCCPRCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
const char* position, const char* /*coverage*/,
const char* /*wind*/) {
varyingHandler->addVarying("klmd", &fKLMD, kHigh_GrSLPrecision);
code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
code->appendf("float d = dot(float3(%s, 1), %s);", position, fEdgeDistanceEquation.c_str());
code->appendf("%s = float4(klm, d);", fKLMD.gsOut());
this->onEmitVaryings(varyingHandler, code);
return WindHandling::kNotHandled;
}
void GrCCPRCubicHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* /*pts*/,
const char* /*wedgeId*/, const char* /*rtAdjust*/,
GeometryVars* vars) const {
// "midpoint" was just defined by the base class.
vars->fHullVars.fAlternateMidpoint = "midpoint";
}
void GrCCPRCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code) {
// "klm" was just defined by the base class.
varyingHandler->addVarying("grad_matrix", &fGradMatrix, kHigh_GrSLPrecision);
code->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str());
code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str());
}
void GrCCPRCubicHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppend ("float f = k*k*k - l*m;");
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage);
f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat closing edge.
}
void GrCCPRCubicCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* cornerId, const char* rtAdjust,
GeometryVars* vars) const {
s->declareGlobal(fEdgeDistanceDerivatives);
s->codeAppendf("%s = %s.xy * %s.xz;",
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
s->codeAppendf("float2 corner = %s[%s * 3];", pts, cornerId);
vars->fCornerVars.fPoint = "corner";
}
void GrCCPRCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code) {
varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx, kHigh_GrSLPrecision);
code->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy, kHigh_GrSLPrecision);
code->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
// Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z).
GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
}
void GrCCPRCubicCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn());
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
// the same pixel because those cases should have been weeded out by this point.
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppend ("float f = k*k*k - l*m;");
f->codeAppend ("float2 grad_f = float3(3*k*k, -m, -l) * float2x3(grad_klmd);");
f->codeAppendf("%s = -clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);",
outputCoverage);
f->codeAppendf("%s -= d;", outputCoverage);
// Use software msaa to estimate actual coverage at the corner pixels.
const int sampleCount = Shader::DefineSoftSampleLocations(f, "samples");
f->codeAppendf("float4 klmd_center = float4(%s.xyz, %s.w + 0.5);",
fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
f->codeAppend ( "float4 klmd = grad_klmd * samples[i] + klmd_center;");
f->codeAppend ( "half f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;");
f->codeAppendf( "%s += all(greaterThan(half4(f, klmd.y, klmd.z, klmd.w), "
"half4(0))) ? %f : 0;",
outputCoverage, 1.0 / sampleCount);
f->codeAppend ("}");
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCCPRCubicShader_DEFINED
#define GrCCPRCubicShader_DEFINED
#include "ccpr/GrCCPRCoverageProcessor.h"
/**
* This class renders the coverage of convex closed cubic segments using the techniques outlined in
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
* Jim Blinn:
*
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
*
* The provided curve segments must be convex, monotonic with respect to the vector of their closing
* edge [P3 - P0], and must not contain or be near any inflection points or loop intersections.
* (Use GrCCPRGeometry.)
*/
class GrCCPRCubicShader : public GrCCPRCoverageProcessor::Shader {
public:
enum class CubicType {
kSerpentine,
kLoop
};
protected:
GrCCPRCubicShader(CubicType cubicType) : fCubicType(cubicType) {}
int getNumInputPoints() const final { return 4; }
void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const final;
void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
const char* outputWind) const final;
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* bloat, const char* wind, const char* rtAdjust,
GeometryVars*) const final;
virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* rtAdjust, GeometryVars*) const = 0;
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) final;
virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
const CubicType fCubicType;
GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
GrShaderVar fKLMDerivatives{"klm_derivatives", kFloat2_GrSLType, 3};
GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
GrGLSLGeoToFrag fKLMD{kFloat4_GrSLType};
};
class GrCCPRCubicHullShader : public GrCCPRCubicShader {
public:
GrCCPRCubicHullShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
private:
GeometryType getGeometryType() const override { return GeometryType::kHull; }
int getNumSegments() const override { return 4; } // 4 wedges.
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
const char* rtAdjust, GeometryVars*) const override;
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
GrGLSLGeoToFrag fGradMatrix{kFloat2x2_GrSLType};
};
class GrCCPRCubicCornerShader : public GrCCPRCubicShader {
public:
GrCCPRCubicCornerShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
private:
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
int getNumSegments() const override { return 2; } // 2 corners.
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
const char* rtAdjust, GeometryVars*) const override;
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
GrShaderVar fEdgeDistanceDerivatives{"edge_distance_derivatives", kFloat2_GrSLType};
GrGLSLGeoToFrag fdKLMDdx{kFloat4_GrSLType};
GrGLSLGeoToFrag fdKLMDdy{kFloat4_GrSLType};
};
#endif

View File

@ -1,168 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRQuadraticProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
void GrCCPRQuadraticProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
GrGLSLVertexBuilder* v,
const TexelBufferHandle& pointsBuffer,
const char* atlasOffset, const char* rtAdjust,
GrGPArgs* gpArgs) const {
v->codeAppend ("float2 self = ");
v->appendTexelFetch(pointsBuffer,
SkStringPrintf("%s.x + sk_VertexID", proc.instanceAttrib()).c_str());
v->codeAppendf(".xy + %s;", atlasOffset);
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
}
void GrCCPRQuadraticProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust,
const char* outputWind) const {
// We will define bezierpts in onEmitGeometryShader.
g->codeAppend ("float area_times_2 = determinant(float2x2(bezierpts[1] - bezierpts[0], "
"bezierpts[2] - bezierpts[0]));");
// Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[2] - bezierpts[0]) * %s.zx)) {",
rtAdjust);
#ifndef SK_BUILD_FOR_MAC
g->codeAppend ( "return;");
#else
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
g->codeAppend ( "area_times_2 = 0;");
#endif
g->codeAppend ("}");
g->codeAppendf("%s = sign(area_times_2);", outputWind);
}
void GrCCPRQuadraticProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
const char* emitVertexFn, const char* wind,
const char* rtAdjust) const {
// Prepend bezierpts at the start of the shader.
g->codePrependf("float3x2 bezierpts = float3x2(sk_in[0].sk_Position.xy, "
"sk_in[1].sk_Position.xy, "
"sk_in[2].sk_Position.xy);");
g->declareGlobal(fCanonicalMatrix);
g->codeAppendf("%s = float3x3(0.0, 0, 1, "
"0.5, 0, 1, "
"1.0, 1, 1) * "
"inverse(float3x3(bezierpts[0], 1, "
"bezierpts[1], 1, "
"bezierpts[2], 1));",
fCanonicalMatrix.c_str());
g->declareGlobal(fCanonicalDerivatives);
g->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
g->declareGlobal(fEdgeDistanceEquation);
g->codeAppendf("float2 edgept0 = bezierpts[%s > 0 ? 2 : 0];", wind);
g->codeAppendf("float2 edgept1 = bezierpts[%s > 0 ? 0 : 2];", wind);
this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
this->emitQuadraticGeometry(g, emitVertexFn, rtAdjust);
}
void GrCCPRQuadraticProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position,
const char* /*coverage*/,
const char* /*wind*/) const {
fnBody->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
fXYD.gsOut(), fCanonicalMatrix.c_str(), position);
fnBody->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
fXYD.gsOut(), fEdgeDistanceEquation.c_str(), position,
fEdgeDistanceEquation.c_str());
this->onEmitPerVertexGeometryCode(fnBody);
}
void GrCCPRQuadraticHullProcessor::emitQuadraticGeometry(GrGLSLGeometryBuilder* g,
const char* emitVertexFn,
const char* /*rtAdjust*/) const {
// Find the t value whose tangent is halfway between the tangents at the endpionts.
// (We defined bezierpts in onEmitGeometryShader.)
g->codeAppend ("float2 tan0 = bezierpts[1] - bezierpts[0];");
g->codeAppend ("float2 tan1 = bezierpts[2] - bezierpts[1];");
g->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
g->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
g->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s=0 is weeded out by this point.
// Clip the bezier triangle by the tangent at our new t value. This is a simple application for
// De Casteljau's algorithm.
g->codeAppendf("float4x2 quadratic_hull = float4x2(bezierpts[0], "
"bezierpts[0] + tan0 * t, "
"bezierpts[1] + tan1 * t, "
"bezierpts[2]);");
int maxVerts = this->emitHullGeometry(g, emitVertexFn, "quadratic_hull", 4, "sk_InvocationID");
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
maxVerts, 4);
}
void GrCCPRQuadraticHullProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
fnBody->appendf("%s = float2(2 * %s.x, -1) * %s;",
fGradXY.gsOut(), fXYD.gsOut(), fCanonicalDerivatives.c_str());
}
void GrCCPRQuadraticHullProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn(), fGradXY.fsIn(), fGradXY.fsIn());
f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYD.fsIn()); // Flat closing edge.
}
void GrCCPRQuadraticCornerProcessor::emitQuadraticGeometry(GrGLSLGeometryBuilder* g,
const char* emitVertexFn,
const char* rtAdjust) const {
g->declareGlobal(fEdgeDistanceDerivatives);
g->codeAppendf("%s = %s.xy * %s.xz;",
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
g->codeAppendf("float2 corner = bezierpts[sk_InvocationID * 2];");
int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip, numVertices, 2);
}
void GrCCPRQuadraticCornerProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
fnBody->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
fdXYDdx.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
fEdgeDistanceDerivatives.c_str());
fnBody->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
fdXYDdy.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
fEdgeDistanceDerivatives.c_str());
}
void GrCCPRQuadraticCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn());
f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn());
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
// the same pixel because those cases should have been weeded out by this point.
f->codeAppend ("float f = x*x - y;");
f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);");
f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage);
f->codeAppendf("%s -= d;", outputCoverage);
// Use software msaa to approximate coverage at the corner pixels.
int sampleCount = this->defineSoftSampleLocations(f, "samples");
f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);",
fXYD.fsIn(), fXYD.fsIn());
f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
f->codeAppend ( "float3 xyd = grad_xyd * samples[i] + xyd_center;");
f->codeAppend ( "half f = xyd.y - xyd.x * xyd.x;"); // f > 0 -> inside curve.
f->codeAppendf( "%s += all(greaterThan(float2(f,xyd.z), float2(0))) ? %f : 0;",
outputCoverage, 1.0 / sampleCount);
f->codeAppendf("}");
}

View File

@ -1,117 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCCPRQuadraticProcessor_DEFINED
#define GrCCPRQuadraticProcessor_DEFINED
#include "ccpr/GrCCPRCoverageProcessor.h"
/**
* This class renders the coverage of closed quadratic curves using the techniques outlined in
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
* Jim Blinn:
*
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
*
* The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
* (Use GrCCPRGeometry.)
*/
class GrCCPRQuadraticProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
public:
GrCCPRQuadraticProcessor()
: INHERITED(CoverageType::kShader)
, fCanonicalMatrix("canonical_matrix", kFloat3x3_GrSLType, GrShaderVar::kNonArray)
, fCanonicalDerivatives("canonical_derivatives", kFloat2x2_GrSLType,
GrShaderVar::kNonArray)
, fEdgeDistanceEquation("edge_distance_equation", kFloat3_GrSLType,
GrShaderVar::kNonArray)
, fXYD(kFloat3_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
varyingHandler->addVarying("xyd", &fXYD, kHigh_GrSLPrecision);
}
void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
const char* rtAdjust, GrGPArgs*) const override;
void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
const char* rtAdjust) const final;
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
const char* wind) const final;
protected:
virtual void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
const char* rtAdjust) const = 0;
virtual void onEmitPerVertexGeometryCode(SkString* fnBody) const = 0;
GrShaderVar fCanonicalMatrix;
GrShaderVar fCanonicalDerivatives;
GrShaderVar fEdgeDistanceEquation;
GrGLSLGeoToFrag fXYD;
typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
};
/**
* This pass draws a conservative raster hull around the quadratic bezier curve, computes the
* curve's coverage using the gradient-based AA technique outlined in the Loop/Blinn paper, and
* uses simple distance-to-edge to subtract out coverage for the flat closing edge [P2 -> P0]. Since
* the provided curves are monotonic, this will get every pixel right except the two corners.
*/
class GrCCPRQuadraticHullProcessor : public GrCCPRQuadraticProcessor {
public:
GrCCPRQuadraticHullProcessor()
: fGradXY(kFloat2_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
this->INHERITED::resetVaryings(varyingHandler);
varyingHandler->addVarying("grad_xy", &fGradXY, kHigh_GrSLPrecision);
}
void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
const char* rtAdjust) const override;
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
void emitShaderCoverage(GrGLSLFragmentBuilder* f, const char* outputCoverage) const override;
private:
GrGLSLGeoToFrag fGradXY;
typedef GrCCPRQuadraticProcessor INHERITED;
};
/**
* This pass fixes the corners of a closed quadratic segment with soft MSAA.
*/
class GrCCPRQuadraticCornerProcessor : public GrCCPRQuadraticProcessor {
public:
GrCCPRQuadraticCornerProcessor()
: fEdgeDistanceDerivatives("edge_distance_derivatives", kFloat2_GrSLType,
GrShaderVar::kNonArray)
, fdXYDdx(kFloat3_GrSLType)
, fdXYDdy(kFloat3_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
this->INHERITED::resetVaryings(varyingHandler);
varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx, kHigh_GrSLPrecision);
varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy, kHigh_GrSLPrecision);
}
void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
const char* rtAdjust) const override;
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
private:
GrShaderVar fEdgeDistanceDerivatives;
GrGLSLGeoToFrag fdXYDdx;
GrGLSLGeoToFrag fdXYDdy;
typedef GrCCPRQuadraticProcessor INHERITED;
};
#endif

View File

@ -0,0 +1,157 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRQuadraticShader.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
void GrCCPRQuadraticShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
GrGLSLShaderBuilder* s,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const {
s->appendTexelFetch(pointsBuffer,
SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
}
void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
const char* outputWind) const {
s->codeAppendf("float area_times_2 = determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0]));",
pts, pts, pts, pts);
// Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
s->codeAppendf("if (2 * abs(area_times_2) < length((%s[2] - %s[0]) * %s.zx)) {",
pts, pts, rtAdjust);
#ifndef SK_BUILD_FOR_MAC
s->codeAppend ( "return;");
#else
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
s->codeAppend ( "area_times_2 = 0;");
#endif
s->codeAppend ("}");
s->codeAppendf("%s = sign(area_times_2);", outputWind);
}
void GrCCPRQuadraticShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* segmentId, const char* bloat,
const char* wind, const char* rtAdjust,
GeometryVars* vars) const {
s->declareGlobal(fCanonicalMatrix);
s->codeAppendf("%s = float3x3(0.0, 0, 1, "
"0.5, 0, 1, "
"1.0, 1, 1) * "
"inverse(float3x3(%s[0], 1, "
"%s[1], 1, "
"%s[2], 1));",
fCanonicalMatrix.c_str(), pts, pts, pts);
s->declareGlobal(fCanonicalDerivatives);
s->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
s->declareGlobal(fEdgeDistanceEquation);
s->codeAppendf("float2 edgept0 = %s[%s > 0 ? 2 : 0];", pts, wind);
s->codeAppendf("float2 edgept1 = %s[%s > 0 ? 0 : 2];", pts, wind);
Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
this->onEmitSetupCode(s, pts, segmentId, rtAdjust, vars);
}
GrCCPRQuadraticShader::WindHandling
GrCCPRQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
const char* position, const char* /*coverage*/,
const char* /*wind*/) {
varyingHandler->addVarying("xyd", &fXYD, kHigh_GrSLPrecision);
code->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
fXYD.gsOut(), fCanonicalMatrix.c_str(), position);
code->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
fXYD.gsOut(), fEdgeDistanceEquation.c_str(), position,
fEdgeDistanceEquation.c_str());
this->onEmitVaryings(varyingHandler, code);
return WindHandling::kNotHandled;
}
void GrCCPRQuadraticHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* /*wedgeId*/, const char* /*rtAdjust*/,
GeometryVars* vars) const {
// Find the T value whose tangent is halfway between the tangents at the endpionts.
s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts);
s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts);
s->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
s->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
s->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s != 0; we cull flat curves on CPU.
// Clip the bezier triangle by the tangent at our new t value. This is a simple application for
// De Casteljau's algorithm.
s->codeAppendf("float4x2 quadratic_hull = float4x2(%s[0], "
"%s[0] + tan0 * t, "
"%s[1] + tan1 * t, "
"%s[2]);", pts, pts, pts, pts);
vars->fHullVars.fAlternatePoints = "quadratic_hull";
}
void GrCCPRQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
SkString* code) {
varyingHandler->addVarying("grad", &fGrad, kHigh_GrSLPrecision);
code->appendf("%s = float2(2 * %s.x, -1) * %s;",
fGrad.gsOut(), fXYD.gsOut(), fCanonicalDerivatives.c_str());
}
void GrCCPRQuadraticHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn(), fGrad.fsIn(), fGrad.fsIn());
f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYD.fsIn()); // Flat closing edge.
}
void GrCCPRQuadraticCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* cornerId, const char* rtAdjust,
GeometryVars* vars) const {
s->declareGlobal(fEdgeDistanceDerivatives);
s->codeAppendf("%s = %s.xy * %s.xz;",
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
s->codeAppendf("float2 corner = %s[%s * 2];", pts, cornerId);
vars->fCornerVars.fPoint = "corner";
}
void GrCCPRQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
SkString* code) {
varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx, kHigh_GrSLPrecision);
code->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
fdXYDdx.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
fEdgeDistanceDerivatives.c_str());
varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy, kHigh_GrSLPrecision);
code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
fdXYDdy.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
fEdgeDistanceDerivatives.c_str());
}
void GrCCPRQuadraticCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn());
f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn());
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
// the same pixel because those cases should have been weeded out by this point.
f->codeAppend ("float f = x*x - y;");
f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);");
f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage);
f->codeAppendf("%s -= d;", outputCoverage);
// Use software msaa to approximate coverage at the corner pixels.
int sampleCount = Shader::DefineSoftSampleLocations(f, "samples");
f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);", fXYD.fsIn(), fXYD.fsIn());
f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
f->codeAppend ( "float3 xyd = grad_xyd * samples[i] + xyd_center;");
f->codeAppend ( "half f = xyd.y - xyd.x * xyd.x;"); // f > 0 -> inside curve.
f->codeAppendf( "%s += all(greaterThan(float2(f,xyd.z), float2(0))) ? %f : 0;",
outputCoverage, 1.0 / sampleCount);
f->codeAppendf("}");
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCCPRQuadraticShader_DEFINED
#define GrCCPRQuadraticShader_DEFINED
#include "ccpr/GrCCPRCoverageProcessor.h"
/**
* This class renders the coverage of closed quadratic curves using the techniques outlined in
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
* Jim Blinn:
*
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
*
* The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
* (Use GrCCPRGeometry.)
*/
class GrCCPRQuadraticShader : public GrCCPRCoverageProcessor::Shader {
protected:
int getNumInputPoints() const final { return 3; }
void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const override;
void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
const char* outputWind) const final;
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* bloat, const char* wind, const char* rtAdjust,
GeometryVars*) const final;
virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* rtAdjust, GeometryVars*) const = 0;
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) final;
virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType};
const GrShaderVar fCanonicalDerivatives{"canonical_derivatives", kFloat2x2_GrSLType};
const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
GrGLSLGeoToFrag fXYD{kFloat3_GrSLType};
};
/**
* This pass draws a conservative raster hull around the quadratic bezier curve, computes the
* curve's coverage using the gradient-based AA technique outlined in the Loop/Blinn paper, and
* uses simple distance-to-edge to subtract out coverage for the flat closing edge [P2 -> P0]. Since
* the provided curves are monotonic, this will get every pixel right except the two corners.
*/
class GrCCPRQuadraticHullShader : public GrCCPRQuadraticShader {
int getNumSegments() const final { return 4; } // 4 wedges.
GeometryType getGeometryType() const override { return GeometryType::kHull; }
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
const char* rtAdjust, GeometryVars*) const override;
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
GrGLSLGeoToFrag fGrad{kFloat2_GrSLType};
};
/**
* This pass fixes the corners of a closed quadratic segment with soft MSAA.
*/
class GrCCPRQuadraticCornerShader : public GrCCPRQuadraticShader {
int getNumSegments() const final { return 2; } // 2 corners.
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
const char* rtAdjust, GeometryVars*) const override;
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
const GrShaderVar fEdgeDistanceDerivatives{"edge_distance_derivatives", kFloat2_GrSLType};
GrGLSLGeoToFrag fdXYDdx{kFloat3_GrSLType};
GrGLSLGeoToFrag fdXYDdy{kFloat3_GrSLType};
};
#endif

View File

@ -1,185 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRTriangleProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
void GrCCPRTriangleProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
GrGLSLVertexBuilder* v,
const TexelBufferHandle& pointsBuffer,
const char* atlasOffset, const char* rtAdjust,
GrGPArgs* gpArgs) const {
// Copy the input attrib to an intermediate array. The Intel GLSL compiler hits an internal
// assertion if we index the input attrib itself with sk_VertexID.
v->codeAppendf("int indices[3] = int[3](%s.x, %s.y, %s.z);",
proc.instanceAttrib(), proc.instanceAttrib(), proc.instanceAttrib());
v->codeAppend ("float2 self = ");
v->appendTexelFetch(pointsBuffer, "indices[sk_VertexID]");
v->codeAppendf(".xy + %s;", atlasOffset);
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
}
void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const {
// Prepend in_vertices at the start of the shader.
g->codePrependf("float3x2 in_vertices = float3x2(sk_in[0].sk_Position.xy, "
"sk_in[1].sk_Position.xy, "
"sk_in[2].sk_Position.xy);");
}
void GrCCPRTriangleProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* /*rtAdjust*/,
const char* outputWind) const {
// We will define in_vertices in defineInputVertices.
g->codeAppendf("%s = sign(determinant(float2x2(in_vertices[1] - in_vertices[0], "
"in_vertices[2] - in_vertices[0])));",
outputWind);
}
void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
const char* emitVertexFn,
const char* wind,
const char* rtAdjust) const {
this->defineInputVertices(g);
int maxOutputVertices = 0;
if (GeometryType::kEdges != fGeometryType) {
maxOutputVertices += this->emitHullGeometry(g, emitVertexFn, "in_vertices", 3,
"sk_InvocationID");
}
if (GeometryType::kHulls != fGeometryType) {
g->codeAppend ("int edgeidx0 = sk_InvocationID, "
"edgeidx1 = (edgeidx0 + 1) % 3;");
g->codeAppendf("float2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind);
g->codeAppendf("float2 edgept1 = in_vertices[%s > 0 ? edgeidx1 : edgeidx0];", wind);
maxOutputVertices += this->emitEdgeGeometry(g, emitVertexFn, "edgept0", "edgept1");
}
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
maxOutputVertices, 3);
}
void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
const char* emitVertexFn, const char* wind,
const char* rtAdjust) const {
this->defineInputVertices(g);
g->codeAppend ("float2 corner = in_vertices[sk_InvocationID];");
g->codeAppend ("float2x2 vectors = float2x2(corner - in_vertices[(sk_InvocationID + 2) % 3], "
"corner - in_vertices[(sk_InvocationID + 1) % 3]);");
// Make sure neither vector is 0 in order to avoid a divide-by-zero. Wind will be zero anyway if
// this is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
g->codeAppendf("for (int i = 0; i < 2; ++i) {");
g->codeAppendf( "vectors[i] = any(notEqual(vectors[i], float2(0))) ? "
"vectors[i] : float2(1);");
g->codeAppendf("}");
// Find the vector that bisects the region outside the incoming edges. Each edge is responsible
// to subtract the outside region on its own the side of the bisector.
g->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
g->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
g->codeAppendf("float2 bisect = dot(leftdir, rightdir) >= 0 ? leftdir + rightdir : "
"float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
// In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge method
// actually finds is coverage inside a logical "AA box", one that is rotated inline with the
// edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set up
// transformations into normalized logical AA box space for both incoming edges. These will tell
// the fragment shader where the corner is located within each edge's AA box.
g->declareGlobal(fAABoxMatrices);
g->declareGlobal(fAABoxTranslates);
g->declareGlobal(fGeoShaderBisects);
g->codeAppendf("for (int i = 0; i < 2; ++i) {");
// The X component runs parallel to the edge (i.e. distance to the corner).
g->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
g->codeAppendf( "float nwidth = dot(abs(n), bloat) * 2;");
g->codeAppendf( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
g->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
g->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
// The Y component runs perpendicular to the edge (i.e. distance-to-edge).
// NOTE: once we are back in device space and bloat.x == bloat.y, we will not need to find and
// divide by nwidth a second time.
g->codeAppendf( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
g->codeAppendf( "nwidth = dot(abs(n), bloat) * 2;");
g->codeAppendf( "n /= nwidth;");
g->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
g->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
// Translate the bisector into logical AA box space.
// NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
// bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
g->codeAppendf( "%s[i] = -bisect * %s[i];",
fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
g->codeAppendf("}");
int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
numVertices, 3);
}
void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody,
const char* position,
const char* /*coverage*/,
const char* wind) const {
fnBody->appendf("for (int i = 0; i < 2; ++i) {");
fnBody->appendf( "%s[i] = %s * %s[i] + %s[i];",
fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
fAABoxTranslates.c_str());
fnBody->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
fnBody->appendf("}");
}
void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
const char* outputCoverage) const {
// By the time we reach this shader, the pixel is in the following state:
//
// 1. The hull shader has emitted a coverage of 1.
// 2. Both edges have subtracted the area on their outside.
//
// This generally works, but it is a problem for corner pixels. There is a region within corner
// pixels that is outside both edges at the same time. This means the region has been double
// subtracted (once by each edge). The purpose of this shader is to fix these corner pixels.
//
// More specifically, each edge redoes its coverage analysis so that it only subtracts the
// outside area that falls on its own side of the bisector line.
//
// NOTE: unless the edges fall on multiples of 90 deg from one another, they will have different
// AA boxes. (For an explanation of AA boxes, see comments in onEmitGeometryShader.) This means
// the coverage analysis will only be approximate. It seems acceptable, but if we want exact
// coverage we will need to switch to a more expensive model.
f->codeAppendf("%s = 0;", outputCoverage);
// Loop through both edges.
f->codeAppendf("for (int i = 0; i < 2; ++i) {");
f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
// Find the point at which the bisector exits the logical AA box.
// (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
": d.x / bisect.x;");
f->codeAppendf( "half2 exit = corner + bisect * T;");
// These lines combined (and the final multiply by .5) accomplish the following:
// 1. Add back the area beyond the corner that was subtracted out previously.
// 2. Subtract out the area beyond the corner, but under the bisector.
// The other edge will take care of the area on its own side of the bisector.
f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
f->codeAppendf("}");
f->codeAppendf("%s *= .5;", outputCoverage);
}

View File

@ -1,103 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCCPRTriangleProcessor_DEFINED
#define GrCCPRTriangleProcessor_DEFINED
#include "ccpr/GrCCPRCoverageProcessor.h"
/**
* This class renders the coverage of triangles.
*
* Triangles are rendered in three passes:
*
* Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
* is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
* convex hull of those boxes.)
*
* Pass 2: Smooth the edges that were over-rendered during Pass 1. Draw the conservative raster of
* each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
* coverage=-1 on the outside edge to coverage=0 on the inside edge.
*
* Pass 3: Touch up the corner pixels to have the correct coverage.
*/
class GrCCPRTriangleProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
public:
GrCCPRTriangleProcessor(CoverageType initialCoverage) : INHERITED(initialCoverage) {}
void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
const char* rtAdjust, GrGPArgs*) const override;
void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
protected:
void defineInputVertices(GrGLSLGeometryBuilder*) const;
private:
typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
};
class GrCCPRTriangleHullAndEdgeProcessor : public GrCCPRTriangleProcessor {
public:
enum class GeometryType {
kHulls,
kEdges,
kHullsAndEdges
};
GrCCPRTriangleHullAndEdgeProcessor(GeometryType geometryType)
: INHERITED(GeometryType::kHulls == geometryType ?
CoverageType::kOne : CoverageType::kInterpolated)
, fGeometryType(geometryType) {}
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
const char* rtAdjust) const override;
private:
const GeometryType fGeometryType;
typedef GrCCPRTriangleProcessor INHERITED;
};
/**
* This pass fixes the corner pixels of a triangle. It touches up the simple distance-to-edge
* coverage analysis done previously so that it takes into account the region that is outside both
* edges at the same time.
*/
class GrCCPRTriangleCornerProcessor : public GrCCPRTriangleProcessor {
public:
GrCCPRTriangleCornerProcessor()
: INHERITED(CoverageType::kShader)
, fAABoxMatrices("aa_box_matrices", kFloat2x2_GrSLType, 2)
, fAABoxTranslates("aa_box_translates", kFloat2_GrSLType, 2)
, fGeoShaderBisects("bisects", kFloat2_GrSLType, 2)
, fCornerLocationInAABoxes(kFloat2x2_GrSLType)
, fBisectInAABoxes(kFloat2x2_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
this->INHERITED::resetVaryings(varyingHandler);
varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
}
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
const char* rtAdjust) const override;
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
const char* wind) const override;
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
private:
GrShaderVar fAABoxMatrices;
GrShaderVar fAABoxTranslates;
GrShaderVar fGeoShaderBisects;
GrGLSLGeoToFrag fCornerLocationInAABoxes;
GrGLSLGeoToFrag fBisectInAABoxes;
typedef GrCCPRTriangleProcessor INHERITED;
};
#endif

View File

@ -0,0 +1,168 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRTriangleShader.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
void GrCCPRTriangleShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
GrGLSLShaderBuilder* s,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const {
s->appendTexelFetch(pointsBuffer,
SkStringPrintf("%s[%s]", proc.instanceAttrib(), pointId).c_str());
}
void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
const char* outputWind) const {
s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));",
outputWind, pts, pts, pts, pts);
}
GrCCPRTriangleHullShader::WindHandling
GrCCPRTriangleHullShader::onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
const char* /*position*/, const char* /*coverage*/,
const char* /*wind*/) {
return WindHandling::kNotHandled; // No varyings.Let the base class handle wind.
}
void GrCCPRTriangleHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("%s = 1;", outputCoverage);
}
GrCCPRTriangleEdgeShader::WindHandling
GrCCPRTriangleEdgeShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
const char* position, const char* coverage,
const char* wind) {
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind, kLow_GrSLPrecision);
code->appendf("%s = %s * %s;", fCoverageTimesWind.gsOut(), coverage, wind);
return WindHandling::kHandled;
}
void GrCCPRTriangleEdgeShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
}
void GrCCPRTriangleCornerShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* cornerId, const char* bloat,
const char* wind, const char* rtAdjust,
GeometryVars* vars) const {
s->codeAppendf("float2 corner = %s[sk_InvocationID];", pts);
vars->fCornerVars.fPoint = "corner";
s->codeAppendf("float2x2 vectors = float2x2(corner - %s[(sk_InvocationID + 2) %% 3], "
"corner - %s[(sk_InvocationID + 1) %% 3]);",
pts, pts);
// Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this
// is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
s->codeAppend ("for (int i = 0; i < 2; ++i) {");
s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);");
s->codeAppend ("}");
// Find the vector that bisects the region outside the incoming edges. Each edge is
// responsible to subtract the outside region on its own the side of the bisector.
s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? "
"leftdir + rightdir : "
"float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
// In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge
// method actually finds is coverage inside a logical "AA box", one that is rotated inline
// with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set
// up transformations into normalized logical AA box space for both incoming edges. These
// will tell the fragment shader where the corner is located within each edge's AA box.
s->declareGlobal(fAABoxMatrices);
s->declareGlobal(fAABoxTranslates);
s->declareGlobal(fGeoShaderBisects);
s->codeAppendf("for (int i = 0; i < 2; ++i) {");
// The X component runs parallel to the edge (i.e. distance to the corner).
s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
s->codeAppend ( "float nwidth = dot(abs(n), bloat) * 2;");
s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
// The Y component runs perpendicular to the edge (i.e. distance-to-edge).
// NOTE: if we are back in device space and bloat.x == bloat.y, we will not need to find and
// divide by nwidth a second time.
s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
s->codeAppend ( "nwidth = dot(abs(n), bloat) * 2;");
s->codeAppend ( "n /= nwidth;");
s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
// Translate the bisector into logical AA box space.
// NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
// bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
s->codeAppendf( "%s[i] = -bisect * %s[i];",
fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
s->codeAppend ("}");
}
GrCCPRTriangleCornerShader::WindHandling
GrCCPRTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
const char* position, const char* /*coverage*/,
const char* /*wind*/) {
varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
code->appendf("for (int i = 0; i < 2; ++i) {");
code->appendf( "%s[i] = %s * %s[i] + %s[i];",
fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
fAABoxTranslates.c_str());
code->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
code->appendf("}");
return WindHandling::kNotHandled;
}
void GrCCPRTriangleCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
const char* outputCoverage) const {
// By the time we reach this shader, the pixel is in the following state:
//
// 1. The hull shader has emitted a coverage of 1.
// 2. Both edges have subtracted the area on their outside.
//
// This generally works, but it is a problem for corner pixels. There is a region within
// corner pixels that is outside both edges at the same time. This means the region has been
// double subtracted (once by each edge). The purpose of this shader is to fix these corner
// pixels.
//
// More specifically, each edge redoes its coverage analysis so that it only subtracts the
// outside area that falls on its own side of the bisector line.
//
// NOTE: unless the edges fall on multiples of 90 deg from one another, they will have
// different AA boxes. (For an explanation of AA boxes, see comments in
// onEmitGeometryShader.) This means the coverage analysis will only be approximate. It
// seems acceptable, but if we want exact coverage we will need to switch to a more
// expensive model.
f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges.
f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
// Find the point at which the bisector exits the logical AA box.
// (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
": d.x / bisect.x;");
f->codeAppendf( "half2 exit = corner + bisect * T;");
// These lines combined (and the final multiply by .5) accomplish the following:
// 1. Add back the area beyond the corner that was subtracted out previously.
// 2. Subtract out the area beyond the corner, but under the bisector.
// The other edge will take care of the area on its own side of the bisector.
f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
f->codeAppendf("}");
f->codeAppendf("%s *= .5;", outputCoverage);
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCCPRTriangleShader_DEFINED
#define GrCCPRTriangleShader_DEFINED
#include "ccpr/GrCCPRCoverageProcessor.h"
/**
* This class renders the coverage of triangles. Triangles are rendered in three passes, as
* described below.
*/
class GrCCPRTriangleShader : public GrCCPRCoverageProcessor::Shader {
public:
int getNumInputPoints() const final { return 3; }
int getNumSegments() const final { return 3; } // 3 wedges, 3 edges, 3 corners.
void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
const TexelBufferHandle& pointsBuffer,
const char* pointId) const final;
void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
const char* outputWind) const final;
};
/**
* Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
* is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
* convex hull of those boxes.)
*/
class GrCCPRTriangleHullShader : public GrCCPRTriangleShader {
GeometryType getGeometryType() const override { return GeometryType::kHull; }
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, const char* outputCoverage) const override;
};
/**
* Pass 2: Smooth the edges that were over-rendered during Pass 1. Draw the conservative raster of
* each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
* coverage=-1 on the outside edge to coverage=0 on the inside edge.
*/
class GrCCPRTriangleEdgeShader : public GrCCPRTriangleShader {
GeometryType getGeometryType() const override { return GeometryType::kEdges; }
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
GrGLSLGeoToFrag fCoverageTimesWind{kHalf_GrSLType};
};
/**
* Pass 3: Touch up the corner pixels. Here we fix the simple distance-to-edge coverage analysis
* done previously so that it takes into account the region that is outside both edges at
* the same time.
*/
class GrCCPRTriangleCornerShader : public GrCCPRTriangleShader {
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
const char* bloat, const char* wind, const char* rtAdjust,
GeometryVars*) const override;
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) override;
void onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, const char* outputCoverage) const override;
GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2};
GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};
GrShaderVar fGeoShaderBisects{"bisects", kFloat2_GrSLType, 2};
GrGLSLGeoToFrag fCornerLocationInAABoxes{kFloat2x2_GrSLType};
GrGLSLGeoToFrag fBisectInAABoxes{kFloat2x2_GrSLType};
};
#endif