diff options
author | Chris Dalton <csmartdalton@google.com> | 2018-03-08 15:54:01 +0000 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-03-08 15:54:12 +0000 |
commit | fe462efbcb105914e4c4bbf689525b530a438773 (patch) | |
tree | b0cdff09f2988f61e7490cfb25621b4505c9119e /src/gpu/ccpr | |
parent | 4997e14c80f133ad43ce17e7b6adab55a37b9ada (diff) |
Revert "ccpr: Simplify triangle corners"
This reverts commit 622650a1949f9a68793ac895d9fbadee7177d860.
Reason for revert: Going to try to improve AAA quality on curve corners
Original change's description:
> ccpr: Simplify triangle corners
>
> Modifies triangle corner shaders to just approximate their coverage with
> linear values that ramp to zero at bloat vertices outside the triangle.
>
> For the vertex backend, since corners now have the same fragment shader
> as the rest of the triangle, we fold them in with the other steps and
> draw triangles in a single pass.
>
> The geometry backend still draws triangles in two passes, as there is
> not an apparent performance advantage in combining them.
>
> Bug: skia:
> Change-Id: Ib4a89d793a3c706f734d0271875c8a3e5c87c49b
> Reviewed-on: https://skia-review.googlesource.com/112632
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>
TBR=bsalomon@google.com,csmartdalton@google.com
Change-Id: I45e7b9d7d7f8452b28bd54ca1e90a1f046cb2462
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/113180
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Diffstat (limited to 'src/gpu/ccpr')
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 65 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 130 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 107 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 521 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.cpp | 18 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.h | 7 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.cpp | 12 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.cpp | 21 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.h | 8 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.cpp | 151 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.h | 49 |
11 files changed, 601 insertions, 488 deletions
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index 3a9eb7bfa7..76ca8f562e 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -7,21 +7,17 @@ #include "GrCCCoverageProcessor.h" -#include "GrGpuCommandBuffer.h" -#include "GrOpFlushState.h" #include "SkMakeUnique.h" #include "ccpr/GrCCCubicShader.h" #include "ccpr/GrCCQuadraticShader.h" +#include "ccpr/GrCCTriangleShader.h" #include "glsl/GrGLSLVertexGeoBuilder.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLVertexGeoBuilder.h" void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const { - int key = ((int)fRenderPass << 3); - if (GSTriangleSubpass::kCorners == fGSTriangleSubpass) { - key |= 4; - } + int key = (int)fRenderPass << 2; if (WindMethod::kInstanceData == fWindMethod) { key |= 2; } @@ -40,7 +36,10 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad std::unique_ptr<Shader> shader; switch (fRenderPass) { case RenderPass::kTriangles: - shader = skstd::make_unique<Shader>(); + shader = skstd::make_unique<GrCCTriangleShader>(); + break; + case RenderPass::kTriangleCorners: + shader = skstd::make_unique<GrCCTriangleCornerShader>(); break; case RenderPass::kQuadratics: shader = skstd::make_unique<GrCCQuadraticShader>(); @@ -53,45 +52,12 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad : this->createVSImpl(std::move(shader)); } -void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline, - const GrMesh meshes[], - const GrPipeline::DynamicState dynamicStates[], int meshCount, - const SkRect& drawBounds) const { - GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer(); - cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds); - - // Geometry shader backend draws triangles in two subpasses. - if (RenderPass::kTriangles == fRenderPass && Impl::kGeometryShader == fImpl) { - SkASSERT(GSTriangleSubpass::kHullsAndEdges == fGSTriangleSubpass); - GrCCCoverageProcessor cornerProc(*this, GSTriangleSubpass::kCorners); - cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds); - } -} - -void GrCCCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code, - const char* position, const char* coverage, - const char* wind) { - SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope); - code->appendf("half coverageTimesWind = %s * %s;", coverage, wind); - CoverageHandling coverageHandling = this->onEmitVaryings(varyingHandler, scope, code, position, - "coverageTimesWind"); - if (CoverageHandling::kNotHandled == coverageHandling) { - fCoverageTimesWind.reset(kHalf_GrSLType, scope); - varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); - code->appendf("%s = coverageTimesWind;", OutName(fCoverageTimesWind)); - } -} - void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc, GrGLSLFPFragmentBuilder* f, const char* skOutputColor, const char* skOutputCoverage) const { - f->codeAppendf("half coverage = +1;"); + f->codeAppendf("half coverage = 0;"); this->onEmitFragmentCode(proc, f, "coverage"); - if (fCoverageTimesWind.fsIn()) { - f->codeAppendf("coverage *= %s;", fCoverageTimesWind.fsIn()); - } f->codeAppendf("%s.a = coverage;", skOutputColor); f->codeAppendf("%s = half4(1);", skOutputCoverage); #ifdef SK_DEBUG @@ -136,20 +102,3 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGe // GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0. s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage); } - -void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder* s, - const char* leftPt, - const char* rightPt, - const char* bloatDir1, - const char* bloatDir2, - const char* outputCoverages) { - // See comments in CalcEdgeCoverageAtBloatVertex. - s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);", - rightPt, leftPt, leftPt, rightPt); - s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);"); - s->codeAppendf("float2 t = n * float2x2(%s, %s);", bloatDir1, bloatDir2); - s->codeAppendf("for (int i = 0; i < 2; ++i) {"); - s->codeAppendf( "%s[i] = (abs(t[i]) != nwidth ? t[i] / nwidth : sign(t[i])) * -.5 - .5;", - outputCoverages); - s->codeAppendf("}"); -} diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index a8ad18ce55..7db424e219 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -10,7 +10,6 @@ #include "GrCaps.h" #include "GrGeometryProcessor.h" -#include "GrPipeline.h" #include "GrShaderCaps.h" #include "SkNx.h" #include "glsl/GrGLSLGeometryProcessor.h" @@ -19,7 +18,6 @@ class GrGLSLFPFragmentBuilder; class GrGLSLVertexGeoBuilder; class GrMesh; -class GrOpFlushState; /** * This is the geometry processor for the simple convex primitive shapes (triangles and closed, @@ -56,9 +54,11 @@ public: void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w); }; // 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. + // mask. Triangles require two render passes: One to draw a rough outline, and a second pass to + // touch up the corners. This is an exhaustive list of all ccpr coverage shaders. enum class RenderPass { kTriangles, + kTriangleCorners, kQuadratics, kCubics, }; @@ -83,6 +83,18 @@ public: } } + // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array + // of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass, + // with coordinates in the desired shape's final atlas-space position. + void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, + SkTArray<GrMesh>* out) { + if (Impl::kGeometryShader == fImpl) { + this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out); + } else { + this->appendVSMesh(instanceBuffer, instanceCount, baseInstance, out); + } + } + // GrPrimitiveProcessor overrides. const char* name() const override { return RenderPassName(fRenderPass); } SkString dumpInfo() const override { @@ -99,39 +111,39 @@ public: float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; } #endif - // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array - // of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass, - // with coordinates in the desired shape's final atlas-space position. - void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, - SkTArray<GrMesh>* out) const { - if (Impl::kGeometryShader == fImpl) { - this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out); - } else { - this->appendVSMesh(instanceBuffer, instanceCount, baseInstance, out); - } - } - - void draw(GrOpFlushState*, const GrPipeline&, const GrMesh[], const GrPipeline::DynamicState[], - int meshCount, const SkRect& drawBounds) const; - - // The Shader provides code to calculate a pixel's coverage. + // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also + // provides details about shape-specific geometry. class Shader { public: - // Called before generating geometry. Subclasses may use this opportunity to setup internal - // member variables that will be needed during onEmitVaryings (e.g. transformation - // matrices). + union GeometryVars { + struct { + const char* fAlternatePoints; // floatNx2 (if left null, will use input points). + } 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). // - // Returns the name of a newly defined list of points around which the Impl should generate - // its geometry, or null if it should just use the input points. (Regardless, the size of - // whatever list of points indicated should match the size expected by the Impl: 3 points - // for triangles, and 4 for quadratics and cubics.) - virtual const char* emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts) const { - return nullptr; + // repetitionID is a 0-based index and indicates which edge or corner is being generated. + // It will be null when generating a hull. + virtual void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, + const char* repetitionID, const char* wind, + GeometryVars*) const {} + + void emitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, + SkString* code, const char* position, const char* inputCoverage, + const char* wind) { + SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope); + this->onEmitVaryings(varyingHandler, scope, code, position, inputCoverage, wind); } - void emitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* coverage, const char* wind); - void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* skOutputColor, const char* skOutputCoverage) const; @@ -145,37 +157,20 @@ public: const char* rightPt, const char* rasterVertexDir, const char* outputCoverage); - // Calculates an edge's coverage at two conservative raster vertices. - // (See CalcEdgeCoverageAtBloatVertex). - static void CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder*, const char* leftPt, - const char* rightPt, const char* bloatDir1, - const char* bloatDir2, - const char* outputCoverages); - virtual ~Shader() {} protected: - enum class CoverageHandling : bool { - kHandled, - kNotHandled - }; - // Here the subclass adds its internal varyings to the handler and produces code to - // initialize those varyings from a given position and coverage/wind. + // initialize those varyings from a given position, input coverage value, and wind. // - // Returns whether the subclass will handle coverage modulation or if this base class should - // take charge of multiplying the final coverage output by 'coverageTimesWind'. - virtual CoverageHandling onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, - SkString* code, const char* position, - const char* coverageTimesWind) { - return CoverageHandling::kNotHandled; - } + // NOTE: the coverage input is only relevant for triangles. Otherwise it is null. + virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, + const char* position, const char* inputCoverage, + const char* wind) = 0; - // Emits the fragment code that calculates a pixel's coverage value. If using - // CoverageHandling::kHandled, this value must be signed and modulated appropriately by - // coverage. + // Emits the fragment code that calculates a pixel's signed coverage value. virtual void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, - const char* outputCoverage) const {} + const char* outputCoverage) const = 0; // Returns the name of a Shader's internal varying at the point where where its value is // assigned. This is intended to work whether called for a vertex or a geometry shader. @@ -184,9 +179,6 @@ public: SkASSERT(Scope::kVertToGeo != varying.scope()); return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut(); } - - private: - GrGLSLVarying fCoverageTimesWind; }; class GSImpl; @@ -205,24 +197,6 @@ private: kVertexShader }; - // Geometry shader backend draws triangles in two subpasses. - enum class GSTriangleSubpass : bool { - kHullsAndEdges, - kCorners - }; - - GrCCCoverageProcessor(const GrCCCoverageProcessor& proc, GSTriangleSubpass subpass) - : INHERITED(kGrCCCoverageProcessor_ClassID) - , fRenderPass(RenderPass::kTriangles) - , fWindMethod(proc.fWindMethod) - , fImpl(Impl::kGeometryShader) - SkDEBUGCODE(, fDebugBloat(proc.fDebugBloat)) - , fGSTriangleSubpass(subpass) { - SkASSERT(RenderPass::kTriangles == proc.fRenderPass); - SkASSERT(Impl::kGeometryShader == proc.fImpl); - this->initGS(); - } - void initGS(); void initVS(GrResourceProvider*); @@ -239,9 +213,6 @@ private: const Impl fImpl; SkDEBUGCODE(float fDebugBloat = 0); - // Used by GSImpl. - const GSTriangleSubpass fGSTriangleSubpass = GSTriangleSubpass::kHullsAndEdges; - // Used by VSImpl. sk_sp<const GrBuffer> fVertexBuffer; sk_sp<const GrBuffer> fIndexBuffer; @@ -283,6 +254,7 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) { switch (pass) { case RenderPass::kTriangles: return "kTriangles"; + case RenderPass::kTriangleCorners: return "kTriangleCorners"; case RenderPass::kQuadratics: return "kQuadratics"; case RenderPass::kCubics: return "kCubics"; } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index 1fef1c3338..e64b8c0838 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -75,7 +75,10 @@ protected: 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(); + const char* coverage = nullptr; + if (RenderPass::kTriangleCorners != proc.fRenderPass) { + coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); + } g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { SkString fnBody; fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody, @@ -113,7 +116,7 @@ protected: * coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges * into smooth, antialiased ones. * - * The final corners get touched up in a later step by GSTriangleCornerImpl. + * The final corners get touched up in a later step by GSCornerImpl. */ class GSTriangleImpl : public GrCCCoverageProcessor::GSImpl { public: @@ -121,8 +124,6 @@ public: void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, const char* emitVertexFn) const override { - SkAssertResult(!fShader->emitSetupCode(g, "pts")); - // Visualize the input triangle as upright and equilateral, with a flat base. Paying special // attention to wind, we can identify the points as top, bottom-left, and bottom-right. // @@ -211,69 +212,19 @@ public: }; /** - * Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates - * coverage ramps that fix up the coverage values written by GSTriangleImpl. - */ -class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl { -public: - GSTriangleCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} - - void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, - const char* emitVertexFn) const override { - SkAssertResult(!fShader->emitSetupCode(g, "pts")); - - g->codeAppendf("float2 corner = pts[sk_InvocationID];"); - g->codeAppendf("float2 left = pts[(sk_InvocationID + (%s > 0 ? 2 : 1)) %% 3];", - wind.c_str()); - g->codeAppendf("float2 right = pts[(sk_InvocationID + (%s > 0 ? 1 : 2)) %% 3];", - wind.c_str()); - - // Find "outbloat" and "crossbloat" at our corner. The outbloat points diagonally out of the - // triangle, in the direction that should ramp to zero coverage. The crossbloat runs - // perpindicular to outbloat, and ramps from left-edge coverage to right-edge coverage. - g->codeAppend ("float2 leftdir = normalize(corner - left);"); - g->codeAppend ("float2 rightdir = normalize(right - corner);"); - g->codeAppend ("float2 outbloat = float2(leftdir.x > rightdir.x ? +1 : -1, " - "leftdir.y > rightdir.y ? +1 : -1);"); - g->codeAppend ("float2 crossbloat = float2(-outbloat.y, +outbloat.x);"); - - g->codeAppend ("half2 left_coverages; {"); - Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "outbloat", "crossbloat", - "left_coverages"); - g->codeAppend ("}"); - - g->codeAppend ("half2 right_coverages; {"); - Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner", "right", "outbloat", "-crossbloat", - "right_coverages"); - g->codeAppend ("}"); - - // Emit a corner box that erases whatever coverage was written previously, and replaces it - // using linearly-interpolated values that ramp to zero in bloat vertices that fall outside - // the triangle. - // - // NOTE: Since this is not a linear mapping, it is important that the box's diagonal shared - // edge points out of the triangle as much as possible. - g->codeAppendf("%s(corner - crossbloat * bloat, -right_coverages[1]);", emitVertexFn); - g->codeAppendf("%s(corner + outbloat * bloat, " - "-1 - left_coverages[0] - right_coverages[0]);", emitVertexFn); - g->codeAppendf("%s(corner - outbloat * bloat, 0);", emitVertexFn); - g->codeAppendf("%s(corner + crossbloat * bloat, -left_coverages[1]);", emitVertexFn); - - g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, 3); - } -}; - -/** * Generates a conservative raster hull around a convex quadrilateral that encloses a cubic or * quadratic, as well as its shared edge. */ -class GSCurveImpl : public GrCCCoverageProcessor::GSImpl { +class GSHull4Impl : public GrCCCoverageProcessor::GSImpl { public: - GSCurveImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} + GSHull4Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, const char* emitVertexFn) const override { - const char* hullPts = fShader->emitSetupCode(g, "pts"); + Shader::GeometryVars vars; + fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); + + const char* hullPts = vars.fHullVars.fAlternatePoints; if (!hullPts) { hullPts = "pts"; } @@ -363,6 +314,34 @@ public: } }; +/** + * Generates conservative rasters around corners. (See comments for RenderPass) + */ +class GSCornerImpl : public GrCCCoverageProcessor::GSImpl { +public: + GSCornerImpl(std::unique_ptr<Shader> shader, int numCorners) + : GSImpl(std::move(shader)), fNumCorners(numCorners) {} + + void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, + const char* emitVertexFn) const override { + Shader::GeometryVars vars; + fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars); + + const char* corner = vars.fCornerVars.fPoint; + SkASSERT(corner); + + g->codeAppendf("%s(%s + float2(-bloat, -bloat));", emitVertexFn, corner); + g->codeAppendf("%s(%s + float2(-bloat, +bloat));", emitVertexFn, corner); + g->codeAppendf("%s(%s + float2(+bloat, -bloat));", emitVertexFn, corner); + g->codeAppendf("%s(%s + float2(+bloat, +bloat));", emitVertexFn, corner); + + g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, fNumCorners); + } + +private: + const int fNumCorners; +}; + void GrCCCoverageProcessor::initGS() { SkASSERT(Impl::kGeometryShader == fImpl); if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) { @@ -393,12 +372,12 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const { switch (fRenderPass) { case RenderPass::kTriangles: - return (GSTriangleSubpass::kHullsAndEdges == fGSTriangleSubpass) - ? (GSImpl*) new GSTriangleImpl(std::move(shadr)) - : (GSImpl*) new GSTriangleCornerImpl(std::move(shadr)); + return new GSTriangleImpl(std::move(shadr)); + case RenderPass::kTriangleCorners: + return new GSCornerImpl(std::move(shadr), 3); case RenderPass::kQuadratics: case RenderPass::kCubics: - return new GSCurveImpl(std::move(shadr)); + return new GSHull4Impl(std::move(shadr)); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index c64ee755fe..08398e1900 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -10,15 +10,89 @@ #include "GrMesh.h" #include "glsl/GrGLSLVertexGeoBuilder.h" +using Shader = GrCCCoverageProcessor::Shader; + static constexpr int kAttribIdx_X = 0; static constexpr int kAttribIdx_Y = 1; static constexpr int kAttribIdx_VertexData = 2; -static constexpr int kVertexData_LeftNeighborIdShift = 10; -static constexpr int kVertexData_RightNeighborIdShift = 8; -static constexpr int kVertexData_BloatIdxShift = 6; -static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 5; -static constexpr int kVertexData_IsCornerBit = 1 << 4; +/** + * This class and its subclasses implement the coverage processor with vertex shaders. + */ +class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor { +protected: + VSImpl(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 GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>(); + + // Vertex shader. + GrGLSLVertexBuilder* v = args.fVertBuilder; + int numInputPoints = proc.numInputPoints(); + + const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz"; + v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));", + numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle, + proc.getAttrib(kAttribIdx_Y).fName, swizzle); + + if (WindMethod::kCrossProduct == proc.fWindMethod) { + v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], " + "pts[0] - pts[2]));"); + if (4 == numInputPoints) { + v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], " + "pts[0] - pts[3]));"); + } + v->codeAppend ("half wind = sign(area_x2);"); + } else { + SkASSERT(WindMethod::kInstanceData == proc.fWindMethod); + SkASSERT(3 == numInputPoints); + SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType); + v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName); + } + + float bloat = kAABloatRadius; +#ifdef SK_DEBUG + if (proc.debugVisualizationsEnabled()) { + bloat *= proc.debugBloat(); + } +#endif + v->defineConstant("bloat", bloat); + + const char* coverage = this->emitVertexPosition(proc, v, gpArgs); + SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType()); + + GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + SkString varyingCode; + fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, + gpArgs->fPositionVar.c_str(), coverage, "wind"); + v->codeAppend(varyingCode.c_str()); + + varyingHandler->emitAttributes(proc); + SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform()); + + // Fragment shader. + fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); + } + + virtual const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, + GrGPArgs*) const = 0; + + virtual ~VSImpl() {} + + const std::unique_ptr<Shader> fShader; + + typedef GrGLSLGeometryProcessor INHERITED; +}; + +static constexpr int kVertexData_LeftNeighborIdShift = 9; +static constexpr int kVertexData_RightNeighborIdShift = 7; +static constexpr int kVertexData_BloatIdxShift = 5; +static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 4; static constexpr int kVertexData_IsEdgeBit = 1 << 3; static constexpr int kVertexData_IsHullBit = 1 << 2; @@ -45,12 +119,8 @@ static constexpr int32_t edge_vertex_data(int32_t leftID, int rightID, int32_t b return pack_vertex_data(leftID, leftID, bloatIdx, rightID, kVertexData_IsEdgeBit | extraData); } -static constexpr int32_t triangle_corner_vertex_data(int32_t cornerID, int32_t bloatIdx) { - return pack_vertex_data((cornerID + 2) % 3, (cornerID + 1) % 3, bloatIdx, cornerID, - kVertexData_IsCornerBit); -} -static constexpr int32_t kTriangleVertices[] = { +static constexpr int32_t kHull3AndEdgeVertices[] = { hull_vertex_data(0, 0, 3), hull_vertex_data(0, 1, 3), hull_vertex_data(0, 2, 3), @@ -81,39 +151,21 @@ static constexpr int32_t kTriangleVertices[] = { edge_vertex_data(0, 2, 0, kVertexData_InvertNegativeCoverageBit), edge_vertex_data(0, 2, 1, kVertexData_InvertNegativeCoverageBit), edge_vertex_data(0, 2, 2, kVertexData_InvertNegativeCoverageBit), - - triangle_corner_vertex_data(0, 0), - triangle_corner_vertex_data(0, 1), - triangle_corner_vertex_data(0, 2), - triangle_corner_vertex_data(0, 3), - - triangle_corner_vertex_data(1, 0), - triangle_corner_vertex_data(1, 1), - triangle_corner_vertex_data(1, 2), - triangle_corner_vertex_data(1, 3), - - triangle_corner_vertex_data(2, 0), - triangle_corner_vertex_data(2, 1), - triangle_corner_vertex_data(2, 2), - triangle_corner_vertex_data(2, 3), }; -GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); static constexpr uint16_t kRestartStrip = 0xffff; -static constexpr uint16_t kTriangleIndicesAsStrips[] = { +static constexpr uint16_t kHull3AndEdgeIndicesAsStrips[] = { 1, 2, 0, 3, 8, kRestartStrip, // First corner and main body of the hull. 4, 5, 3, 6, 8, 7, kRestartStrip, // Opposite side and corners of the hull. 10, 9, 11, 14, 12, 13, kRestartStrip, // First edge. 16, 15, 17, 20, 18, 19, kRestartStrip, // Second edge. - 22, 21, 23, 26, 24, 25, kRestartStrip, // Third edge. - 27, 28, 30, 29, kRestartStrip, // First corner. - 31, 32, 34, 33, kRestartStrip, // Second corner. - 35, 36, 38, 37 // Third corner. + 22, 21, 23, 26, 24, 25 // Third edge. }; -static constexpr uint16_t kTriangleIndicesAsTris[] = { +static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = { // First corner and main body of the hull. 1, 2, 0, 2, 3, 0, @@ -142,21 +194,9 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = { 21, 26, 23, 23, 26, 24, 26, 25, 24, - - // First corner. - 27, 28, 30, - 28, 29, 30, - - // Second corner. - 31, 32, 34, - 32, 33, 34, - - // Third corner. - 35, 36, 38, - 36, 37, 38, }; -GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); static constexpr int32_t kHull4AndEdgeVertices[] = { hull_vertex_data(0, 0, 4), @@ -212,229 +252,162 @@ static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = { GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey); - /** - * This class and its subclasses implement the coverage processor with vertex shaders. + * Generates a conservative raster hull around a triangle or curve. For triangles we generate + * additional conservative rasters with coverage ramps around the edges. For curves we + * generate an additional raster with coverage ramps around its shared edge. + * + * Triangle rough outlines are drawn in two steps: (1) Draw a conservative raster of the entire + * triangle, with a coverage of +1. (2) Draw conservative rasters around each edge, with a + * coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges + * into smooth, antialiased ones. The final corners get touched up in a later step by VSCornerImpl. + * + * Curves are drawn in two steps: (1) Draw a conservative raster around the input points, passing + * coverage=+1 to the Shader. (2) Draw an additional conservative raster around the curve's shared + * edge, using coverage=-1 at bloat vertices that fall outside the input points. This erases what + * the hull just wrote and ramps coverage to zero. */ -class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor { +class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl { public: - VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {} - -private: - void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, - FPCoordTransformIter&& transformIter) override { - this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); - } - - void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override; + VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides) + : VSImpl(std::move(shader)), fNumSides(numSides) {} - const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, - GrGPArgs*) const; + const char* emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v, + GrGPArgs* gpArgs) const override { + Shader::GeometryVars vars; + fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars); - const std::unique_ptr<Shader> fShader; -}; + const char* hullPts = vars.fHullVars.fAlternatePoints; + if (!hullPts) { + hullPts = "pts"; + } -void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { - const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>(); + // Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0]. + v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;", + proc.getAttrib(kAttribIdx_VertexData).fName, + ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) | + ((fNumSides - 1) << kVertexData_RightNeighborIdShift) | + (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) | + (fNumSides - 1), + proc.getAttrib(kAttribIdx_VertexData).fName); + + // Here we generate conservative raster geometry for the input polygon. It is the convex + // hull of N pixel-size boxes, one centered on each the input points. Each corner has three + // vertices, where one or two may cause degenerate triangles. The vertex data tells us how + // to offset each vertex. Edges are also handled here using the same concept. For more + // details on conservative raster, see: + // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html + v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); + v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", + hullPts, kVertexData_LeftNeighborIdShift); + v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];", + hullPts, kVertexData_RightNeighborIdShift); + + v->codeAppend ("float2 leftbloat = sign(corner - left);"); + v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " + "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);"); + + v->codeAppend ("float2 rightbloat = sign(right - corner);"); + v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " + "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);"); + + v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);"); + + // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices. We begin + // with the first hull vertex (leftbloat), then continue rotating 90 degrees clockwise until + // we reach the desired vertex for this invocation. Corners with less than 3 corresponding + // hull vertices will result in redundant vertices and degenerate triangles. + v->codeAppend ("float2 bloatdir = leftbloat;"); + v->codeAppendf("int bloatidx = (%s >> %i) & 3;", + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift); + v->codeAppend ("switch (bloatidx) {"); + v->codeAppend ( "case 2:"); + v->codeAppendf( "if (all(left_right_notequal)) {"); + v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); + v->codeAppend ( "}"); + // fallthru. + v->codeAppend ( "case 1:"); + v->codeAppendf( "if (any(left_right_notequal)) {"); + v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); + v->codeAppend ( "}"); + // fallthru. + v->codeAppend ("}"); - // Vertex shader. - GrGLSLVertexBuilder* v = args.fVertBuilder; - int numInputPoints = proc.numInputPoints(); + v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); + gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); + + // The hull has a coverage of +1 all around. + v->codeAppend ("half coverage = +1;"); + + if (3 == fNumSides) { + v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); + Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage"); + v->codeAppend ("}"); + } else { + SkASSERT(4 == fNumSides); + v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); + v->codeAppend ( "coverage = -1;"); + v->codeAppend ("}"); + } - const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz"; - v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));", - numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle, - proc.getAttrib(kAttribIdx_Y).fName, swizzle); + v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? + proc.getAttrib(kAttribIdx_VertexData).fName, + kVertexData_InvertNegativeCoverageBit); + v->codeAppend ( "coverage = -1 - coverage;"); + v->codeAppend ("}"); - if (WindMethod::kCrossProduct == proc.fWindMethod) { - v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], " - "pts[0] - pts[2]));"); - if (4 == numInputPoints) { - v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], " - "pts[0] - pts[3]));"); - } - v->codeAppend ("half wind = sign(area_x2);"); - } else { - SkASSERT(WindMethod::kInstanceData == proc.fWindMethod); - SkASSERT(3 == numInputPoints); - SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType); - v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName); + return "coverage"; } - float bloat = kAABloatRadius; -#ifdef SK_DEBUG - if (proc.debugVisualizationsEnabled()) { - bloat *= proc.debugBloat(); - } -#endif - v->defineConstant("bloat", bloat); +private: + const int fNumSides; +}; - const char* coverage = this->emitVertexPosition(proc, v, gpArgs); - SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType()); +static constexpr uint16_t kCornerIndicesAsStrips[] = { + 0, 1, 2, 3, kRestartStrip, // First corner. + 4, 5, 6, 7, kRestartStrip, // Second corner. + 8, 9, 10, 11 // Third corner. +}; - GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; - SkString varyingCode; - fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, - gpArgs->fPositionVar.c_str(), coverage, "wind"); - v->codeAppend(varyingCode.c_str()); +static constexpr uint16_t kCornerIndicesAsTris[] = { + // First corner. + 0, 1, 2, + 1, 3, 2, - varyingHandler->emitAttributes(proc); - SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform()); + // Second corner. + 4, 5, 6, + 5, 7, 6, - // Fragment shader. - fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); -} + // Third corner. + 8, 9, 10, + 9, 11, 10, +}; + +GR_DECLARE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); /** - * Generates a conservative raster hull around a triangle or curve. For triangles we generate - * additional conservative rasters with coverage ramps around the edges. For curves we generate an - * additional raster with coverage ramps around its shared edge. - * - * Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a - * coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to - * 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased - * ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the - * previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the - * triangle. - * - * Curves are drawn in two steps: (1) Draw a conservative raster around the input points, passing - * coverage=+1 to the Shader. (2) Draw an additional conservative raster around the curve's shared - * edge, using coverage=-1 at bloat vertices that fall outside the input points. This erases what - * the hull just wrote and ramps coverage to zero. + * Generates conservative rasters around corners. (See comments for RenderPass) */ -const char* GrCCCoverageProcessor::VSImpl::emitVertexPosition(const GrCCCoverageProcessor& proc, - GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) const { - int numSides = (RenderPass::kTriangles == proc.fRenderPass) ? 3 : 4; - const char* hullPts = fShader->emitSetupCode(v, "pts"); - if (!hullPts) { - SkASSERT(numSides == proc.numInputPoints()); - hullPts = "pts"; - } +class VSCornerImpl : public GrCCCoverageProcessor::VSImpl { +public: + VSCornerImpl(std::unique_ptr<Shader> shader) : VSImpl(std::move(shader)) {} - // Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0]. - v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;", - proc.getAttrib(kAttribIdx_VertexData).fName, - ((numSides - 1) << kVertexData_LeftNeighborIdShift) | - ((numSides - 1) << kVertexData_RightNeighborIdShift) | - (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) | - (numSides - 1), - proc.getAttrib(kAttribIdx_VertexData).fName); - - // Here we generate conservative raster geometry for the input polygon. It is the convex hull of - // N pixel-size boxes, one centered on each the input points. Each corner has three vertices, - // where one or two may cause degenerate triangles. The vertex data tells us how to offset each - // vertex. Edges are also handled here using the same concept. For more details on conservative - // raster, see: https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html - v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); - v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", - hullPts, kVertexData_LeftNeighborIdShift); - v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];", - hullPts, kVertexData_RightNeighborIdShift); - - v->codeAppend ("float2 leftbloat = sign(corner - left);"); - v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " - "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);"); - - v->codeAppend ("float2 rightbloat = sign(right - corner);"); - v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " - "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);"); - - v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);"); - - v->codeAppend ("float2 bloatdir = leftbloat;"); - - if (RenderPass::kTriangles == proc.fRenderPass) { // Only triangles emit corner boxes. - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - - // For corner boxes, we hack 'left_right_notequal' to [true, true]. This - // causes the upcoming code to always rotate, which is the right thing - // for corners. - v->codeAppendf( "left_right_notequal = bool2(true, true);"); - - // In corner boxes, all 4 coverage values will not map linearly, so it is - // important to rotate the box so its diagonal shared edge points out of - // the triangle, in the direction that ramps to zero. - v->codeAppend ( "float2 bisect = normalize(corner - right) + normalize(corner - left);"); - v->codeAppend ( "if (sign(bisect) == sign(leftbloat)) {"); - v->codeAppend ( "bloatdir = float2(+bloatdir.y, -bloatdir.x);"); - v->codeAppend ( "}"); - v->codeAppend ("}"); - } + const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v, + GrGPArgs* gpArgs) const override { + Shader::GeometryVars vars; + v->codeAppend ("int corner_id = sk_VertexID / 4;"); + fShader->emitSetupCode(v, "pts", "corner_id", "wind", &vars); - // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if it's a - // corner box). We begin with the first hull vertex (leftbloat), then continue rotating 90 - // degrees clockwise until we reach the desired vertex for this invocation. Corners with less - // than 3 corresponding hull vertices will result in redundant vertices and degenerate - // triangles. - v->codeAppendf("int bloatidx = (%s >> %i) & 3;", - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift); - v->codeAppend ("switch (bloatidx) {"); - if (RenderPass::kTriangles == proc.fRenderPass) { // Only triangles emit corner boxes. - v->codeAppend ( "case 3:"); - // Only corners will have bloatidx=3, and corners always rotate. - v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. - // fallthru. - } - v->codeAppend ( "case 2:"); - v->codeAppendf( "if (all(left_right_notequal)) {"); - v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. - v->codeAppend ( "}"); - // fallthru. - v->codeAppend ( "case 1:"); - v->codeAppendf( "if (any(left_right_notequal)) {"); - v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. - v->codeAppend ( "}"); - // fallthru. - v->codeAppend ("}"); - - v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); - gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - - // The hull has a coverage of +1 all around. - v->codeAppend ("half coverage = +1;"); - - if (RenderPass::kTriangles == proc.fRenderPass) { - v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge OR corner? - proc.getAttrib(kAttribIdx_VertexData).fName, - kVertexData_IsEdgeBit | kVertexData_IsCornerBit); - Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage"); - v->codeAppend ("}"); + v->codeAppendf("float2 vertex = %s;", vars.fCornerVars.fPoint); + v->codeAppend ("vertex.x += (0 == (sk_VertexID & 2)) ? -bloat : +bloat;"); + v->codeAppend ("vertex.y += (0 == (sk_VertexID & 1)) ? -bloat : +bloat;"); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - // Corner boxes erase whatever coverage was written previously, and - // replace it with linearly-interpolated values that ramp to zero in the - // diagonal that points out of the triangle, and ramp from left-edge - // coverage to right-edge coverage in the other diagonal. - v->codeAppend ( "half left_coverage = coverage;"); - v->codeAppend ( "half right_coverage;"); - Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", - "right_coverage"); - v->codeAppend ( "coverage = (1 == bloatidx) ? -1 : 0;"); - v->codeAppend ( "if (((bloatidx + 3) & 3) < 2) {"); - v->codeAppend ( "coverage -= left_coverage;"); - v->codeAppend ( "}"); - v->codeAppend ( "if (bloatidx < 2) {"); - v->codeAppend ( "coverage -= right_coverage;"); - v->codeAppend ( "}"); - v->codeAppend ("}"); - } else { - v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); - v->codeAppend ( "coverage = -1;"); - v->codeAppend ("}"); + gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); + return nullptr; // Corner vertices don't have an initial coverage value. } - - v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? - proc.getAttrib(kAttribIdx_VertexData).fName, - kVertexData_InvertNegativeCoverageBit); - v->codeAppend ( "coverage = -1 - coverage;"); - v->codeAppend ("}"); - - return "coverage"; -} +}; void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { SkASSERT(Impl::kVertexShader == fImpl); @@ -442,24 +415,42 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { switch (fRenderPass) { case RenderPass::kTriangles: { - GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, - sizeof(kTriangleVertices), - kTriangleVertices, - gTriangleVertexBufferKey); - GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); + sizeof(kHull3AndEdgeVertices), + kHull3AndEdgeVertices, + gHull3AndEdgeVertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); + if (caps.usePrimitiveRestart()) { + fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kHull3AndEdgeIndicesAsStrips), + kHull3AndEdgeIndicesAsStrips, + gHull3AndEdgeIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsStrips); + } else { + fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kHull3AndEdgeIndicesAsTris), + kHull3AndEdgeIndicesAsTris, + gHull3AndEdgeIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris); + } + break; + } + + case RenderPass::kTriangleCorners: { + GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); if (caps.usePrimitiveRestart()) { fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kTriangleIndicesAsStrips), - kTriangleIndicesAsStrips, - gTriangleIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips); + sizeof(kCornerIndicesAsStrips), + kCornerIndicesAsStrips, + gCornerIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsStrips); } else { fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kTriangleIndicesAsTris), - kTriangleIndicesAsTris, - gTriangleIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris); + sizeof(kCornerIndicesAsTris), + kCornerIndicesAsTris, + gCornerIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris); } break; } @@ -540,5 +531,15 @@ void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceC } GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const { - return new VSImpl(std::move(shadr)); + switch (fRenderPass) { + case RenderPass::kTriangles: + return new VSHullAndEdgeImpl(std::move(shadr), 3); + case RenderPass::kTriangleCorners: + return new VSCornerImpl(std::move(shadr)); + case RenderPass::kQuadratics: + case RenderPass::kCubics: + return new VSHullAndEdgeImpl(std::move(shadr), 4); + } + SK_ABORT("Invalid RenderPass"); + return nullptr; } diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp index 7066fbdaed..76d1646b65 100644 --- a/src/gpu/ccpr/GrCCCubicShader.cpp +++ b/src/gpu/ccpr/GrCCCubicShader.cpp @@ -12,7 +12,9 @@ using Shader = GrCCCoverageProcessor::Shader; -const char* GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts) const { +void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, + const char* /*repetitionID*/, const char* /*wind*/, + GeometryVars*) const { // Find the cubic's power basis coefficients. s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, " " 3, -6, 3, 0, " @@ -62,28 +64,24 @@ const char* GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, " "0, orientation[0], 0, " "0, 0, orientation[1]);", fKLMMatrix.c_str()); - - return nullptr; } -Shader::CoverageHandling GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code, - const char* position, - const char* coverageTimesWind) { +void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code, + const char* position, const char* inputCoverage, + const char* wind) { code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str()); fKLMW.reset(kFloat4_GrSLType, scope); varyingHandler->addVarying("klmw", &fKLMW); code->appendf("%s.xyz = klm;", OutName(fKLMW)); - code->appendf("%s.w = %s;", OutName(fKLMW), coverageTimesWind); + code->appendf("%s.w = %s * %s;", OutName(fKLMW), inputCoverage, wind); fGradMatrix.reset(kFloat2x2_GrSLType, scope); varyingHandler->addVarying("grad_matrix", &fGradMatrix); code->appendf("%s[0] = 3 * klm[0] * %s[0].xy;", OutName(fGradMatrix), fKLMMatrix.c_str()); code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;", OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str()); - - return CoverageHandling::kHandled; } void GrCCCubicShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h index 3ffce65042..70d3300461 100644 --- a/src/gpu/ccpr/GrCCCubicShader.h +++ b/src/gpu/ccpr/GrCCCubicShader.h @@ -23,10 +23,11 @@ */ class GrCCCubicShader : public GrCCCoverageProcessor::Shader { protected: - const char* emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts) const override; + void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + const char* wind, GeometryVars*) const override; - CoverageHandling onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* coverageTimesWind) override; + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, + const char* position, const char* inputCoverage, const char* wind) override; void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index 42f1010409..e625c43743 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -514,12 +514,17 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount if (batchTotalCounts.fTriangles) { this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles, WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, + WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); } if (batchTotalCounts.fWoundTriangles) { this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles, WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, drawBounds); + this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, + WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, + drawBounds); } if (batchTotalCounts.fQuadratics) { @@ -587,8 +592,9 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType); if (!fMeshesScratchBuffer.empty()) { - proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(), - fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(), - SkRect::Make(drawBounds)); + SkASSERT(flushState->rtCommandBuffer()); + flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(), + fDynamicStatesScratchBuffer.begin(), + fMeshesScratchBuffer.count(), SkRect::Make(drawBounds)); } } diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp index e164cff862..baa10fd34e 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.cpp +++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp @@ -13,7 +13,9 @@ using Shader = GrCCCoverageProcessor::Shader; -const char* GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts) const { +void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, + const char* /*repetitionID*/, const char* /*wind*/, + GeometryVars* vars) const { s->declareGlobal(fCanonicalMatrix); s->codeAppendf("%s = float3x3(0.0, 0, 1, " "0.5, 0, 1, " @@ -36,20 +38,23 @@ const char* GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const "%s[0] + tan0 * t, " "%s[1] + tan1 * t, " "%s[2]);", pts, pts, pts, pts); - return "quadratic_hull"; + vars->fHullVars.fAlternatePoints = "quadratic_hull"; } -Shader::CoverageHandling GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, - SkString* code, const char* position, - const char* coverageTimesWind) { +void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code, + const char* position, const char* inputCoverage, + const char* wind) { fCoords.reset(kFloat4_GrSLType, scope); varyingHandler->addVarying("coords", &fCoords); code->appendf("%s.xy = (%s * float3(%s, 1)).xy;", OutName(fCoords), fCanonicalMatrix.c_str(), position); code->appendf("%s.zw = float2(2 * %s.x, -1) * float2x2(%s);", OutName(fCoords), OutName(fCoords), fCanonicalMatrix.c_str()); - return CoverageHandling::kNotHandled; + + fCoverageTimesWind.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); + code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); } void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, @@ -62,5 +67,5 @@ void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, f->codeAppendf("d /= %f;", proc.debugBloat()); } #endif - f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage); + f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s;", outputCoverage, fCoverageTimesWind.fsIn()); } diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h index a2ac5dab9f..d91f943471 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.h +++ b/src/gpu/ccpr/GrCCQuadraticShader.h @@ -22,16 +22,18 @@ */ class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader { protected: - const char* emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts) const override; + void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + const char* wind, GeometryVars*) const override; - CoverageHandling onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* coverageTimesWind) override; + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, + const char* position, const char* inputCoverage, const char* wind) override; void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType}; GrGLSLVarying fCoords; + GrGLSLVarying fCoverageTimesWind; }; #endif diff --git a/src/gpu/ccpr/GrCCTriangleShader.cpp b/src/gpu/ccpr/GrCCTriangleShader.cpp new file mode 100644 index 0000000000..8135313965 --- /dev/null +++ b/src/gpu/ccpr/GrCCTriangleShader.cpp @@ -0,0 +1,151 @@ +/* + * 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 "GrCCTriangleShader.h" + +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLVertexGeoBuilder.h" + +using Shader = GrCCCoverageProcessor::Shader; + +void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code, + const char* /*position*/, const char* inputCoverage, + const char* wind) { + SkASSERT(inputCoverage); + fCoverageTimesWind.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); + code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); +} + +void GrCCTriangleShader::onEmitFragmentCode(const GrCCCoverageProcessor&, + GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); +} + +void GrCCTriangleCornerShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, + const char* repetitionID, const char* wind, + GeometryVars* vars) const { + s->codeAppendf("float2 corner = %s[%s];", pts, repetitionID); + vars->fCornerVars.fPoint = "corner"; + + s->codeAppendf("float2x2 vectors = float2x2(corner - %s[0 != %s ? %s - 1 : 2], " + "corner - %s[2 != %s ? %s + 1 : 0]);", + pts, repetitionID, repetitionID, pts, repetitionID, + repetitionID); + + // 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 = (abs(n.x) + abs(n.y)) * (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). + s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);"); + 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 ("}"); +} + +void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, + GrGLSLVarying::Scope scope, SkString* code, + const char* position, const char* inputCoverage, + const char* wind) { + using Interpolation = GrGLSLVaryingHandler::Interpolation; + SkASSERT(!inputCoverage); + + fCornerLocationInAABoxes.reset(kFloat2x2_GrSLType, scope); + varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes); + + fBisectInAABoxes.reset(kFloat2x2_GrSLType, scope); + varyingHandler->addVarying("bisect_in_aa_boxes", &fBisectInAABoxes, Interpolation::kCanBeFlat); + + code->appendf("for (int i = 0; i < 2; ++i) {"); + code->appendf( "%s[i] = %s * %s[i] + %s[i];", + OutName(fCornerLocationInAABoxes), position, fAABoxMatrices.c_str(), + fAABoxTranslates.c_str()); + code->appendf( "%s[i] = %s[i];", OutName(fBisectInAABoxes), fGeoShaderBisects.c_str()); + code->appendf("}"); + + fWindTimesHalf.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("wind_times_half", &fWindTimesHalf, Interpolation::kCanBeFlat); + code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind); +} + +void GrCCTriangleCornerShader::onEmitFragmentCode(const GrCCCoverageProcessor&, + GrGLSLFPFragmentBuilder* 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 *= %s;", outputCoverage, fWindTimesHalf.fsIn()); +} diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h new file mode 100644 index 0000000000..6dae8df497 --- /dev/null +++ b/src/gpu/ccpr/GrCCTriangleShader.h @@ -0,0 +1,49 @@ +/* + * 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 GrCCTriangleShader_DEFINED +#define GrCCTriangleShader_DEFINED + +#include "ccpr/GrCCCoverageProcessor.h" + +/** + * Steps 1 & 2: Draw the triangle's conservative raster hull with a coverage of +1, then smooth the + * edges by drawing the conservative rasters of all 3 edges and interpolating from + * coverage=-1 on the outside to coverage=0 on the inside. The Impl may choose to + * implement these steps in either one or two actual render passes. + */ +class GrCCTriangleShader : public GrCCCoverageProcessor::Shader { + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, + const char* position, const char* inputCoverage, const char* wind) override; + void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, + const char* outputCoverage) const override; + + GrGLSLVarying fCoverageTimesWind; +}; + +/** + * Step 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 GrCCTriangleCornerShader : public GrCCCoverageProcessor::Shader { + void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, + const char* wind, GeometryVars*) const override; + void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, + const char* position, const char* inputCoverage, const char* wind) override; + void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, + 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}; + GrGLSLVarying fCornerLocationInAABoxes; + GrGLSLVarying fBisectInAABoxes; + GrGLSLVarying fWindTimesHalf; +}; + +#endif |