aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Chris Dalton <csmartdalton@google.com>2017-09-18 22:23:53 -0600
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-09-20 13:36:33 +0000
commit71e37979d5087c06c314989b2a71fae04da4cbea (patch)
treec1b1cb243da59345e5dfffee79562149f5608c05
parentfe02b89d0a345d9a1a75d05160ca9f486faf54d6 (diff)
CCPR: Process corners analytically
Replaces the sampling-based corner approximation with a faster, smoother analytic one. The new version also only looks at the two incoming edges rather than considering the entire triangle. This paves the way for fanning with more efficient, larger polygons instead of just triangles. Bug: skia:6993 Change-Id: Ia5ab9a4ccadfba72759d52ce5e6a9cc719294459 Reviewed-on: https://skia-review.googlesource.com/48341 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
-rw-r--r--src/gpu/ccpr/GrCCPRTriangleProcessor.cpp206
-rw-r--r--src/gpu/ccpr/GrCCPRTriangleProcessor.h36
2 files changed, 112 insertions, 130 deletions
diff --git a/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp b/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
index ee25851497..ef43f03cb8 100644
--- a/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
@@ -64,42 +64,63 @@ void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuil
maxOutputVertices, 3);
}
-void GrCCPRTriangleCornerProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
- GrGLSLVertexBuilder* v,
- const TexelBufferHandle& pointsBuffer,
- const char* atlasOffset,
- const char* rtAdjust,
- GrGPArgs* gpArgs) const {
- this->INHERITED::onEmitVertexShader(proc, v, pointsBuffer, atlasOffset, rtAdjust, gpArgs);
-
- // Fetch and transform the next point in the triangle.
- v->codeAppend ("highfloat2 next = ");
- v->appendTexelFetch(pointsBuffer,
- SkStringPrintf("%s[(sk_VertexID+1) %% 3]", proc.instanceAttrib()).c_str());
- v->codeAppendf(".xy + %s;", atlasOffset);
-
- // Find the plane that gives distance from the [self -> next] edge, normalized to its AA
- // bloat width.
- v->codeAppend ("highfloat2 n = highfloat2(next.y - self.y, self.x - next.x);");
- v->codeAppendf("highfloat2 d = n * highfloat2x2(self + %f * sign(n), "
- "self - %f * sign(n));",
- kAABloatRadius, kAABloatRadius);
-
- // Clamp for when n=0. (wind=0 when n=0, so as long as we don't get Inf or NaN we are fine.)
- v->codeAppendf("%s.xy = n / max(d[0] - d[1], 1e-30);", fEdgeDistance.vsOut());
- v->codeAppendf("%s.z = -dot(%s.xy, self);", fEdgeDistance.vsOut(), fEdgeDistance.vsOut());
-
- // Emit device coords to geo shader.
- v->codeAppendf("%s = self;", fDevCoord.vsOut());
-}
-
void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
const char* emitVertexFn, const char* wind,
const char* rtAdjust) const {
this->defineInputVertices(g);
- g->codeAppend ("highfloat2 self = in_vertices[sk_InvocationID];");
- int numVertices = this->emitCornerGeometry(g, emitVertexFn, "self");
+ g->codeAppend ("highfloat2 corner = in_vertices[sk_InvocationID];");
+ g->codeAppend ("highfloat2x2 vectors = highfloat2x2("
+ "corner - in_vertices[(sk_InvocationID + 2) % 3], "
+ "corner - in_vertices[(sk_InvocationID + 1) % 3]);");
+
+ // Make sure neither vector is 0 in order to avoid a divide-by-zero. Wind will be zero anyway if
+ // this is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
+ g->codeAppendf("for (int i = 0; i < 2; ++i) {");
+ g->codeAppendf( "vectors[i] = any(notEqual(vectors[i], highfloat2(0))) ? "
+ "vectors[i] : highfloat2(1);");
+ g->codeAppendf("}");
+
+ // Find the vector that bisects the region outside the incoming edges. Each edge is responsible
+ // to subtract the outside region on its own the side of the bisector.
+ g->codeAppendf("highfloat2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
+ g->codeAppendf("highfloat2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
+ g->codeAppendf("highfloat2 bisect = dot(leftdir, rightdir) >= 0 ? leftdir + rightdir : "
+ "highfloat2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
+
+ // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge method
+ // actually finds is coverage inside a logical "AA box", one that is rotated inline with the
+ // edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set up
+ // transformations into normalized logical AA box space for both incoming edges. These will tell
+ // the fragment shader where the corner is located within each edge's AA box.
+ g->declareGlobal(fAABoxMatrices);
+ g->declareGlobal(fAABoxTranslates);
+ g->declareGlobal(fGeoShaderBisects);
+ g->codeAppendf("for (int i = 0; i < 2; ++i) {");
+ // The X component runs parallel to the edge (i.e. distance to the corner).
+ g->codeAppendf( "highfloat2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
+ g->codeAppendf( "highfloat nwidth = dot(abs(n), bloat) * 2;");
+ g->codeAppendf( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
+ g->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
+ g->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
+
+ // The Y component runs perpendicular to the edge (i.e. distance-to-edge).
+ // NOTE: once we are back in device space and bloat.x == bloat.y, we will not need to find and
+ // divide by nwidth a second time.
+ g->codeAppendf( "n = (i == 0) ? highfloat2(-n.y, n.x) : highfloat2(n.y, -n.x);");
+ g->codeAppendf( "nwidth = dot(abs(n), bloat) * 2;");
+ g->codeAppendf( "n /= nwidth;");
+ g->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
+ g->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
+
+ // Translate the bisector into logical AA box space.
+ // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
+ // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
+ g->codeAppendf( "%s[i] = -bisect * %s[i];",
+ fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
+ g->codeAppendf("}");
+
+ int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
@@ -110,86 +131,53 @@ void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody,
const char* position,
const char* /*coverage*/,
const char* wind) const {
- fnBody->appendf("%s.xy = %s[(sk_InvocationID + 1) %% 3];",
- fNeighbors.gsOut(), fDevCoord.gsIn());
- fnBody->appendf("%s.zw = %s[(sk_InvocationID + 2) %% 3];",
- fNeighbors.gsOut(), fDevCoord.gsIn());
- fnBody->appendf("%s = highfloat3x3(%s[(sk_InvocationID + 2) %% 3], "
- "%s[sk_InvocationID], "
- "%s[(sk_InvocationID + 1) %% 3]) * %s;",
- fEdgeDistances.gsOut(), fEdgeDistance.gsIn(), fEdgeDistance.gsIn(),
- fEdgeDistance.gsIn(), wind);
-
- // Otherwise, fEdgeDistances = float3x3(...) * sign(wind * rtAdjust.x * rdAdjust.z).
- GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
-
- fnBody->appendf("%s = sk_InvocationID;", fCornerIdx.gsOut());
+ fnBody->appendf("for (int i = 0; i < 2; ++i) {");
+ fnBody->appendf( "%s[i] = %s * %s[i] + %s[i];",
+ fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
+ fAABoxTranslates.c_str());
+ fnBody->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
+ fnBody->appendf("}");
}
void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
const char* outputCoverage) const {
- // FIXME: Adreno breaks if we don't put the frag coord in an intermediate highp variable.
- f->codeAppendf("highfloat2 fragcoord = sk_FragCoord.xy;");
-
- // Approximate coverage by tracking where 4 horizontal lines enter and leave the triangle.
- GrShaderVar samples("samples", kHighFloat4_GrSLType, GrShaderVar::kNonArray);
- f->declareGlobal(samples);
- f->codeAppendf("%s = fragcoord.y + highfloat4(-0.375, -0.125, 0.125, 0.375);", samples.c_str());
-
- GrShaderVar leftedge("leftedge", kHighFloat4_GrSLType, GrShaderVar::kNonArray);
- f->declareGlobal(leftedge);
- f->codeAppendf("%s = highfloat4(fragcoord.x - 0.5);", leftedge.c_str());
-
- GrShaderVar rightedge("rightedge", kHighFloat4_GrSLType, GrShaderVar::kNonArray);
- f->declareGlobal(rightedge);
- f->codeAppendf("%s = highfloat4(fragcoord.x + 0.5);", rightedge.c_str());
-
- SkString sampleEdgeFn;
- GrShaderVar edgeArg("edge_distance", kHighFloat3_GrSLType, GrShaderVar::kNonArray);
- f->emitFunction(kVoid_GrSLType, "sampleEdge", 1, &edgeArg, [&]() {
- SkString b;
- b.appendf("highfloat m = abs(%s.x) < 1e-3 ? 1e18 : -1 / %s.x;",
- edgeArg.c_str(), edgeArg.c_str());
- b.appendf("highfloat4 edge = m * (%s.y * samples + %s.z);",
- edgeArg.c_str(), edgeArg.c_str());
- b.appendf("if (%s.x <= 1e-3 || (abs(%s.x) < 1e-3 && %s.y > 0)) {",
- edgeArg.c_str(), edgeArg.c_str(), edgeArg.c_str());
- b.appendf( "%s = max(%s, edge);", leftedge.c_str(), leftedge.c_str());
- b.append ("} else {");
- b.appendf( "%s = min(%s, edge);", rightedge.c_str(), rightedge.c_str());
- b.append ("}");
- return b;
- }().c_str(), &sampleEdgeFn);
-
- // See if the previous neighbor already handled this pixel.
- f->codeAppendf("if (all(lessThan(abs(fragcoord - %s.zw), highfloat2(%f)))) {",
- fNeighbors.fsIn(), kAABloatRadius);
- // Handle the case where all 3 corners defer to the previous neighbor.
- f->codeAppendf( "if (%s != 0 || !all(lessThan(abs(fragcoord - %s.xy), highfloat2(%f)))) {",
- fCornerIdx.fsIn(), fNeighbors.fsIn(), kAABloatRadius);
- f->codeAppend ( "discard;");
- f->codeAppend ( "}");
- f->codeAppend ("}");
-
- // Erase what the hull and two edges wrote at this corner in previous shaders (the two .5's
- // for the edges and the -1 for the hull cancel each other out).
- f->codeAppendf("%s = dot(highfloat3(fragcoord, 1) * highfloat2x3(%s), highfloat2(1));",
- outputCoverage, fEdgeDistances.fsIn());
-
- // Sample the two edges at this corner.
- f->codeAppendf("%s(%s[0]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn());
- f->codeAppendf("%s(%s[1]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn());
-
- // Handle the opposite edge if the next neighbor will defer to us.
- f->codeAppendf("if (all(lessThan(abs(fragcoord - %s.xy), highfloat2(%f)))) {",
- fNeighbors.fsIn(), kAABloatRadius);
- // Erase the coverage the opposite edge wrote to this corner.
- f->codeAppendf( "%s += dot(%s[2], highfloat3(fragcoord, 1)) + 0.5;",
- outputCoverage, fEdgeDistances.fsIn());
- // Sample the opposite edge.
- f->codeAppendf( "%s(%s[2]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn());
- f->codeAppend ("}");
-
- f->codeAppendf("highfloat4 widths = max(%s - %s, 0);", rightedge.c_str(), leftedge.c_str());
- f->codeAppendf("%s += dot(widths, highfloat4(0.25));", outputCoverage);
+ // By the time we reach this shader, the pixel is in the following state:
+ //
+ // 1. The hull shader has emitted a coverage of 1.
+ // 2. Both edges have subtracted the area on their outside.
+ //
+ // This generally works, but it is a problem for corner pixels. There is a region within corner
+ // pixels that is outside both edges at the same time. This means the region has been double
+ // subtracted (once by each edge). The purpose of this shader is to fix these corner pixels.
+ //
+ // More specifically, each edge redoes its coverage analysis so that it only subtracts the
+ // outside area that falls on its own side of the bisector line.
+ //
+ // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have different
+ // AA boxes. (For an explanation of AA boxes, see comments in onEmitGeometryShader.) This means
+ // the coverage analysis will only be approximate. It seems acceptable, but if we want exact
+ // coverage we will need to switch to a more expensive model.
+ f->codeAppendf("%s = 0;", outputCoverage);
+
+ // Loop through both edges.
+ f->codeAppendf("for (int i = 0; i < 2; ++i) {");
+ f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
+ f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
+
+ // Find the point at which the bisector exits the logical AA box.
+ // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
+ f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
+ f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
+ ": d.x / bisect.x;");
+ f->codeAppendf( "half2 exit = corner + bisect * T;");
+
+ // These lines combined (and the final multiply by .5) accomplish the following:
+ // 1. Add back the area beyond the corner that was subtracted out previously.
+ // 2. Subtract out the area beyond the corner, but under the bisector.
+ // The other edge will take care of the area on its own side of the bisector.
+ f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
+ f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
+ f->codeAppendf("}");
+
+ f->codeAppendf("%s *= .5;", outputCoverage);
}
diff --git a/src/gpu/ccpr/GrCCPRTriangleProcessor.h b/src/gpu/ccpr/GrCCPRTriangleProcessor.h
index 9ac76521bf..74e714cb5f 100644
--- a/src/gpu/ccpr/GrCCPRTriangleProcessor.h
+++ b/src/gpu/ccpr/GrCCPRTriangleProcessor.h
@@ -64,32 +64,26 @@ private:
};
/**
- * This pass fixes the corner pixels of a triangle. It erases the (incorrect) coverage that was
- * written at the corners during the previous hull and edge passes, and then approximates the true
- * coverage by sampling the triangle with horizontal lines.
+ * This pass fixes the corner pixels of a triangle. It touches up the simple distance-to-edge
+ * coverage analysis done previously so that it takes into account the region that is outside both
+ * edges at the same time.
*/
class GrCCPRTriangleCornerProcessor : public GrCCPRTriangleProcessor {
public:
GrCCPRTriangleCornerProcessor()
: INHERITED(CoverageType::kShader)
- , fEdgeDistance(kHighFloat3_GrSLType)
- , fDevCoord(kHighFloat2_GrSLType)
- , fNeighbors(kHighFloat4_GrSLType)
- , fEdgeDistances(kHighFloat3x3_GrSLType)
- , fCornerIdx(kShort_GrSLType) {}
+ , fAABoxMatrices("aa_box_matrices", kHighFloat2x2_GrSLType, 2)
+ , fAABoxTranslates("aa_box_translates", kHighFloat2_GrSLType, 2)
+ , fGeoShaderBisects("bisects", kHighFloat2_GrSLType, 2)
+ , fCornerLocationInAABoxes(kHighFloat2x2_GrSLType)
+ , fBisectInAABoxes(kHighFloat2x2_GrSLType) {}
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
this->INHERITED::resetVaryings(varyingHandler);
- varyingHandler->addFlatVarying("edge_distance", &fEdgeDistance, kHigh_GrSLPrecision);
- varyingHandler->addFlatVarying("devcoord", &fDevCoord, kHigh_GrSLPrecision);
- varyingHandler->addFlatVarying("neighbors", &fNeighbors, kHigh_GrSLPrecision);
- varyingHandler->addFlatVarying("edge_distances", &fEdgeDistances, kHigh_GrSLPrecision);
- varyingHandler->addFlatVarying("corner_idx", &fCornerIdx, kLow_GrSLPrecision);
+ varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
+ varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
}
- void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
- const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
- const char* rtAdjust, GrGPArgs*) const override;
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
const char* rtAdjust) const override;
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
@@ -97,11 +91,11 @@ public:
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
private:
- GrGLSLVertToGeo fEdgeDistance;
- GrGLSLVertToGeo fDevCoord;
- GrGLSLGeoToFrag fNeighbors;
- GrGLSLGeoToFrag fEdgeDistances;
- GrGLSLGeoToFrag fCornerIdx;
+ GrShaderVar fAABoxMatrices;
+ GrShaderVar fAABoxTranslates;
+ GrShaderVar fGeoShaderBisects;
+ GrGLSLGeoToFrag fCornerLocationInAABoxes;
+ GrGLSLGeoToFrag fBisectInAABoxes;
typedef GrCCPRTriangleProcessor INHERITED;
};