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