diff options
author | Chris Dalton <csmartdalton@google.com> | 2018-03-07 17:28:07 -0700 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-03-08 02:13:50 +0000 |
commit | df04ce29490a24f9d5b4f5caafd8f6a3368a19e0 (patch) | |
tree | 8ee3e7c10e544d852456b607d6a6476237083e84 /src | |
parent | 5f20fa2923cdd5c8df36efff2ce7b5bd00c5bbb4 (diff) |
ccpr: Draw curves in a single pass
Throws out the complicated MSAA curve corner shaders, and instead just
ramps coverage to zero at bloat vertices that fall outside the curve.
Updates SampleCCPRGeometry to better visualize this new geometry by
clearing to black and drawing with SkBlendMode::kPlus.
Bug: skia:
Change-Id: Ibe86cbc741d8b015127b10dd43e3b52e7cb35732
Reviewed-on: https://skia-review.googlesource.com/112626
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 119 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 45 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 89 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 199 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.cpp | 118 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.h | 30 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.cpp | 4 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.cpp | 118 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.h | 43 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.cpp | 6 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.h | 6 |
11 files changed, 271 insertions, 506 deletions
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index 686ab5514d..76ca8f562e 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -15,35 +15,59 @@ #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLVertexGeoBuilder.h" +void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, + GrProcessorKeyBuilder* b) const { + int key = (int)fRenderPass << 2; + if (WindMethod::kInstanceData == fWindMethod) { + key |= 2; + } + if (Impl::kVertexShader == fImpl) { + key |= 1; + } +#ifdef SK_DEBUG + uint32_t bloatBits; + memcpy(&bloatBits, &fDebugBloat, 4); + b->add32(bloatBits); +#endif + b->add32(key); +} + +GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const { + std::unique_ptr<Shader> shader; + switch (fRenderPass) { + case RenderPass::kTriangles: + shader = skstd::make_unique<GrCCTriangleShader>(); + break; + case RenderPass::kTriangleCorners: + shader = skstd::make_unique<GrCCTriangleCornerShader>(); + break; + case RenderPass::kQuadratics: + shader = skstd::make_unique<GrCCQuadraticShader>(); + break; + case RenderPass::kCubics: + shader = skstd::make_unique<GrCCCubicShader>(); + break; + } + return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader)) + : this->createVSImpl(std::move(shader)); +} + void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc, GrGLSLFPFragmentBuilder* f, const char* skOutputColor, const char* skOutputCoverage) const { f->codeAppendf("half coverage = 0;"); - this->onEmitFragmentCode(f, "coverage"); + this->onEmitFragmentCode(proc, f, "coverage"); f->codeAppendf("%s.a = coverage;", skOutputColor); f->codeAppendf("%s = half4(1);", skOutputCoverage); #ifdef SK_DEBUG if (proc.debugVisualizationsEnabled()) { - f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);", - skOutputColor, skOutputColor, skOutputColor); + f->codeAppendf("%s = half4(-%s.a, %s.a, 0, abs(%s.a));", + skOutputColor, skOutputColor, skOutputColor, skOutputColor); } #endif } -void GrCCCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder* s, - const char* leftPt, - const char* rightPt, - const char* outputDistanceEquation) { - 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)) * (bloat * 2);"); - // When nwidth=0, wind must also be 0 (and coverage * wind = 0). So it doesn't matter what we - // come up with here as long as it isn't NaN or Inf. - s->codeAppend ("n /= (0 != nwidth) ? nwidth : 1;"); - s->codeAppendf("%s = float3(-n, dot(n, %s) - .5);", outputDistanceEquation, leftPt); -} - void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s, const char* leftPt, const char* rightPt, @@ -78,66 +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); } - -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; - if (WindMethod::kInstanceData == fWindMethod) { - key |= 2; - } - if (Impl::kVertexShader == fImpl) { - key |= 1; - } -#ifdef SK_DEBUG - uint32_t bloatBits; - memcpy(&bloatBits, &fDebugBloat, 4); - b->add32(bloatBits); -#endif - b->add32(key); -} - -GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const { - std::unique_ptr<Shader> shader; - switch (fRenderPass) { - case RenderPass::kTriangles: - shader = skstd::make_unique<GrCCTriangleShader>(); - break; - case RenderPass::kTriangleCorners: - shader = skstd::make_unique<GrCCTriangleCornerShader>(); - break; - case RenderPass::kQuadratics: - shader = skstd::make_unique<GrCCQuadraticHullShader>(); - break; - case RenderPass::kQuadraticCorners: - shader = skstd::make_unique<GrCCQuadraticCornerShader>(); - break; - case RenderPass::kCubics: - shader = skstd::make_unique<GrCCCubicHullShader>(); - break; - case RenderPass::kCubicCorners: - shader = skstd::make_unique<GrCCCubicCornerShader>(); - break; - } - return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader)) - : this->createVSImpl(std::move(shader)); -} diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index c1f85993a1..7db424e219 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -53,20 +53,15 @@ public: void set(const SkPoint[4], float dx, float dy); void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w); }; - - // All primitive shapes (triangles and closed, convex bezier curves) require two - // render passes: One to draw a rough outline of the shape, and a second pass to touch up the - // corners. 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. + // Here we enumerate every render pass needed in order to produce a complete coverage count + // 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, - kQuadraticCorners, kCubics, - kCubicCorners }; - static bool RenderPassIsCubic(RenderPass); static const char* RenderPassName(RenderPass); enum class WindMethod : bool { @@ -152,13 +147,6 @@ public: void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* skOutputColor, const char* skOutputCoverage) const; - // Defines an equation ("dot(float3(pt, 1), distance_equation)") that is -1 on the outside - // border of a conservative raster edge and 0 on the inside. 'leftPt' and 'rightPt' must be - // ordered clockwise. - static void EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder*, const char* leftPt, - const char* rightPt, - const char* outputDistanceEquation); - // Calculates an edge's coverage at a conservative raster vertex. The edge is defined by two // clockwise-ordered points, 'leftPt' and 'rightPt'. 'rasterVertexDir' is a pair of +/-1 // values that point in the direction of conservative raster bloat, starting from an @@ -181,7 +169,7 @@ public: const char* wind) = 0; // Emits the fragment code that calculates a pixel's signed coverage value. - virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, + virtual void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; // Returns the name of a Shader's internal varying at the point where where its value is @@ -191,12 +179,6 @@ 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; @@ -208,7 +190,7 @@ private: static constexpr float kAABloatRadius = 0.491111f; // Number of bezier points for curves, or 3 for triangles. - int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; } + int numInputPoints() const { return RenderPass::kCubics == fRenderPass ? 4 : 3; } enum class Impl : bool { kGeometryShader, @@ -269,29 +251,12 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con Sk2f::Store4(this, P0, P1, P2, W); } -inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) { - switch (pass) { - case RenderPass::kTriangles: - case RenderPass::kTriangleCorners: - case RenderPass::kQuadratics: - case RenderPass::kQuadraticCorners: - return false; - case RenderPass::kCubics: - case RenderPass::kCubicCorners: - return true; - } - SK_ABORT("Invalid RenderPass"); - return false; -} - 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::kQuadraticCorners: return "kQuadraticCorners"; case RenderPass::kCubics: return "kCubics"; - case RenderPass::kCubicCorners: return "kCubicCorners"; } SK_ABORT("Invalid RenderPass"); return ""; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index d9febc0e66..e64b8c0838 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -76,7 +76,7 @@ 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) { + if (RenderPass::kTriangleCorners != proc.fRenderPass) { coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); } g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { @@ -212,7 +212,8 @@ public: }; /** - * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. + * Generates a conservative raster hull around a convex quadrilateral that encloses a cubic or + * quadratic, as well as its shared edge. */ class GSHull4Impl : public GrCCCoverageProcessor::GSImpl { public: @@ -231,54 +232,85 @@ public: // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, // we can identify the points by their corresponding corner. // - // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate - // the hull in two independent invocations. Each invocation designates the corner it will - // begin with as top-left. - g->codeAppend ("int i = sk_InvocationID * 2;"); + // NOTE: For the hull we split the square down the diagonal from top-right to bottom-left, + // and generate it in two independent invocations. All invocations, including the shared + // edge, designate the corner they will begin with as top-left. + g->codeAppendf("bool is_shared_edge = (2 == sk_InvocationID);"); + g->codeAppendf("int i = !is_shared_edge ? sk_InvocationID * 2 : (%s > 0 ? 3 : 0);", + wind.c_str()); g->codeAppendf("float2 topleft = %s[i];", hullPts); - g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str()); - g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str()); - g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts); + g->codeAppendf("float2 topright = %s[(i + (%s > 0 ? 1 : 3)) & 3];", hullPts, wind.c_str()); + g->codeAppendf("float2 bottomleft = %s[(i + (%s > 0 ? 3 : 1)) & 3];", + hullPts, wind.c_str()); + g->codeAppendf("float2 bottomright = %s[(i + 2) & 3];", hullPts); // Determine how much to outset the conservative raster hull from the relevant edges. - g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, " - "topleft.x > bottomleft.x ? -bloat : bloat);"); - g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, " - "topright.x > topleft.x ? -bloat : +bloat);"); - g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, " - "bottomright.x > topright.x ? -bloat : +bloat);"); + g->codeAppend ("float2 leftbloat = sign(topleft - bottomleft) * bloat;"); + g->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " + "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);"); + + g->codeAppend ("float2 upbloat = sign(topright - topleft) * bloat;"); + g->codeAppend ("upbloat = float2(0 != upbloat.y ? upbloat.y : upbloat.x, " + "0 != upbloat.x ? -upbloat.x : -upbloat.y);"); + + g->codeAppend ("float2 rightbloat = sign(bottomright - topright) * bloat;"); + g->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " + "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);"); + + // The hull raster has a coverage of +1 all around. + g->codeAppend ("half2 coverages = half2(+1);"); + + g->codeAppend ("if (is_shared_edge) {"); + // On bloat vertices along the shared edge that fall outside the input + // points, ramp coverage to 0. We do this by using coverage=-1 to erase + // what the hull just wrote. + g->codeAppend ( "coverages = half2(-1, 0);"); + // Reassign bloats to characterize a conservative raster around just the + // shared edge, rather than the entire hull. + g->codeAppend ( "leftbloat = rightbloat = -upbloat;"); + g->codeAppend ("}"); // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size // boxes centered on the input points, split evenly between two invocations. This translates // to a polygon with either one, two, or three vertices at each input point, depending on - // how sharp the corner is. For more details on conservative raster, see: + // how sharp the corner is. The shared edge raster is the convex hull of 2 pixel-size boxes, + // one at each endpoint. For more details on conservative raster, see: // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);"); g->codeAppend ("if (all(left_up_notequal)) {"); // The top-left corner will have three conservative raster vertices. // Emit the middle one first to the triangle strip. - g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn); + g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x), coverages[0]);", + emitVertexFn); g->codeAppend ("}"); g->codeAppend ("if (any(left_up_notequal)) {"); // Second conservative raster vertex for the top-left corner. - g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn); + g->codeAppendf( "%s(topleft + leftbloat, coverages[1]);", emitVertexFn); g->codeAppend ("}"); - // Main interior body of this invocation's half of the hull. - g->codeAppendf("%s(topleft + upbloat);", emitVertexFn); - g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn); - g->codeAppendf("%s(topright + upbloat);", emitVertexFn); + g->codeAppendf("%s(topleft + upbloat, coverages[0]);", emitVertexFn); + + g->codeAppend ("if (!is_shared_edge) {"); + // Main interior body of this invocation's half of the hull. + g->codeAppendf( "%s(bottomleft + leftbloat, +1);", emitVertexFn); + g->codeAppend ("}"); + + g->codeAppendf("%s(topright + (is_shared_edge ? rightbloat : upbloat), coverages[1]);", + emitVertexFn); // Remaining two conservative raster vertices for the top-right corner. g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);"); g->codeAppend ("if (any(up_right_notequal)) {"); - g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn); + g->codeAppendf( "%s(topright + (is_shared_edge ? upbloat : rightbloat), " + "coverages[0]);", emitVertexFn); g->codeAppend ("}"); g->codeAppend ("if (all(up_right_notequal)) {"); - g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn); + g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x), coverages[0]);", + emitVertexFn); g->codeAppend ("}"); - g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2); + // 3 invocations: 2 hull invocations and 1 shared edge. + g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 3); } }; @@ -312,17 +344,15 @@ private: void GrCCCoverageProcessor::initGS() { SkASSERT(Impl::kGeometryShader == fImpl); - if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { + if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) { SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2); SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride()); - GR_STATIC_ASSERT(0 == offsetof(QuadPointInstance, fX)); } else { this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2); SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride()); - GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX)); } this->setWillUseGeoShader(); } @@ -348,9 +378,6 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Sh case RenderPass::kQuadratics: case RenderPass::kCubics: return new GSHull4Impl(std::move(shadr)); - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: - return new GSCornerImpl(std::move(shadr), 2); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index 144a4a5d58..08398e1900 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -92,13 +92,13 @@ protected: static constexpr int kVertexData_LeftNeighborIdShift = 9; static constexpr int kVertexData_RightNeighborIdShift = 7; static constexpr int kVertexData_BloatIdxShift = 5; -static constexpr int kVertexData_InvertCoverageBit = 1 << 4; +static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 4; static constexpr int kVertexData_IsEdgeBit = 1 << 3; static constexpr int kVertexData_IsHullBit = 1 << 2; /** * Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to - * calculate initial coverage values for edges. See VSHullAndEdgeImpl. + * calculate coverage values. See VSHullAndEdgeImpl. */ static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID, int32_t bloatIdx, int32_t cornerID, @@ -114,15 +114,12 @@ static constexpr int32_t hull_vertex_data(int32_t cornerID, int32_t bloatIdx, in kVertexData_IsHullBit); } -static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int32_t bloatIdx, - int n) { - return pack_vertex_data(0 == endptIdx ? (edgeID + 1) % n : edgeID, - 0 == endptIdx ? (edgeID + 1) % n : edgeID, - bloatIdx, 0 == endptIdx ? edgeID : (edgeID + 1) % n, - kVertexData_IsEdgeBit | - (!endptIdx ? kVertexData_InvertCoverageBit : 0)); +static constexpr int32_t edge_vertex_data(int32_t leftID, int rightID, int32_t bloatIdx, + int32_t extraData = 0) { + return pack_vertex_data(leftID, leftID, bloatIdx, rightID, kVertexData_IsEdgeBit | extraData); } + static constexpr int32_t kHull3AndEdgeVertices[] = { hull_vertex_data(0, 0, 3), hull_vertex_data(0, 1, 3), @@ -134,26 +131,26 @@ static constexpr int32_t kHull3AndEdgeVertices[] = { hull_vertex_data(2, 1, 3), hull_vertex_data(2, 2, 3), - edge_vertex_data(0, 0, 0, 3), - edge_vertex_data(0, 0, 1, 3), - edge_vertex_data(0, 0, 2, 3), - edge_vertex_data(0, 1, 0, 3), - edge_vertex_data(0, 1, 1, 3), - edge_vertex_data(0, 1, 2, 3), - - edge_vertex_data(1, 0, 0, 3), - edge_vertex_data(1, 0, 1, 3), - edge_vertex_data(1, 0, 2, 3), - edge_vertex_data(1, 1, 0, 3), - edge_vertex_data(1, 1, 1, 3), - edge_vertex_data(1, 1, 2, 3), - - edge_vertex_data(2, 0, 0, 3), - edge_vertex_data(2, 0, 1, 3), - edge_vertex_data(2, 0, 2, 3), - edge_vertex_data(2, 1, 0, 3), - edge_vertex_data(2, 1, 1, 3), - edge_vertex_data(2, 1, 2, 3), + edge_vertex_data(0, 1, 0), + edge_vertex_data(0, 1, 1), + edge_vertex_data(0, 1, 2), + edge_vertex_data(1, 0, 0, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(1, 0, 1, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(1, 0, 2, kVertexData_InvertNegativeCoverageBit), + + edge_vertex_data(1, 2, 0), + edge_vertex_data(1, 2, 1), + edge_vertex_data(1, 2, 2), + edge_vertex_data(2, 1, 0, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(2, 1, 1, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(2, 1, 2, kVertexData_InvertNegativeCoverageBit), + + edge_vertex_data(2, 0, 0), + edge_vertex_data(2, 0, 1), + edge_vertex_data(2, 0, 2), + edge_vertex_data(0, 2, 0, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(0, 2, 1, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(0, 2, 2, kVertexData_InvertNegativeCoverageBit), }; GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); @@ -201,7 +198,7 @@ static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = { GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); -static constexpr int32_t kHull4Vertices[] = { +static constexpr int32_t kHull4AndEdgeVertices[] = { hull_vertex_data(0, 0, 4), hull_vertex_data(0, 1, 4), hull_vertex_data(0, 2, 4), @@ -215,17 +212,23 @@ 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). + edge_vertex_data(0, 3, 0, kVertexData_InvertNegativeCoverageBit), + edge_vertex_data(0, 3, 1), + edge_vertex_data(0, 3, 2), + edge_vertex_data(3, 0, 0), + edge_vertex_data(3, 0, 1), + edge_vertex_data(3, 0, 2, kVertexData_InvertNegativeCoverageBit), }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeVertexBufferKey); -static constexpr uint16_t kHull4IndicesAsStrips[] = { +static constexpr uint16_t kHull4AndEdgeIndicesAsStrips[] = { 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, 17, 15, 16 // Shared edge. }; -static constexpr uint16_t kHull4IndicesAsTris[] = { +static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = { // First half of the hull (split diagonally). 1, 0, 2, 0, 11, 2, @@ -239,23 +242,30 @@ static constexpr uint16_t kHull4IndicesAsTris[] = { 8, 5, 9, 5, 11, 9, 9, 11, 10, + + // Shared edge. + 13, 12, 14, + 12, 17, 14, + 14, 17, 15, + 17, 16, 15, }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey); /** - * Generates a conservative raster hull around a convex polygon. For triangles we generate - * additional conservative rasters around the edges and calculate coverage ramps. + * 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, and (2) draw conservative rasters around each edge, with a + * 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. + * into smooth, antialiased ones. The final corners get touched up in a later step by VSCornerImpl. * - * Curve rough outlines are just the conservative raster of a convex quadrilateral that encloses the - * curve. The Shader takes care of everything else for now. - * - * 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 VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl { public: @@ -284,10 +294,9 @@ public: // 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. For more details on conservative raster, see: + // 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 - // - // Triangle edges are also handled here using the same concept (see kHull3AndEdgeVertices). v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", hullPts, kVertexData_LeftNeighborIdShift); @@ -324,29 +333,32 @@ public: // fallthru. v->codeAppend ("}"); - // For triangles, we also emit coverage in order to handle edges and corners. - const char* coverage = nullptr; + 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->codeAppend ("half coverage;"); + 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->codeAppendf("if (0 != (%s & %i)) {", // Are we the opposite endpoint of an edge? - proc.getAttrib(kAttribIdx_VertexData).fName, - kVertexData_InvertCoverageBit); - v->codeAppend ( "coverage = -1 - coverage;"); v->codeAppend ("}"); - - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a hull vertex? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsHullBit); - v->codeAppend ( "coverage = +1;"); // Hull coverage is +1 all around. + } 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 ("}"); - - coverage = "coverage"; } - v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); - gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); + v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? + proc.getAttrib(kAttribIdx_VertexData).fName, + kVertexData_InvertNegativeCoverageBit); + v->codeAppend ( "coverage = -1 - coverage;"); + v->codeAppend ("}"); - return coverage; + return "coverage"; } private: @@ -425,31 +437,7 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { break; } - case RenderPass::kQuadratics: - case RenderPass::kCubics: { - GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); - fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices), - kHull4Vertices, gHull4VertexBufferKey); - GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); - if (caps.usePrimitiveRestart()) { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull4IndicesAsStrips), - kHull4IndicesAsStrips, - gHull4IndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips); - } else { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull4IndicesAsTris), - kHull4IndicesAsTris, - gHull4IndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris); - } - break; - } - - case RenderPass::kTriangleCorners: - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: { + case RenderPass::kTriangleCorners: { GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); if (caps.usePrimitiveRestart()) { fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, @@ -464,14 +452,35 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { gCornerIndexBufferKey); fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris); } - if (RenderPass::kTriangleCorners != fRenderPass) { - fNumIndicesPerInstance = fNumIndicesPerInstance * 2/3; + break; + } + + case RenderPass::kQuadratics: + case RenderPass::kCubics: { + GR_DEFINE_STATIC_UNIQUE_KEY(gHull4AndEdgeVertexBufferKey); + fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, + sizeof(kHull4AndEdgeVertices), + kHull4AndEdgeVertices, + gHull4AndEdgeVertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey); + if (caps.usePrimitiveRestart()) { + fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kHull4AndEdgeIndicesAsStrips), + kHull4AndEdgeIndicesAsStrips, + gHull4AndEdgeIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4AndEdgeIndicesAsStrips); + } else { + fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kHull4AndEdgeIndicesAsTris), + kHull4AndEdgeIndicesAsTris, + gHull4AndEdgeIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4AndEdgeIndicesAsTris); } break; } } - if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { + if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) { SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); SkASSERT(kAttribIdx_X == this->numAttribs()); @@ -525,13 +534,11 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Sh 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); - case RenderPass::kTriangleCorners: - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: - return new VSCornerImpl(std::move(shadr)); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp index 5ae51c7d9b..76d1646b65 100644 --- a/src/gpu/ccpr/GrCCCubicShader.cpp +++ b/src/gpu/ccpr/GrCCCubicShader.cpp @@ -13,8 +13,8 @@ using Shader = GrCCCoverageProcessor::Shader; void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, const char* wind, - GeometryVars* vars) const { + 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, " @@ -58,118 +58,44 @@ 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); - - // Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0). - s->declareGlobal(fEdgeDistanceEquation); - s->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind); - s->codeAppendf("float2 edgept0 = %s[edgeidx0];", pts); - s->codeAppendf("float2 edgept1 = %s[3 - edgeidx0];", pts); - Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str()); - - this->onEmitSetupCode(s, pts, repetitionID, vars); + "0, orientation[0], 0, " + "0, 0, orientation[1]);", fKLMMatrix.c_str()); } void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, const char* position, const char* inputCoverage, - const char* /*wind*/) { - SkASSERT(!inputCoverage); - - fKLMD.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("klmd", &fKLMD); + const char* wind) { 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); + fKLMW.reset(kFloat4_GrSLType, scope); + varyingHandler->addVarying("klmw", &fKLMW); + code->appendf("%s.xyz = klm;", OutName(fKLMW)); + code->appendf("%s.w = %s * %s;", OutName(fKLMW), inputCoverage, wind); - // 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); -} - -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] = 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()); } -void GrCCCubicHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - // k,l,m,d are defined by the base class. - 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 = 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 = 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, +void GrCCCubicShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, + GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { - f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn()); - - // Erase what the previous hull shader wrote. We don't worry about the two corners falling on - // the same pixel because those cases should have been weeded out by this point. - // k,l,m,d are defined by the base class. + f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;", + fKLMW.fsIn(), fKLMW.fsIn(), fKLMW.fsIn()); f->codeAppend ("float f = k*k*k - l*m;"); - f->codeAppend ("float2 grad_f = float3(3*k*k, -m, -l) * float2x3(grad_klmd);"); - f->codeAppendf("%s = -clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", - outputCoverage); - f->codeAppendf("%s -= d;", outputCoverage); - - // Use software msaa to estimate actual coverage at the corner pixels. - const int sampleCount = Shader::DefineSoftSampleLocations(f, "samples"); - f->codeAppendf("float4 klmd_center = float4(%s.xyz, %s.w + 0.5);", - fKLMD.fsIn(), fKLMD.fsIn()); - f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount); - f->codeAppend ( "float4 klmd = grad_klmd * samples[i] + klmd_center;"); - f->codeAppend ( "half f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;"); - f->codeAppendf( "%s += all(greaterThan(half4(f, klmd.y, klmd.z, klmd.w), " - "half4(0))) ? %f : 0;", - outputCoverage, 1.0 / sampleCount); - f->codeAppend ("}"); + f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn()); + f->codeAppend ("float d = f * inversesqrt(dot(grad_f, grad_f));"); +#ifdef SK_DEBUG + if (proc.debugVisualizationsEnabled()) { + f->codeAppendf("d /= %f;", proc.debugBloat()); + } +#endif + f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s.w;", outputCoverage, fKLMW.fsIn()); } diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h index 063549264a..70d3300461 100644 --- a/src/gpu/ccpr/GrCCCubicShader.h +++ b/src/gpu/ccpr/GrCCCubicShader.h @@ -24,37 +24,17 @@ 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 {} + const char* wind, GeometryVars*) const override; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) final; - virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0; + const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; - virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; + void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, + const char* outputCoverage) const override; GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType}; - 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 fKLMW; 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; -}; - #endif diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index 43f5e6be6a..e625c43743 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -530,15 +530,11 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount if (batchTotalCounts.fQuadratics) { this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics, WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners, - WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds); } if (batchTotalCounts.fCubics) { this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics, WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners, - WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); } } diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp index 090e29f4c3..baa10fd34e 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.cpp +++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp @@ -14,7 +14,7 @@ using Shader = GrCCCoverageProcessor::Shader; void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, const char* wind, + const char* /*repetitionID*/, const char* /*wind*/, GeometryVars* vars) const { s->declareGlobal(fCanonicalMatrix); s->codeAppendf("%s = float3x3(0.0, 0, 1, " @@ -25,41 +25,6 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p "%s[2], 1));", fCanonicalMatrix.c_str(), pts, pts, pts); - s->declareGlobal(fEdgeDistanceEquation); - s->codeAppendf("float2 edgept0 = %s[%s > 0 ? 2 : 0];", pts, wind); - s->codeAppendf("float2 edgept1 = %s[%s > 0 ? 0 : 2];", pts, wind); - Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str()); - - this->onEmitSetupCode(s, pts, repetitionID, vars); -} - -void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code, - const char* position, const char* inputCoverage, - const char* wind) { - SkASSERT(!inputCoverage); - - fXYDW.reset(kFloat4_GrSLType, scope); - varyingHandler->addVarying("xydw", &fXYDW); - code->appendf("%s.xy = (%s * float3(%s, 1)).xy;", - OutName(fXYDW), fCanonicalMatrix.c_str(), position); - 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); -} - -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); @@ -76,66 +41,31 @@ void GrCCQuadraticHullShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const c 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 = float2(2 * %s.x, -1) * float2x2(%s);", - OutName(fGrad), OutName(fXYDW), fCanonicalMatrix.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 = float3(%s[0].x, %s[0].y, %s.x);", - OutName(fdXYDdx), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(), - fEdgeDistanceEquation.c_str()); +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()); - fdXYDdy.reset(kFloat3_GrSLType, scope); - varyingHandler->addVarying("dXYDdy", &fdXYDdy, Interpolation::kCanBeFlat); - code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);", - OutName(fdXYDdy), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(), - fEdgeDistanceEquation.c_str()); + fCoverageTimesWind.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); + code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); } -void GrCCQuadraticCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f, +void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc, + 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->codeAppend ("float f = x*x - y;"); - f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);"); - f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage); - f->codeAppendf("%s -= d;", outputCoverage); - - // Use software msaa to approximate coverage at the corner pixels. - int sampleCount = Shader::DefineSoftSampleLocations(f, "samples"); - f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);", 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("}"); + f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s.zw, %s.zw));", + fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn()); +#ifdef SK_DEBUG + if (proc.debugVisualizationsEnabled()) { + f->codeAppendf("d /= %f;", proc.debugBloat()); + } +#endif + 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 0be03d33dd..d91f943471 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.h +++ b/src/gpu/ccpr/GrCCQuadraticShader.h @@ -23,48 +23,17 @@ 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; + const char* wind, GeometryVars*) const override; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) final; - virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {} + const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; - virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0; + void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, + const char* outputCoverage) const override; const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_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 fCoords; + GrGLSLVarying fCoverageTimesWind; }; #endif diff --git a/src/gpu/ccpr/GrCCTriangleShader.cpp b/src/gpu/ccpr/GrCCTriangleShader.cpp index e086201b42..8135313965 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.cpp +++ b/src/gpu/ccpr/GrCCTriangleShader.cpp @@ -22,7 +22,8 @@ void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); } -void GrCCTriangleShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, +void GrCCTriangleShader::onEmitFragmentCode(const GrCCCoverageProcessor&, + GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); } @@ -106,7 +107,8 @@ void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandl code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind); } -void GrCCTriangleCornerShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, +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: // diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h index 5f33b077cd..6dae8df497 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.h +++ b/src/gpu/ccpr/GrCCTriangleShader.h @@ -19,7 +19,8 @@ class GrCCTriangleShader : public GrCCCoverageProcessor::Shader { void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; + void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, + const char* outputCoverage) const override; GrGLSLVarying fCoverageTimesWind; }; @@ -34,7 +35,8 @@ class GrCCTriangleCornerShader : public GrCCCoverageProcessor::Shader { 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(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const 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}; |