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