aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/sharedcorners.cpp158
-rw-r--r--gn/gm.gni1
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor.cpp37
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor.h21
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp56
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp112
-rw-r--r--src/gpu/ccpr/GrCCCubicShader.cpp7
-rw-r--r--src/gpu/ccpr/GrCCCubicShader.h3
-rw-r--r--src/gpu/ccpr/GrCCQuadraticShader.cpp7
-rw-r--r--src/gpu/ccpr/GrCCQuadraticShader.h3
-rw-r--r--src/gpu/ccpr/GrCCTriangleShader.h27
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;)
+
+}
diff --git a/gn/gm.gni b/gn/gm.gni
index 649a36efaf..590ee30db8 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -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;