aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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;
};