blob: ef43f03cb86459e8779553f73fe65115afab2734 [file] [log] [blame]
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRTriangleProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"
void GrCCPRTriangleProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
GrGLSLVertexBuilder* v,
const TexelBufferHandle& pointsBuffer,
const char* atlasOffset, const char* rtAdjust,
GrGPArgs* gpArgs) const {
v->codeAppend ("highfloat2 self = ");
v->appendTexelFetch(pointsBuffer,
SkStringPrintf("%s[sk_VertexID]", proc.instanceAttrib()).c_str());
v->codeAppendf(".xy + %s;", atlasOffset);
gpArgs->fPositionVar.set(kHighFloat2_GrSLType, "self");
}
void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const {
// Prepend in_vertices at the start of the shader.
g->codePrependf("highfloat3x2 in_vertices = highfloat3x2(sk_in[0].gl_Position.xy, "
"sk_in[1].gl_Position.xy, "
"sk_in[2].gl_Position.xy);");
}
void GrCCPRTriangleProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* /*rtAdjust*/,
const char* outputWind) const {
// We will define in_vertices in defineInputVertices.
g->codeAppendf("%s = sign(determinant(highfloat2x2(in_vertices[1] - in_vertices[0], "
"in_vertices[2] - in_vertices[0])));",
outputWind);
}
void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
const char* emitVertexFn,
const char* wind,
const char* rtAdjust) const {
this->defineInputVertices(g);
int maxOutputVertices = 0;
if (GeometryType::kEdges != fGeometryType) {
maxOutputVertices += this->emitHullGeometry(g, emitVertexFn, "in_vertices", 3,
"sk_InvocationID");
}
if (GeometryType::kHulls != fGeometryType) {
g->codeAppend ("int edgeidx0 = sk_InvocationID, "
"edgeidx1 = (edgeidx0 + 1) % 3;");
g->codeAppendf("highfloat2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind);
g->codeAppendf("highfloat2 edgept1 = in_vertices[%s > 0 ? edgeidx1 : edgeidx0];", wind);
maxOutputVertices += this->emitEdgeGeometry(g, emitVertexFn, "edgept0", "edgept1");
}
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
maxOutputVertices, 3);
}
void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
const char* emitVertexFn, const char* wind,
const char* rtAdjust) const {
this->defineInputVertices(g);
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,
numVertices, 3);
}
void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody,
const char* position,
const char* /*coverage*/,
const char* wind) const {
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 {
// 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);
}