diff options
-rw-r--r-- | gm/sharedcorners.cpp | 158 | ||||
-rw-r--r-- | gn/gm.gni | 1 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 37 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 21 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 56 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 112 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.cpp | 7 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCubicShader.h | 3 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.cpp | 7 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCQuadraticShader.h | 3 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.h | 27 |
11 files changed, 350 insertions, 82 deletions
diff --git a/gm/sharedcorners.cpp b/gm/sharedcorners.cpp new file mode 100644 index 0000000000..b91a2757f7 --- /dev/null +++ b/gm/sharedcorners.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "sk_tool_utils.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPoint.h" +#include <array> +#include <vector> + +namespace skiagm { + +static constexpr int kPadSize = 20; +static constexpr int kBoxSize = 100; +static constexpr SkPoint kJitters[] = {{0, 0}, {.5f, .5f}, {2/3.f, 1/3.f}}; + +// Tests various corners of different angles falling on the same pixel, particularly to ensure +// analytic AA is working properly. +class SharedCornersGM : public GM { +public: + SharedCornersGM() { + this->setBGColor(sk_tool_utils::color_to_565(0xFF1A65D7)); + } + +protected: + SkString onShortName() override { + return SkString("sharedcorners"); + } + + SkISize onISize() override { + constexpr int numRows = 3 * 2; + constexpr int numCols = (1 + SK_ARRAY_COUNT(kJitters)) * 2; + return SkISize::Make(numCols * (kBoxSize + kPadSize) + kPadSize, + numRows * (kBoxSize + kPadSize) + kPadSize); + } + + void onOnceBeforeDraw() override { + fFillPaint.setColor(SK_ColorWHITE); + fFillPaint.setAntiAlias(true); + + fWireFramePaint = fFillPaint; + fWireFramePaint.setStyle(SkPaint::kStroke_Style); + + } + + void onDraw(SkCanvas* canvas) override { + canvas->translate(kPadSize, kPadSize); + canvas->save(); + + // Adjacent rects. + this->drawTriangleBoxes(canvas, + {{0, 0}, {40, 0}, {80, 0}, {120, 0}, + {0, 20}, {40, 20}, {80, 20}, {120, 20}, + {40, 40}, {80, 40}, + {40, 60}, {80, 60}}, + {{{0, 1, 4}}, {{1, 5, 4}}, + {{5, 1, 6}}, {{1, 2, 6}}, + {{2, 3, 6}}, {{3, 7, 6}}, + {{8, 5, 9}}, {{5, 6, 9}}, + {{10, 8, 11}}, {{8, 9, 11}}}); + + // Obtuse angles. + this->drawTriangleBoxes(canvas, + {{ 0, 0}, {10, 0}, {20, 0}, + { 0, 2}, {20, 2}, + {10, 4}, + { 0, 6}, {20, 6}, + { 0, 8}, {10, 8}, {20, 8}}, + {{{3, 1, 4}}, {{4, 5, 3}}, {{6, 5, 7}}, {{7, 9, 6}}, + {{0, 1, 3}}, {{1, 2, 4}}, + {{3, 5, 6}}, {{5, 4, 7}}, + {{6, 9, 8}}, {{9, 7, 10}}}); + + canvas->restore(); + canvas->translate((kBoxSize + kPadSize) * 4, 0); + + // Right angles. + this->drawTriangleBoxes(canvas, + {{0, 0}, {-1, 0}, {0, -1}, {1, 0}, {0, 1}}, + {{{0, 1, 2}}, {{0, 2, 3}}, {{0, 3, 4}}, {{0, 4, 1}}}); + + // Acute angles. + SkRandom rand; + std::vector<SkPoint> pts; + std::vector<std::array<int, 3>> indices; + SkScalar theta = 0; + pts.push_back({0, 0}); + while (theta < 2*SK_ScalarPI) { + pts.push_back({SkScalarCos(theta), SkScalarSin(theta)}); + if (pts.size() > 2) { + indices.push_back({{0, (int)pts.size() - 2, (int)pts.size() - 1}}); + } + theta += rand.nextRangeF(0, SK_ScalarPI/3); + } + indices.push_back({{0, (int)pts.size() - 1, 1}}); + this->drawTriangleBoxes(canvas, pts, indices); + } + + void drawTriangleBoxes(SkCanvas* canvas, const std::vector<SkPoint>& points, + const std::vector<std::array<int, 3>>& triangles) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.setIsVolatile(true); + for (const std::array<int, 3>& triangle : triangles) { + path.moveTo(points[triangle[0]]); + path.lineTo(points[triangle[1]]); + path.lineTo(points[triangle[2]]); + path.close(); + } + SkScalar scale = kBoxSize / SkTMax(path.getBounds().height(), path.getBounds().width()); + path.transform(SkMatrix::MakeScale(scale, scale)); + + this->drawRow(canvas, path); + canvas->translate(0, kBoxSize + kPadSize); + + SkMatrix rot; + rot.setRotate(45, path.getBounds().centerX(), path.getBounds().centerY()); + path.transform(rot); + this->drawRow(canvas, path); + canvas->translate(0, kBoxSize + kPadSize); + + rot.setRotate(-45 - 69.38111f, path.getBounds().centerX(), path.getBounds().centerY()); + path.transform(rot); + this->drawRow(canvas, path); + canvas->translate(0, kBoxSize + kPadSize); + } + + void drawRow(SkCanvas* canvas, const SkPath& path) { + SkAutoCanvasRestore acr(canvas, true); + const SkRect& bounds = path.getBounds(); + canvas->translate((kBoxSize - bounds.width()) / 2 - bounds.left(), + (kBoxSize - bounds.height()) / 2 - bounds.top()); + + canvas->drawPath(path, fWireFramePaint); + canvas->translate(kBoxSize + kPadSize, 0); + + for (SkPoint jitter : kJitters) { + { + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(jitter.x(), jitter.y()); + canvas->drawPath(path, fFillPaint); + } + canvas->translate(kBoxSize + kPadSize, 0); + } + } + + SkPaint fWireFramePaint; + SkPaint fFillPaint; +}; + +DEF_GM(return new SharedCornersGM;) + +} @@ -272,6 +272,7 @@ gm_sources = [ "$_gm/shallowgradient.cpp", "$_gm/shapes.cpp", "$_gm/shapes_as_paths.cpp", + "$_gm/sharedcorners.cpp", "$_gm/showmiplevels.cpp", "$_gm/simpleaaclip.cpp", "$_gm/simple_magnification.cpp", diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index efe003dd60..1d570dca06 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -96,6 +96,43 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVerte s->codeAppendf("}"); } +void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGeoBuilder* s, + const char* leftDir, + const char* rightDir, + const char* outputAttenuation) { + // obtuseness = cos(corner_angle) if corner_angle > 90 degrees + // 0 if corner_angle <= 90 degrees + s->codeAppendf("half obtuseness = max(dot(%s, %s), 0);", leftDir, rightDir); + + // axis_alignedness = 1 when the leftDir/rightDir bisector is aligned with the x- or y-axis + // 0 when the bisector falls on a 45 degree angle + // (i.e. 1 - tan(angle_to_nearest_axis)) + s->codeAppendf("half2 abs_bisect = abs(%s - %s);", leftDir, rightDir); + s->codeAppend ("half axis_alignedness = 1 - min(abs_bisect.y, abs_bisect.x) / " + "max(abs_bisect.x, abs_bisect.y);"); + + // ninety_degreesness = sin^2(corner_angle) + // sin^2 just because... it's always positive and the results looked better than plain sine... ? + s->codeAppendf("half ninety_degreesness = determinant(half2x2(%s, %s));", leftDir, rightDir); + s->codeAppend ("ninety_degreesness = ninety_degreesness * ninety_degreesness;"); + + // The below formula is not smart. It was just arrived at by considering the following + // observations: + // + // 1. 90-degree, axis-aligned corners have full attenuation along the bisector. + // (i.e. coverage = 1 - distance_to_corner^2) + // (i.e. outputAttenuation = 0) + // + // 2. 180-degree corners always have zero attenuation. + // (i.e. coverage = 1 - distance_to_corner) + // (i.e. outputAttenuation = 1) + // + // 3. 90-degree corners whose bisector falls on a 45 degree angle also do not attenuate. + // (i.e. outputAttenuation = 1) + s->codeAppendf("%s = max(obtuseness, axis_alignedness * ninety_degreesness);", + outputAttenuation); +} + int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f, const char* samplesName) { // Standard DX11 sample locations. diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index 5c10630f49..a60a38ee5e 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -150,10 +150,11 @@ public: GeometryVars*) const {} void emitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, - SkString* code, const char* position, const char* inputCoverage, - const char* wind) { + SkString* code, const char* position, const char* coverage, + const char* attenuatedCoverage, const char* wind) { SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope); - this->onEmitVaryings(varyingHandler, scope, code, position, inputCoverage, wind); + this->onEmitVaryings(varyingHandler, scope, code, position, coverage, + attenuatedCoverage, wind); } void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*, @@ -183,16 +184,24 @@ public: const char* bloatDir2, const char* outputCoverages); + // Corner boxes require an additional "attenuation" varying that is multiplied by the + // regular (linearly-interpolated) coverage. This function calculates the attenuation value + // to use in the single, outermost vertex. The remaining three vertices of the corner box + // all use an attenuation value of 1. + static void CalcCornerCoverageAttenuation(GrGLSLVertexGeoBuilder*, const char* leftDir, + const char* rightDir, + const char* outputAttenuation); + virtual ~Shader() {} 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. // - // NOTE: the coverage input is only relevant for triangles. Otherwise it is null. + // NOTE: the coverage inputs are only relevant for triangles. Otherwise they are null. virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, - const char* wind) = 0; + const char* position, const char* coverage, + const char* attenuatedCoverage, const char* wind) = 0; // Emits the fragment code that calculates a pixel's signed coverage value. virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index b210aa1615..dd3d4890e1 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -80,10 +80,15 @@ protected: RenderPass::kTriangleCorners == proc.fRenderPass) { coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); } + const char* attenuatedCoverage = nullptr; + if (RenderPass::kTriangleCorners == proc.fRenderPass) { + attenuatedCoverage = emitArgs.emplace_back("attenuated_coverage", + kHalf2_GrSLType).c_str(); + } g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { SkString fnBody; fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody, - position, coverage, wind.c_str()); + position, coverage, attenuatedCoverage, wind.c_str()); g->emitVertex(&fnBody, position, rtAdjust); return fnBody; }().c_str(), &emitVertexFn); @@ -218,7 +223,7 @@ public: /** * Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates - * coverage ramps that fix up the coverage values written by GSTriangleImpl. + * coverage and attenuation ramps to fix up the coverage values written by GSTriangleImpl. */ class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl { public: @@ -236,36 +241,55 @@ public: g->codeAppendf("float2 right = pts[(sk_InvocationID + (%s > 0 ? 1 : 2)) %% 3];", wind.c_str()); + g->codeAppend ("float2 leftdir = corner - left;"); + g->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);"); + + g->codeAppend ("float2 rightdir = right - corner;"); + g->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);"); + // Find "outbloat" and "crossbloat" at our corner. The outbloat points diagonally out of the - // triangle, in the direction that should ramp to zero coverage. The crossbloat runs - // perpindicular to outbloat, and ramps from left-edge coverage to right-edge coverage. - g->codeAppend ("float2 leftdir = normalize(corner - left);"); - g->codeAppend ("float2 rightdir = normalize(right - corner);"); + // triangle, in the direction that should ramp to zero coverage with attenuation. The + // crossbloat runs perpindicular to outbloat. g->codeAppend ("float2 outbloat = float2(leftdir.x > rightdir.x ? +1 : -1, " "leftdir.y > rightdir.y ? +1 : -1);"); g->codeAppend ("float2 crossbloat = float2(-outbloat.y, +outbloat.x);"); g->codeAppend ("half2 left_coverages; {"); - Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "outbloat", "crossbloat", + Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "-outbloat", "-crossbloat", "left_coverages"); g->codeAppend ("}"); g->codeAppend ("half2 right_coverages; {"); - Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner", "right", "outbloat", "-crossbloat", + Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner", "right", "-outbloat", "crossbloat", "right_coverages"); g->codeAppend ("}"); - // Emit a corner box that erases whatever coverage was written previously, and replaces it - // using linearly-interpolated values that ramp to zero in bloat vertices that fall outside - // the triangle. + g->codeAppend ("half attenuation; {"); + Shader::CalcCornerCoverageAttenuation(g, "leftdir", "rightdir", "attenuation"); + g->codeAppend ("}"); + + // Emit a corner box. The first coverage argument erases the values that were written + // previously by the hull and edge geometry. The second pair are multiplied together by the + // fragment shader. They ramp to 0 with attenuation in the direction of outbloat, and + // linearly from left-edge coverage to right-edge coverage in the direction of crossbloat. // // NOTE: Since this is not a linear mapping, it is important that the box's diagonal shared - // edge points out of the triangle as much as possible. - g->codeAppendf("%s(corner - crossbloat * bloat, -right_coverages[1]);", emitVertexFn); + // 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]);", emitVertexFn); - g->codeAppendf("%s(corner - outbloat * bloat, 0);", emitVertexFn); - g->codeAppendf("%s(corner + crossbloat * bloat, -left_coverages[1]);", emitVertexFn); + "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); } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index 28ed6fc319..9fbac5a64f 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -28,6 +28,11 @@ protected: this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); } + struct Coverages { + const char* fCoverage = nullptr; // half + const char* fAttenuatedCoverage = nullptr; // half2 + }; + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>(); @@ -63,13 +68,15 @@ protected: #endif v->defineConstant("bloat", bloat); - const char* coverage = this->emitVertexPosition(proc, v, gpArgs); + Coverages coverages; + this->emitVertexPosition(proc, v, gpArgs, &coverages); SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType()); GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; SkString varyingCode; fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, - gpArgs->fPositionVar.c_str(), coverage, "wind"); + gpArgs->fPositionVar.c_str(), coverages.fCoverage, + coverages.fAttenuatedCoverage, "wind"); v->codeAppend(varyingCode.c_str()); varyingHandler->emitAttributes(proc); @@ -79,8 +86,8 @@ protected: fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); } - virtual const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, - GrGPArgs*) const = 0; + virtual void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, GrGPArgs*, + Coverages* outCoverages) const = 0; virtual ~VSImpl() {} @@ -187,9 +194,9 @@ static constexpr uint16_t kTriangleIndicesAsStrips[] = { 10, 9, 11, 14, 12, 13, kRestartStrip, // First edge. 16, 15, 17, 20, 18, 19, kRestartStrip, // Second edge. 22, 21, 23, 26, 24, 25, kRestartStrip, // Third edge. - 27, 28, 30, 29, kRestartStrip, // First corner. - 31, 32, 34, 33, kRestartStrip, // Second corner. - 35, 36, 38, 37 // Third corner. + 28, 27, 29, 30, kRestartStrip, // First corner. + 32, 31, 33, 34, kRestartStrip, // Second corner. + 36, 35, 37, 38 // Third corner. }; static constexpr uint16_t kTriangleIndicesAsTris[] = { @@ -223,16 +230,16 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = { 26, 25, 24, // First corner. - 27, 28, 30, - 28, 29, 30, + 28, 27, 29, + 27, 30, 29, // Second corner. - 31, 32, 34, - 32, 33, 34, + 32, 31, 33, + 31, 34, 33, // Third corner. - 35, 36, 38, - 36, 37, 38, + 36, 35, 37, + 35, 38, 37, }; GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); @@ -300,8 +307,8 @@ public: VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides) : VSImpl(std::move(shader)), fNumSides(numSides) {} - const char* emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) const override { + void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v, + GrGPArgs* gpArgs, Coverages* outCoverages) const override { Shader::GeometryVars vars; fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars); @@ -344,22 +351,26 @@ 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 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); - // For corner boxes, we hack 'left_right_notequal' to [true, true]. - // This causes the upcoming code to always rotate, which is the right - // thing for corners. - v->codeAppendf( "left_right_notequal = bool2(true, true);"); - - // In corner boxes, all 4 coverage values will not map linearly, so - // it is important to rotate the box so its diagonal shared edge - // points out of the triangle, in the direction that ramps to zero. - v->codeAppend ( "float2 bisect = normalize(corner - right) +" - "normalize(corner - left);"); - v->codeAppend ( "if (sign(bisect) == sign(leftbloat)) {"); - v->codeAppend ( "bloatdir = float2(+bloatdir.y, -bloatdir.x);"); - v->codeAppend ( "}"); + // 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 ("}"); } @@ -392,12 +403,14 @@ public: v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - // For triangles, we also emit coverage in order to handle edges and corners. - const char* coverage = nullptr; + // 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;"); + // Corner boxes require attenuation. + v->codeAppend ("half2 attenuated_coverage = half2(0);"); + v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge OR corner? proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit | kVertexData_IsCornerBit); @@ -406,20 +419,31 @@ public: v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - // Corner boxes erase whatever coverage was written previously, and - // replace it with linearly-interpolated values that ramp to zero in - // the diagonal that points out of the triangle, and ramp from - // left-edge coverage to right-edge coverage in the other diagonal. v->codeAppend ( "half left_coverage = coverage;"); + v->codeAppend ( "half right_coverage;"); Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage"); - v->codeAppend ( "coverage = (1 == bloatidx) ? -1 : 0;"); - v->codeAppend ( "if (((bloatidx + 3) & 3) < 2) {"); - v->codeAppend ( "coverage -= left_coverage;"); + + v->codeAppend ( "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 ( "coverage -= right_coverage;"); + v->codeAppend ( "if (bloatidx >= 2) {"); + v->codeAppend ( "attenuated_coverage.x += left_coverage;"); v->codeAppend ( "}"); v->codeAppend ("}"); @@ -429,10 +453,9 @@ public: v->codeAppend ( "coverage = -1 - coverage;"); v->codeAppend ("}"); - coverage = "coverage"; + outCoverages->fCoverage = "coverage"; + outCoverages->fAttenuatedCoverage = "attenuated_coverage"; } - - return coverage; } private: @@ -463,8 +486,8 @@ class VSCornerImpl : public GrCCCoverageProcessor::VSImpl { public: VSCornerImpl(std::unique_ptr<Shader> shader) : VSImpl(std::move(shader)) {} - const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) const override { + 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); @@ -474,7 +497,6 @@ public: v->codeAppend ("vertex.y += (0 == (sk_VertexID & 1)) ? -bloat : +bloat;"); gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - return nullptr; // Corner vertices don't have an initial coverage value. } }; diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp index 5ae51c7d9b..066f086a58 100644 --- a/src/gpu/ccpr/GrCCCubicShader.cpp +++ b/src/gpu/ccpr/GrCCCubicShader.cpp @@ -79,9 +79,10 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, - const char* position, const char* inputCoverage, - const char* /*wind*/) { - SkASSERT(!inputCoverage); + const char* position, const char* coverage, + const char* attenuatedCoverage, const char* /*wind*/) { + SkASSERT(!coverage); + SkASSERT(!attenuatedCoverage); fKLMD.reset(kFloat4_GrSLType, scope); varyingHandler->addVarying("klmd", &fKLMD); diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h index 063549264a..dff7f6bbf5 100644 --- a/src/gpu/ccpr/GrCCCubicShader.h +++ b/src/gpu/ccpr/GrCCCubicShader.h @@ -29,7 +29,8 @@ protected: GeometryVars*) const {} void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) final; + const char* position, const char* coverage, const char* attenuatedCoverage, + const char* wind) final; virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0; void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp index 090e29f4c3..16fd174d47 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.cpp +++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp @@ -35,9 +35,10 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, - const char* position, const char* inputCoverage, - const char* wind) { - SkASSERT(!inputCoverage); + 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); diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h index 0be03d33dd..a8b837489b 100644 --- a/src/gpu/ccpr/GrCCQuadraticShader.h +++ b/src/gpu/ccpr/GrCCQuadraticShader.h @@ -28,7 +28,8 @@ protected: GeometryVars*) const = 0; void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) final; + const char* position, const char* coverage, const char* attenuatedCoverage, + const char* wind) final; virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {} void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final; diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h index c7fbefd3b4..3dcb351970 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.h +++ b/src/gpu/ccpr/GrCCTriangleShader.h @@ -18,16 +18,29 @@ */ class GrCCTriangleShader : public GrCCCoverageProcessor::Shader { void onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, - SkString* code, const char* /*position*/, const char* inputCoverage, - const char* wind) override { - SkASSERT(inputCoverage); - fCoverageTimesWind.reset(kHalf_GrSLType, scope); - varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); - code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); + SkString* code, const char* position, const char* coverage, + const char* attenuatedCoverage, const char* wind) override { + if (!attenuatedCoverage) { + fCoverageTimesWind.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); + code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), coverage, wind); + } 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); + } } void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override { - f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); + if (kHalf_GrSLType == fCoverageTimesWind.type()) { + f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); + } else { + f->codeAppendf("%s = %s.x * %s.y + %s.z;", + outputCoverage, fCoverageTimesWind.fsIn(), fCoverageTimesWind.fsIn(), + fCoverageTimesWind.fsIn()); + } } GrGLSLVarying fCoverageTimesWind; |