diff options
author | Chris Dalton <csmartdalton@google.com> | 2018-03-21 17:20:21 -0600 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-03-26 20:25:44 +0000 |
commit | 21ba551f13b2cd15c0cfc2743c6ea3b83976f1b6 (patch) | |
tree | 531c5dc7e166879aaf421e41c1d91c6143a14b58 /src | |
parent | 4ccf49c1bf312558f57d9b0339529c0cc6b235a1 (diff) |
ccpr: Replace curve corner MSAA with analytic attenuation
Begins using the new triangle corner algorithm on curves as well.
Updates the vertex backend to render curves in a single pass.
Simplifies the cubic and quadratic shaders. Removes all code related to
sample locations.
Bug: skia:
Change-Id: I96c6c401be765e96a8fe087deb7f84760e68dcf0
Reviewed-on: https://skia-review.googlesource.com/115746
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 28 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 62 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 165 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 273 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.cpp | 120 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.h | 38 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.cpp | 146 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.h | 47 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.h | 25 |
9 files changed, 326 insertions, 578 deletions
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index 10b6736f33..8e7249e9e4 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -127,26 +127,6 @@ void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGe outputAttenuation); } -int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* 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](" - "float2(+1, -3)/16, float2(-1, +3)/16, float2(+5, +1)/16, float2(-3, -5)/16, " - "float2(-5, +5)/16, float2(-7, -1)/16, float2(+3, +7)/16, float2(+7, -7)/16." - ")"); - return 8; -#else - f->defineConstant("float2[16]", samplesName, "float2[16](" - "float2(+1, +1)/16, float2(-1, -3)/16, float2(-3, +2)/16, float2(+4, -1)/16, " - "float2(-5, -2)/16, float2(+2, +5)/16, float2(+5, +3)/16, float2(+3, -5)/16, " - "float2(-2, +6)/16, float2( 0, -7)/16, float2(-4, -6)/16, float2(-6, +4)/16, " - "float2(-8, 0)/16, float2(+7, -4)/16, float2(+6, +7)/16, float2(-7, -8)/16." - ")"); - return 16; -#endif -} - void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const { int key = (int)fRenderPass << 2; @@ -172,16 +152,12 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad shader = skstd::make_unique<GrCCTriangleShader>(); break; case RenderPass::kQuadratics: - shader = skstd::make_unique<GrCCQuadraticHullShader>(); - break; case RenderPass::kQuadraticCorners: - shader = skstd::make_unique<GrCCQuadraticCornerShader>(); + shader = skstd::make_unique<GrCCQuadraticShader>(); break; case RenderPass::kCubics: - shader = skstd::make_unique<GrCCCubicHullShader>(); - break; case RenderPass::kCubicCorners: - shader = skstd::make_unique<GrCCCubicCornerShader>(); + shader = skstd::make_unique<GrCCCubicShader>(); break; } return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader)) diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index d826b810c9..8b2b00392b 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -70,9 +70,19 @@ public: static bool RenderPassIsCubic(RenderPass); static const char* RenderPassName(RenderPass); - constexpr static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) { - return RenderPass::kTriangleCorners != renderPass || - caps.shaderCaps()->geometryShaderSupport(); + static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) { + switch (renderPass) { + case RenderPass::kTriangles: + case RenderPass::kQuadratics: + case RenderPass::kCubics: + return true; + case RenderPass::kTriangleCorners: + case RenderPass::kQuadraticCorners: + case RenderPass::kCubicCorners: + return caps.shaderCaps()->geometryShaderSupport(); + } + SK_ABORT("Invalid RenderPass"); + return false; } enum class WindMethod : bool { @@ -126,34 +136,20 @@ public: // provides details about shape-specific geometry. class Shader { public: - 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). + // Called before generating geometry. Subclasses may set up internal member variables during + // this time that will be needed during onEmitVaryings (e.g. transformation matrices). // - // 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 {} + // If the optional 'tighterHull' parameter is not null and gets filled out by the subclass, + // the the Impl will generate geometry around those points rather than the input points. + virtual void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* wind, + const char** tighterHull = nullptr) const {} void emitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, const char* position, const char* coverage, - const char* attenuatedCoverage, const char* wind) { + const char* attenuatedCoverage) { SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope); this->onEmitVaryings(varyingHandler, scope, code, position, coverage, - attenuatedCoverage, wind); + attenuatedCoverage); } void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, @@ -195,12 +191,13 @@ public: protected: // Here the subclass adds its internal varyings to the handler and produces code to - // initialize those varyings from a given position, input coverage value, and wind. + // initialize those varyings from a given position and coverage values. // - // NOTE: the coverage inputs are only relevant for triangles. Otherwise they are null. + // NOTE: the coverage values are signed appropriately for wind. + // 'coverage' will only be +1 or -1 on curves. virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position, const char* coverage, - const char* attenuatedCoverage, const char* wind) = 0; + const char* attenuatedCoverage) = 0; // Emits the fragment code that calculates a pixel's signed coverage value. virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, @@ -213,15 +210,12 @@ public: SkASSERT(Scope::kVertToGeo != varying.scope()); return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut(); } - - // 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(GrGLSLFPFragmentBuilder* f, const char* samplesName); }; class GSImpl; + class GSTriangleHullImpl; + class GSCurveHullImpl; + class GSCornerImpl; class VSImpl; private: diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index 82a292cb06..907de46019 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -12,7 +12,6 @@ using InputType = GrGLSLGeometryBuilder::InputType; using OutputType = GrGLSLGeometryBuilder::OutputType; -using Shader = GrCCCoverageProcessor::Shader; /** * This class and its subclasses implement the coverage processor with geometry shaders. @@ -21,6 +20,9 @@ class GrCCCoverageProcessor::GSImpl : public GrGLSLGeometryProcessor { protected: GSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {} + virtual bool hasCoverage() const { return false; } + virtual bool hasAttenuatedCoverage() const { return false; } + void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, FPCoordTransformIter&& transformIter) final { this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); @@ -76,19 +78,24 @@ protected: SkSTArray<2, GrShaderVar> emitArgs; const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str(); const char* coverage = nullptr; - if (RenderPass::kTriangles == proc.fRenderPass || - RenderPass::kTriangleCorners == proc.fRenderPass) { + if (this->hasCoverage()) { coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); } const char* attenuatedCoverage = nullptr; - if (RenderPass::kTriangleCorners == proc.fRenderPass) { + if (this->hasAttenuatedCoverage()) { attenuatedCoverage = emitArgs.emplace_back("attenuated_coverage", kHalf2_GrSLType).c_str(); } g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { SkString fnBody; + if (coverage) { + fnBody.appendf("%s *= %s;", coverage, wind.c_str()); + } + if (attenuatedCoverage) { + fnBody.appendf("%s.x *= %s;", attenuatedCoverage, wind.c_str()); + } fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody, - position, coverage, attenuatedCoverage, wind.c_str()); + position, coverage ? coverage : wind.c_str(), attenuatedCoverage); g->emitVertex(&fnBody, position, rtAdjust); return fnBody; }().c_str(), &emitVertexFn); @@ -101,11 +108,11 @@ protected: #endif g->defineConstant("bloat", bloat); - this->onEmitGeometryShader(g, wind, emitVertexFn.c_str()); + this->onEmitGeometryShader(proc, g, wind, emitVertexFn.c_str()); } - virtual void onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind, - const char* emitVertexFn) const = 0; + virtual void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder*, + const GrShaderVar& wind, const char* emitVertexFn) const = 0; virtual ~GSImpl() {} @@ -124,15 +131,15 @@ protected: * * The final corners get touched up in a later step by GSTriangleCornerImpl. */ -class GSTriangleImpl : public GrCCCoverageProcessor::GSImpl { +class GrCCCoverageProcessor::GSTriangleHullImpl : public GrCCCoverageProcessor::GSImpl { public: - GSTriangleImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} + GSTriangleHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} + + bool hasCoverage() const override { return true; } - void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, - const char* emitVertexFn) const override { - Shader::GeometryVars vars; - fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); - SkASSERT(!vars.fHullVars.fAlternatePoints); + void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g, + const GrShaderVar& wind, const char* emitVertexFn) const override { + fShader->emitSetupCode(g, "pts", wind.c_str()); // 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. @@ -222,24 +229,31 @@ public: }; /** - * Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates - * coverage and attenuation ramps to fix up the coverage values written by GSTriangleImpl. + * Generates conservative rasters around corners (aka pixel-size boxes) and calculates + * coverage and attenuation ramps to fix up the coverage values written by the hulls. */ -class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl { +class GrCCCoverageProcessor::GSCornerImpl : public GrCCCoverageProcessor::GSImpl { public: - GSTriangleCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} + GSCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} - void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, - const char* emitVertexFn) const override { - Shader::GeometryVars vars; - fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); - SkASSERT(!vars.fHullVars.fAlternatePoints); + bool hasCoverage() const override { return true; } + bool hasAttenuatedCoverage() const override { return true; } - 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()); + void onEmitGeometryShader(const GrCCCoverageProcessor& proc, GrGLSLGeometryBuilder* g, + const GrShaderVar& wind, const char* emitVertexFn) const override { + fShader->emitSetupCode(g, "pts", wind.c_str()); + + bool isTriangle = RenderPass::kTriangleCorners == proc.fRenderPass; + g->codeAppendf("int corneridx = sk_InvocationID;"); + if (!isTriangle) { + g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1); + } + + g->codeAppendf("float2 corner = pts[corneridx];"); + g->codeAppendf("float2 left = pts[(corneridx + (%s > 0 ? %i : 1)) %% %i];", + wind.c_str(), proc.numInputPoints() - 1, proc.numInputPoints()); + g->codeAppendf("float2 right = pts[(corneridx + (%s > 0 ? 1 : %i)) %% %i];", + wind.c_str(), proc.numInputPoints() - 1, proc.numInputPoints()); g->codeAppend ("float2 leftdir = corner - left;"); g->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);"); @@ -275,42 +289,42 @@ public: // // NOTE: Since this is not a linear mapping, it is important that the box's diagonal shared // edge points in the direction of outbloat. - g->codeAppendf("%s(corner - crossbloat * bloat, " - "right_coverages[1] - left_coverages[1]," - "half2(1 + left_coverages[1], 1));", emitVertexFn); - - g->codeAppendf("%s(corner + outbloat * bloat, " - "1 + left_coverages[0] + right_coverages[0]," - "half2(0, attenuation));", emitVertexFn); - - g->codeAppendf("%s(corner - outbloat * bloat, " - "-1 - left_coverages[0] - right_coverages[0]," - "half2(1 + left_coverages[0] + right_coverages[0], 1));", emitVertexFn); - - g->codeAppendf("%s(corner + crossbloat * bloat, " - "left_coverages[1] - right_coverages[1]," - "half2(1 + right_coverages[1], 1));", emitVertexFn); - - g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, 3); + g->codeAppendf("%s(corner - crossbloat * bloat, %s, half2(1 + left_coverages[1], 1));", + emitVertexFn, + // Erase what the hull wrote previously. + isTriangle ? "right_coverages[1] - left_coverages[1]" : "-1"); + + g->codeAppendf("%s(corner + outbloat * bloat, %s, half2(0, attenuation));", + emitVertexFn, + // Erase what the hull wrote previously. + isTriangle ? "1 + left_coverages[0] + right_coverages[0]" : "-1"); + + g->codeAppendf("%s(corner - outbloat * bloat, %s, " + "half2(1 + left_coverages[0] + right_coverages[0], 1));", + emitVertexFn, + // Erase what the hull wrote previously. + isTriangle ? "-1 - left_coverages[0] - right_coverages[0]" : "-1"); + + g->codeAppendf("%s(corner + crossbloat * bloat, %s, half2(1 + right_coverages[1], 1));", + emitVertexFn, + // Erase what the hull wrote previously. + isTriangle ? "left_coverages[1] - right_coverages[1]" : "-1"); + + g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, isTriangle ? 3 : 2); } }; /** * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. */ -class GSHull4Impl : public GrCCCoverageProcessor::GSImpl { +class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl { public: - GSHull4Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} - - void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, - const char* emitVertexFn) const override { - Shader::GeometryVars vars; - fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); + GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} - const char* hullPts = vars.fHullVars.fAlternatePoints; - if (!hullPts) { - hullPts = "pts"; - } + void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g, + const GrShaderVar& wind, const char* emitVertexFn) const override { + const char* hullPts = "pts"; + fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts); // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, // we can identify the points by their corresponding corner. @@ -366,34 +380,6 @@ 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 (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { @@ -424,15 +410,14 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const { switch (fRenderPass) { case RenderPass::kTriangles: - return new GSTriangleImpl(std::move(shadr)); - case RenderPass::kTriangleCorners: - return new GSTriangleCornerImpl(std::move(shadr)); + return new GSTriangleHullImpl(std::move(shadr)); case RenderPass::kQuadratics: case RenderPass::kCubics: - return new GSHull4Impl(std::move(shadr)); + return new GSCurveHullImpl(std::move(shadr)); + case RenderPass::kTriangleCorners: case RenderPass::kQuadraticCorners: case RenderPass::kCubicCorners: - return new GSCornerImpl(std::move(shadr), 2); + return new GSCornerImpl(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 e0ec4d4142..d0fc4e0f71 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -76,7 +76,7 @@ protected: SkString varyingCode; fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, gpArgs->fPositionVar.c_str(), coverages.fCoverage, - coverages.fAttenuatedCoverage, "wind"); + coverages.fAttenuatedCoverage); v->codeAppend(varyingCode.c_str()); varyingHandler->emitAttributes(proc); @@ -131,9 +131,9 @@ static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int3 (!endptIdx ? kVertexData_InvertNegativeCoverageBit : 0)); } -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 corner_vertex_data(int32_t leftID, int32_t cornerID, int32_t rightID, + int32_t bloatIdx) { + return pack_vertex_data(leftID, rightID, bloatIdx, cornerID, kVertexData_IsCornerBit); } static constexpr int32_t kTriangleVertices[] = { @@ -168,20 +168,20 @@ static constexpr int32_t kTriangleVertices[] = { edge_vertex_data(2, 1, 1, 3), edge_vertex_data(2, 1, 2, 3), - triangle_corner_vertex_data(0, 0), - triangle_corner_vertex_data(0, 1), - triangle_corner_vertex_data(0, 2), - triangle_corner_vertex_data(0, 3), + corner_vertex_data(2, 0, 1, 0), + corner_vertex_data(2, 0, 1, 1), + corner_vertex_data(2, 0, 1, 2), + corner_vertex_data(2, 0, 1, 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), + corner_vertex_data(0, 1, 2, 0), + corner_vertex_data(0, 1, 2, 1), + corner_vertex_data(0, 1, 2, 2), + corner_vertex_data(0, 1, 2, 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), + corner_vertex_data(1, 2, 0, 0), + corner_vertex_data(1, 2, 0, 1), + corner_vertex_data(1, 2, 0, 2), + corner_vertex_data(1, 2, 0, 3), }; GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey); @@ -258,14 +258,24 @@ static constexpr int32_t kHull4Vertices[] = { hull_vertex_data(3, 1, 4), hull_vertex_data(3, 2, 4), - // No edges for now (beziers don't use edges). + corner_vertex_data(3, 0, 1, 0), + corner_vertex_data(3, 0, 1, 1), + corner_vertex_data(3, 0, 1, 2), + corner_vertex_data(3, 0, 1, 3), + + corner_vertex_data(2, 3, 0, 0), + corner_vertex_data(2, 3, 0, 1), + corner_vertex_data(2, 3, 0, 2), + corner_vertex_data(2, 3, 0, 3), }; GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); static constexpr uint16_t kHull4IndicesAsStrips[] = { 1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally). - 7, 6, 8, 5, 9, 11, 10 // Second half of the hull. + 7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull. + 13, 12, 14, 15, kRestartStrip, // First corner. + 17, 16, 18, 19 // Second corner. }; static constexpr uint16_t kHull4IndicesAsTris[] = { @@ -282,6 +292,14 @@ static constexpr uint16_t kHull4IndicesAsTris[] = { 8, 5, 9, 5, 11, 9, 9, 11, 10, + + // First corner. + 13, 12, 14, + 12, 15, 14, + + // Second corner. + 17, 16, 18, + 16, 19, 18, }; GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); @@ -309,13 +327,8 @@ public: void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v, GrGPArgs* gpArgs, Coverages* outCoverages) const override { - Shader::GeometryVars vars; - fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars); - - const char* hullPts = vars.fHullVars.fAlternatePoints; - if (!hullPts) { - hullPts = "pts"; - } + const char* hullPts = "pts"; + fShader->emitSetupCode(v, "pts", "wind", &hullPts); // 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;", @@ -350,29 +363,27 @@ public: v->codeAppend ("float2 bloatdir = leftbloat;"); - if (3 == fNumSides) { // Only triangles emit corner boxes. - v->codeAppend ("float2 leftdir = corner - left;"); - v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);"); + v->codeAppend ("float2 leftdir = corner - left;"); + v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);"); - v->codeAppend ("float2 rightdir = right - corner;"); - v->codeAppend ("rightdir = (float2(0) != rightdir)" - "? normalize(rightdir) : float2(1, 0);"); + v->codeAppend ("float2 rightdir = right - corner;"); + v->codeAppend ("rightdir = (float2(0) != rightdir)" + "? normalize(rightdir) : float2(1, 0);"); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - // In corner boxes, all 4 coverage values will not map linearly. - // Therefore it is important to align the box so its diagonal shared - // edge points out of the triangle, in the direction that ramps to 0. - v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, " - "leftdir.y > rightdir.y ? +1 : -1);"); + // In corner boxes, all 4 coverage values will not map linearly. + // Therefore it is important to align the box so its diagonal shared + // edge points out of the triangle, in the direction that ramps to 0. + v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, " + "leftdir.y > rightdir.y ? +1 : -1);"); - // For corner boxes, we hack left_right_notequal to always true. This - // in turn causes the upcoming code to always rotate, generating all - // 4 vertices of the corner box. - v->codeAppendf( "left_right_notequal = bool2(true);"); - v->codeAppend ("}"); - } + // For corner boxes, we hack left_right_notequal to always true. This + // in turn causes the upcoming code to always rotate, generating all + // 4 vertices of the corner box. + v->codeAppendf( "left_right_notequal = bool2(true);"); + v->codeAppend ("}"); // 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 this corner's first raster vertex (leftbloat), then @@ -382,12 +393,10 @@ public: v->codeAppendf("int bloatidx = (%s >> %i) & 3;", proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift); v->codeAppend ("switch (bloatidx) {"); - if (3 == fNumSides) { // 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 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. @@ -403,48 +412,25 @@ public: v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - // For triangles, we also emit coverage and attenuation. - if (3 == fNumSides) { - // The hull has a coverage of +1 all around. - v->codeAppend ("half coverage = +1;"); + v->codeAppend ("half left_coverage; {"); + Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage"); + v->codeAppend ("}"); - // Corner boxes require attenuation. - v->codeAppend ("half2 attenuated_coverage = half2(0);"); + v->codeAppend ("half right_coverage; {"); + Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage"); + v->codeAppend ("}"); - 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->codeAppend ("half attenuation; {"); + Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation"); + v->codeAppend ("}"); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - v->codeAppend ( "half left_coverage = coverage;"); - - v->codeAppend ( "half right_coverage;"); - Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", - "right_coverage"); - - v->codeAppend ( "half attenuation;"); - Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation"); - - // For corners, "coverage" erases the values that were written - // previously by the hull and edge geometry. - v->codeAppend ( "coverage = -1 - left_coverage - right_coverage;"); - - // The x and y components of "attenuated_coverage" are multiplied - // together by the fragment shader. They ramp to 0 with attenuation - // in the diagonal that points out of the triangle, and linearly from - // left-edge coverage to right in the opposite diagonal. bloatidx=0 - // is the outermost vertex; the one that has attenuation. - v->codeAppend ( "attenuated_coverage = (0 == bloatidx)" - "? half2(0, attenuation) : half2(1);"); - v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {"); - v->codeAppend ( "attenuated_coverage.x += right_coverage;"); - v->codeAppend ( "}"); - v->codeAppend ( "if (bloatidx >= 2) {"); - v->codeAppend ( "attenuated_coverage.x += left_coverage;"); - v->codeAppend ( "}"); + // Hulls have 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); + v->codeAppend ( "coverage = left_coverage;"); v->codeAppend ("}"); v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? @@ -452,52 +438,43 @@ public: kVertexData_InvertNegativeCoverageBit); v->codeAppend ( "coverage = -1 - coverage;"); v->codeAppend ("}"); - - outCoverages->fCoverage = "coverage"; - outCoverages->fAttenuatedCoverage = "attenuated_coverage"; } - } -private: - const int fNumSides; -}; + // Corner boxes require attenuation. + v->codeAppend ("half2 attenuated_coverage = half2(0, 1);"); -static constexpr uint16_t kCornerIndicesAsStrips[] = { - 0, 1, 2, 3, kRestartStrip, // First corner. - 4, 5, 6, 7 // Second corner. -}; - -static constexpr uint16_t kCornerIndicesAsTris[] = { - // First corner. - 0, 1, 2, - 1, 3, 2, - - // Second corner. - 4, 5, 6, - 5, 7, 6, -}; - -GR_DECLARE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); - -/** - * Generates conservative rasters around corners. (See comments for RenderPass) - */ -class VSCornerImpl : public GrCCCoverageProcessor::VSImpl { -public: - VSCornerImpl(std::unique_ptr<Shader> shader) : VSImpl(std::move(shader)) {} - - void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v, GrGPArgs* gpArgs, - Coverages* /*outCoverages*/) const override { - Shader::GeometryVars vars; - v->codeAppend ("int corner_id = sk_VertexID / 4;"); - fShader->emitSetupCode(v, "pts", "corner_id", "wind", &vars); + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); + // We use coverage=-1 to erase what the hull geometry wrote. + v->codeAppend ( "coverage = -1;"); + if (3 == fNumSides) { + // Triangle corners also have to erase what the edge geometry wrote. + v->codeAppend ("coverage -= left_coverage + right_coverage;"); + } + // The x and y components of "attenuated_coverage" are multiplied + // together by the fragment shader. They ramp to 0 with attenuation in + // the diagonal that points out of the corner, and linearly from + // left-edge coverage to right in the opposite diagonal. + // bloatidx=0 is the outermost vertex; the one that has attenuation. + v->codeAppend ( "attenuated_coverage = (0 == bloatidx)" + "? half2(0, attenuation) : half2(1);"); + v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {"); + v->codeAppend ( "attenuated_coverage.x += right_coverage;"); + v->codeAppend ( "}"); + v->codeAppend ( "if (bloatidx >= 2) {"); + v->codeAppend ( "attenuated_coverage.x += left_coverage;"); + v->codeAppend ( "}"); + 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->codeAppend ("coverage *= wind;"); + outCoverages->fCoverage = "coverage"; - gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); + v->codeAppend ("attenuated_coverage.x *= wind;"); + outCoverages->fAttenuatedCoverage = "attenuated_coverage"; } + +private: + const int fNumSides; }; void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { @@ -528,9 +505,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { break; } - case RenderPass::kTriangleCorners: - SK_ABORT("RenderPass::kTriangleCorners is unused by VSImpl."); - case RenderPass::kQuadratics: case RenderPass::kCubics: { GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); @@ -553,24 +527,10 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { break; } + case RenderPass::kTriangleCorners: case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: { - GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); - if (caps.usePrimitiveRestart()) { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kCornerIndicesAsStrips), - kCornerIndicesAsStrips, - gCornerIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsStrips); - } else { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kCornerIndicesAsTris), - kCornerIndicesAsTris, - gCornerIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris); - } - break; - } + case RenderPass::kCubicCorners: + SK_ABORT("Corners are not used by VSImpl."); } if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { @@ -597,12 +557,9 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { SkASSERT(sizeof(TriPointInstance) == this->getInstanceStride()); } - if (fVertexBuffer) { - SkASSERT(kAttribIdx_VertexData == this->numAttribs()); - this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType); - - SkASSERT(sizeof(int32_t) == this->getVertexStride()); - } + SkASSERT(kAttribIdx_VertexData == this->numAttribs()); + this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType); + SkASSERT(sizeof(int32_t) == this->getVertexStride()); if (caps.usePrimitiveRestart()) { this->setWillUsePrimitiveRestart(); @@ -618,9 +575,7 @@ void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceC GrMesh& mesh = out->emplace_back(fPrimitiveType); mesh.setIndexedInstanced(fIndexBuffer.get(), fNumIndicesPerInstance, instanceBuffer, instanceCount, baseInstance); - if (fVertexBuffer) { - mesh.setVertexData(fVertexBuffer.get(), 0); - } + mesh.setVertexData(fVertexBuffer.get(), 0); } GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const { @@ -633,7 +588,7 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Sh case RenderPass::kTriangleCorners: case RenderPass::kQuadraticCorners: case RenderPass::kCubicCorners: - return new VSCornerImpl(std::move(shadr)); + SK_ABORT("Corners are not used by VSImpl."); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp index 4dc100b892..edff5ff563 100644 --- a/src/gpu/ccpr/GrCCCubicShader.cpp +++ b/src/gpu/ccpr/GrCCCubicShader.cpp @@ -13,8 +13,7 @@ using Shader = GrCCCoverageProcessor::Shader; void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, const char* wind, - GeometryVars* vars) const { + const char* wind, const char** /*tighterHull*/) const { // Find the cubic's power basis coefficients. s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, " " 3, -6, 3, 0, " @@ -58,14 +57,12 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, // Evaluate the cubic at T=.5 for a mid-ish point. s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts); - // Orient the KLM matrix so L & M have matching signs on the side of the curve we wish to fill. - // We give L & M both the same sign as wind, in order to pass this value to the fragment shader. - // (Cubics are pre-chopped such that L & M do not change sign within any individual segment). + // Orient the KLM matrix so L & M are both positive on the side of the curve we wish to fill. 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] * %s, 0, " - "0, 0, orientation[1] * %s);", fKLMMatrix.c_str(), wind, wind); + "0, orientation[0], 0, " + "0, 0, orientation[1]);", fKLMMatrix.c_str()); // Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0). s->declareGlobal(fEdgeDistanceEquation); @@ -73,105 +70,54 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, 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, repetitionID, vars); } void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, const char* position, const char* coverage, - const char* attenuatedCoverage, const char* /*wind*/) { - SkASSERT(!coverage); - SkASSERT(!attenuatedCoverage); - + const char* attenuatedCoverage) { fKLMD.reset(kFloat4_GrSLType, scope); varyingHandler->addVarying("klmd", &fKLMD); 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);", OutName(fKLMD)); - - this->onEmitVaryings(varyingHandler, scope, code); -} - -void GrCCCubicShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* 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()); - - this->emitCoverage(f, outputCoverage); - - // Wind is the sign of both L and/or M. Take the sign of whichever has the larger magnitude. - // (In reality, either would be fine because we chop cubics with more than a half pixel of - // padding around the L & M lines, so neither should approach zero.) - f->codeAppend ("half wind = sign(l + m);"); - f->codeAppendf("%s *= wind;", outputCoverage); -} + // We give L & M both the same sign as wind, in order to pass this value to the fragment shader. + // (Cubics are pre-chopped such that L & M do not change sign within any individual segment.) + code->appendf("%s.xyz = klm * float3(1, %s, %s);", + OutName(fKLMD), coverage, coverage); // coverage == wind on curves. + code->appendf("%s.w = dot(float3(%s, 1), %s);", // Flat edge opposite the curve. + OutName(fKLMD), position, fEdgeDistanceEquation.c_str()); -void GrCCCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code) { fGradMatrix.reset(kFloat2x2_GrSLType, scope); varyingHandler->addVarying("grad_matrix", &fGradMatrix); - // "klm" was just defined by the base class. code->appendf("%s[0] = 2*bloat * 3 * klm[0] * %s[0].xy;", OutName(fGradMatrix), fKLMMatrix.c_str()); code->appendf("%s[1] = -2*bloat * (klm[1] * %s[2].xy + klm[2] * %s[1].xy);", - OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str()); + OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str()); + + if (attenuatedCoverage) { + fCornerCoverage.reset(kHalf2_GrSLType, scope); + varyingHandler->addVarying("corner_coverage", &fCornerCoverage); + code->appendf("%s = %s;", // Attenuated corner coverage. + OutName(fCornerCoverage), attenuatedCoverage); + } } -void GrCCCubicHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - // k,l,m,d are defined by the base class. +void GrCCCubicShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, + const char* outputCoverage) const { + f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;", 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 edge opposite the curve. -} - -void GrCCCubicCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, GeometryVars* vars) const { - s->codeAppendf("float2 corner = %s[%s * 3];", pts, repetitionID); - vars->fCornerVars.fPoint = "corner"; -} - -void GrCCCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code) { - using Interpolation = GrGLSLVaryingHandler::Interpolation; - - fdKLMDdx.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("dklmddx", &fdKLMDdx, Interpolation::kCanBeFlat); - code->appendf("%s = 2*bloat * float4(%s[0].x, %s[1].x, %s[2].x, %s.x);", - OutName(fdKLMDdx), fKLMMatrix.c_str(), fKLMMatrix.c_str(), - fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str()); - - fdKLMDdy.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("dklmddy", &fdKLMDdy, Interpolation::kCanBeFlat); - code->appendf("%s = 2*bloat * float4(%s[0].y, %s[1].y, %s[2].y, %s.y);", - OutName(fdKLMDdy), fKLMMatrix.c_str(), fKLMMatrix.c_str(), - fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str()); -} -void GrCCCubicCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn()); + f->codeAppendf("half d = min(%s.w, 0);", fKLMD.fsIn()); // Flat edge opposite the curve. + // Wind is the sign of both L and/or M. Take the sign of whichever has the larger magnitude. + // (In reality, either would be fine because we chop cubics with more than a half pixel of + // padding around the L & M lines, so neither should approach zero.) + f->codeAppend ("half wind = sign(l + m);"); + f->codeAppendf("%s = (%s + d) * wind;", outputCoverage, outputCoverage); - // 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. - // k,l,m,d are defined by the base class. - 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 ("}"); + if (fCornerCoverage.fsIn()) { + f->codeAppendf("%s = %s.x * %s.y + %s;", // Attenuated corner coverage. + outputCoverage, fCornerCoverage.fsIn(), fCornerCoverage.fsIn(), + outputCoverage); + } } diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h index dff7f6bbf5..39ca55fd9e 100644 --- a/src/gpu/ccpr/GrCCCubicShader.h +++ b/src/gpu/ccpr/GrCCCubicShader.h @@ -19,43 +19,23 @@ * * 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 GrCCGeometry.) + * (Use GrCCGeometry::cubicTo().) */ class GrCCCubicShader : public GrCCCoverageProcessor::Shader { -protected: - void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - const char* wind, GeometryVars*) const final; - virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - GeometryVars*) const {} + void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* wind, + const char** tighterHull) const override; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* coverage, const char* attenuatedCoverage, - const char* wind) final; - virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0; + const char* position, const char* coverage, + const char* attenuatedCoverage) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; - virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; - GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType}; - GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType}; + const GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType}; + const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType}; GrGLSLVarying fKLMD; -}; - -class GrCCCubicHullShader : public GrCCCubicShader { - void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; - void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; - GrGLSLVarying fGradMatrix; -}; - -class GrCCCubicCornerShader : public GrCCCubicShader { - void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - GeometryVars*) const override; - void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; - void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; - - GrGLSLVarying fdKLMDdx; - GrGLSLVarying fdKLMDdy; + GrGLSLVarying fCornerCoverage; }; #endif diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp index 5bee85ef3f..287e63e8bf 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.cpp +++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp @@ -14,8 +14,7 @@ using Shader = GrCCCoverageProcessor::Shader; void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, const char* wind, - GeometryVars* vars) const { + const char* wind, const char** tighterHull) const { s->declareGlobal(fQCoordMatrix); s->codeAppendf("%s = float2x2(1, 1, .5, 0) * inverse(float2x2(%s[2] - %s[0], %s[1] - %s[0]));", fQCoordMatrix.c_str(), pts, pts, pts, pts); @@ -28,113 +27,60 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p s->codeAppendf("float2 edgept1 = %s[%s > 0 ? 0 : 2];", pts, wind); Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str()); - this->onEmitSetupCode(s, pts, repetitionID, vars); + if (tighterHull) { + // 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); + *tighterHull = "quadratic_hull"; + } } void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, const char* position, const char* coverage, - const char* attenuatedCoverage, const char* wind) { - SkASSERT(!coverage); - SkASSERT(!attenuatedCoverage); - - fXYDW.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("xydw", &fXYDW); - code->appendf("%s.xy = %s * (%s - %s);", - OutName(fXYDW), fQCoordMatrix.c_str(), position, fQCoord0.c_str()); - code->appendf("%s.z = dot(%s.xy, %s) + %s.z;", - OutName(fXYDW), fEdgeDistanceEquation.c_str(), position, - fEdgeDistanceEquation.c_str()); - code->appendf("%s.w = %s;", OutName(fXYDW), wind); - - this->onEmitVaryings(varyingHandler, scope, code); + const char* attenuatedCoverage) { + fCoord.reset(kFloat4_GrSLType, scope); + varyingHandler->addVarying("coord", &fCoord); + code->appendf("%s.xy = %s * (%s - %s);", // Quadratic coords. + OutName(fCoord), fQCoordMatrix.c_str(), position, fQCoord0.c_str()); + code->appendf("%s.zw = 2*bloat * float2(2 * %s.x, -1) * %s;", // Gradient. + OutName(fCoord), OutName(fCoord), fQCoordMatrix.c_str()); + + // Coverages need full precision since distance to the opposite edge can be large. + fCoverages.reset(attenuatedCoverage ? kFloat4_GrSLType : kFloat2_GrSLType, scope); + varyingHandler->addVarying("coverages", &fCoverages); + code->appendf("%s.x = dot(%s, float3(%s, 1));", // Distance to flat edge opposite the curve. + OutName(fCoverages), fEdgeDistanceEquation.c_str(), position); + code->appendf("%s.y = %s;", OutName(fCoverages), coverage); // Wind. + if (attenuatedCoverage) { + code->appendf("%s.zw = %s;", // Attenuated corner coverage. + OutName(fCoverages), attenuatedCoverage); + } } void GrCCQuadraticShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { - this->emitCoverage(f, outputCoverage); - f->codeAppendf("%s *= %s.w;", outputCoverage, fXYDW.fsIn()); // Sign by wind. -} - -void GrCCQuadraticHullShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* /*repetitionID*/, - 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 GrCCQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code) { - fGrad.reset(kFloat2_GrSLType, scope); - varyingHandler->addVarying("grad", &fGrad); - code->appendf("%s = 2*bloat * float2(2 * %s.x, -1) * %s;", - OutName(fGrad), OutName(fXYDW), fQCoordMatrix.c_str()); -} - -void GrCCQuadraticHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));", - fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn(), fGrad.fsIn(), fGrad.fsIn()); - f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage); - f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYDW.fsIn()); // Flat closing edge. -} - -void GrCCQuadraticCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, - GeometryVars* vars) const { - s->codeAppendf("float2 corner = %s[%s * 2];", pts, repetitionID); - vars->fCornerVars.fPoint = "corner"; -} - -void GrCCQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code) { - using Interpolation = GrGLSLVaryingHandler::Interpolation; - - fdXYDdx.reset(kFloat3_GrSLType, scope); - varyingHandler->addVarying("dXYDdx", &fdXYDdx, Interpolation::kCanBeFlat); - code->appendf("%s = 2*bloat * float3(%s[0].x, %s[0].y, %s.x);", - OutName(fdXYDdx), fQCoordMatrix.c_str(), fQCoordMatrix.c_str(), - fEdgeDistanceEquation.c_str()); - - fdXYDdy.reset(kFloat3_GrSLType, scope); - varyingHandler->addVarying("dXYDdy", &fdXYDdy, Interpolation::kCanBeFlat); - code->appendf("%s = 2*bloat * float3(%s[1].x, %s[1].y, %s.y);", - OutName(fdXYDdy), fQCoordMatrix.c_str(), fQCoordMatrix.c_str(), - fEdgeDistanceEquation.c_str()); -} - -void GrCCQuadraticCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;", - fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.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->codeAppendf("float x = %s.x, y = %s.y;", fCoord.fsIn(), fCoord.fsIn()); 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); + f->codeAppendf("float2 grad = %s.zw;", fCoord.fsIn()); + f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad, grad)), 0, 1);", outputCoverage); + + f->codeAppendf("half d = min(%s.x, 0);", fCoverages.fsIn()); // Flat edge opposite the curve. + f->codeAppendf("half wind = %s.y;", fCoverages.fsIn()); + f->codeAppendf("%s = (%s + d) * wind;", outputCoverage, 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);", fXYDW.fsIn(), fXYDW.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("}"); + if (kFloat4_GrSLType == fCoverages.type()) { + f->codeAppendf("%s = %s.z * %s.w + %s;", // Attenuated corner coverage. + outputCoverage, fCoverages.fsIn(), fCoverages.fsIn(), outputCoverage); + } } diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h index 8635ba4ba0..30c5230154 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.h +++ b/src/gpu/ccpr/GrCCQuadraticShader.h @@ -18,55 +18,24 @@ * 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 GrCCGeometry.) + * (Use GrCCGeometry::quadraticTo().) */ class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader { protected: - void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - const char* wind, GeometryVars*) const final; - virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - GeometryVars*) const = 0; + void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* wind, + const char** tighterHull) const override; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* coverage, const char* attenuatedCoverage, - const char* wind) final; - virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {} + const char* position, const char* coverage, + const char* attenuatedCoverage) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; - virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; const GrShaderVar fQCoordMatrix{"qcoord_matrix", kFloat2x2_GrSLType}; const GrShaderVar fQCoord0{"qcoord0", kFloat2_GrSLType}; const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType}; - GrGLSLVarying fXYDW; -}; - -/** - * 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 GrCCQuadraticHullShader : public GrCCQuadraticShader { - void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - GeometryVars*) const override; - void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; - void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; - - GrGLSLVarying fGrad; -}; - -/** - * This pass fixes the corners of a closed quadratic segment with soft MSAA. - */ -class GrCCQuadraticCornerShader : public GrCCQuadraticShader { - void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - GeometryVars*) const override; - void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override; - void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; - - GrGLSLVarying fdXYDdx; - GrGLSLVarying fdXYDdy; + GrGLSLVarying fCoord; + GrGLSLVarying fCoverages; }; #endif diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h index 3dcb351970..f9af13c3be 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.h +++ b/src/gpu/ccpr/GrCCTriangleShader.h @@ -19,31 +19,28 @@ class GrCCTriangleShader : public GrCCCoverageProcessor::Shader { void onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, const char* position, const char* coverage, - const char* attenuatedCoverage, const char* wind) override { + const char* attenuatedCoverage) override { if (!attenuatedCoverage) { - fCoverageTimesWind.reset(kHalf_GrSLType, scope); - varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); - code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), coverage, wind); + fCoverages.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("coverage", &fCoverages); + code->appendf("%s = %s;", OutName(fCoverages), coverage); } else { - fCoverageTimesWind.reset(kHalf3_GrSLType, scope); - varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); - code->appendf("%s = half3(%s, %s);", - OutName(fCoverageTimesWind), attenuatedCoverage, coverage); - code->appendf("%s.yz *= %s;", OutName(fCoverageTimesWind), wind); + fCoverages.reset(kHalf3_GrSLType, scope); + varyingHandler->addVarying("coverages", &fCoverages); + code->appendf("%s = half3(%s, %s);", OutName(fCoverages), attenuatedCoverage, coverage); } } void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override { - if (kHalf_GrSLType == fCoverageTimesWind.type()) { - f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); + if (kHalf_GrSLType == fCoverages.type()) { + f->codeAppendf("%s = %s;", outputCoverage, fCoverages.fsIn()); } else { f->codeAppendf("%s = %s.x * %s.y + %s.z;", - outputCoverage, fCoverageTimesWind.fsIn(), fCoverageTimesWind.fsIn(), - fCoverageTimesWind.fsIn()); + outputCoverage, fCoverages.fsIn(), fCoverages.fsIn(), fCoverages.fsIn()); } } - GrGLSLVarying fCoverageTimesWind; + GrGLSLVarying fCoverages; }; #endif |