CCPR: Move flatness checks from geometry shaders to CPU

Vertex shaders can't discard geometry.

Bug: skia:
Change-Id: Iec22a103b7a4eda9a59fd99e48644183b0880f5f
Reviewed-on: https://skia-review.googlesource.com/82260
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2017-12-07 12:47:02 -07:00 committed by Skia Commit-Bot
parent 032d924ba8
commit 43646533fa
10 changed files with 58 additions and 103 deletions

View File

@ -117,17 +117,11 @@ public:
};
virtual GeometryType getGeometryType() const = 0;
virtual int getNumInputPoints() const = 0;
// Returns the number of independent geometric segments to generate for the render pass
// (number of wedges for a hull, number of edges, or number of corners.)
virtual int getNumSegments() const = 0;
// Determines the winding direction of the primitive. The subclass must write a value of
// either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
virtual void emitWind(GrGLSLShaderBuilder*, const char* pts,
const char* outputWind) const = 0;
union GeometryVars {
struct {
const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
@ -223,6 +217,10 @@ private:
// accidentally bleed into neighbor pixels.
static constexpr float kAABloatRadius = 0.491111f;
int numInputPoints() const {
return RenderPassIsCubic(fRenderPass) ? 4 : 3;
}
static GrGLSLPrimitiveProcessor* CreateGSImpl(std::unique_ptr<Shader>);
const RenderPass fRenderPass;

View File

@ -49,11 +49,11 @@ protected:
using InputType = GrGLSLGeometryBuilder::InputType;
using OutputType = GrGLSLGeometryBuilder::OutputType;
int numPts = fShader->getNumInputPoints();
SkASSERT(3 == numPts || 4 == numPts);
int numInputPoints = proc.numInputPoints();
SkASSERT(3 == numInputPoints || 4 == numInputPoints);
g->codeAppendf("float%ix2 pts = float%ix2(", numPts, numPts);
for (int i = 0; i < numPts; ++i) {
g->codeAppendf("float%ix2 pts = float%ix2(", numInputPoints, numInputPoints);
for (int i = 0; i < numInputPoints; ++i) {
g->codeAppend (i ? ", " : "");
g->codeAppendf("sk_in[%i].sk_Position.xy", i);
}
@ -61,7 +61,11 @@ protected:
GrShaderVar wind("wind", kHalf_GrSLType);
g->declareGlobal(wind);
fShader->emitWind(g, "pts", wind.c_str());
g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], pts[0] - pts[2]));");
if (4 == numInputPoints) {
g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], pts[0] - pts[3]));");
}
g->codeAppendf("%s = sign(area_x2);", wind.c_str());
SkString emitVertexFn;
SkSTArray<2, GrShaderVar> emitArgs;
@ -85,12 +89,8 @@ protected:
Shader::GeometryVars vars;
fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), vars);
int numInputPoints = fShader->getNumInputPoints();
SkASSERT(3 == numInputPoints || 4 == numInputPoints);
InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
: InputType::kLinesAdjacency;
g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
}
@ -117,7 +117,6 @@ public:
const char* hullPts = vars.fHullVars.fAlternatePoints;
if (!hullPts) {
SkASSERT(fShader->getNumInputPoints() == numSides);
hullPts = "pts";
}

View File

@ -9,25 +9,6 @@
#include "glsl/GrGLSLFragmentShaderBuilder.h"
void GrCCPRCubicShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
const char* outputWind) const {
s->codeAppendf("float area_times_2 = determinant(float3x3(1, %s[0], "
"1, %s[2], "
"0, %s[3] - %s[1]));",
pts, pts, pts, pts);
// Drop curves that are nearly flat. The KLM math becomes unstable in this case.
s->codeAppendf("if (2 * abs(area_times_2) < length(%s[3] - %s[0])) {", pts, pts);
#ifndef SK_BUILD_FOR_MAC
s->codeAppend ( "return;");
#else
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
s->codeAppend ( "area_times_2 = 0;");
#endif
s->codeAppend ("}");
s->codeAppendf("%s = sign(area_times_2);", outputWind);
}
void GrCCPRCubicShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* segmentId, const char* wind,
GeometryVars* vars) const {

View File

@ -31,10 +31,6 @@ public:
protected:
GrCCPRCubicShader(CubicType cubicType) : fCubicType(cubicType) {}
int getNumInputPoints() const final { return 4; }
void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* outputWind) const final;
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* wind, GeometryVars*) const final;

View File

@ -58,7 +58,7 @@ static inline float dot(const Sk2f& a, const Sk2f& b) {
}
static inline bool are_collinear(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
static constexpr float kFlatnessTolerance = 16; // 1/16 of a pixel.
static constexpr float kFlatnessTolerance = 4; // 1/4 of a pixel.
// Area (times 2) of the triangle.
Sk2f a = (p0 - p1) * SkNx_shuffle<1,0>(p1 - p2);
@ -99,28 +99,17 @@ void GrCCPRGeometry::quadraticTo(const SkPoint& devP0, const SkPoint& devP1) {
Sk2f p2 = Sk2f::Load(&devP1);
fCurrFanPoint = devP1;
// Don't send curves to the GPU if we know they are flat (or just very small).
if (are_collinear(p0, p1, p2)) {
p2.store(&fPoints.push_back());
fVerbs.push_back(Verb::kLineTo);
return;
}
this->appendMonotonicQuadratics(p0, p1, p2);
}
inline void GrCCPRGeometry::appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1,
const Sk2f& p2, bool allowChop) {
SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
const Sk2f& p2) {
Sk2f tan0 = p1 - p0;
Sk2f tan1 = p2 - p1;
// This should almost always be this case for well-behaved curves in the real world.
if (!allowChop || is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
p1.store(&fPoints.push_back());
p2.store(&fPoints.push_back());
fVerbs.push_back(Verb::kMonotonicQuadraticTo);
++fCurrContourTallies.fQuadratics;
if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
this->appendSingleMonotonicQuadratic(p0, p1, p2);
return;
}
@ -148,12 +137,25 @@ inline void GrCCPRGeometry::appendMonotonicQuadratics(const Sk2f& p0, const Sk2f
Sk2f p12 = SkNx_fma(t, tan1, p1);
Sk2f p012 = lerp(p01, p12, t);
p01.store(&fPoints.push_back());
p012.store(&fPoints.push_back());
p12.store(&fPoints.push_back());
this->appendSingleMonotonicQuadratic(p0, p01, p012);
this->appendSingleMonotonicQuadratic(p012, p12, p2);
}
inline void GrCCPRGeometry::appendSingleMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1,
const Sk2f& p2) {
SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
// Don't send curves to the GPU if we know they are nearly flat (or just very small).
if (are_collinear(p0, p1, p2)) {
p2.store(&fPoints.push_back());
fVerbs.push_back(Verb::kLineTo);
return;
}
p1.store(&fPoints.push_back());
p2.store(&fPoints.push_back());
fVerbs.push_back_n(2, Verb::kMonotonicQuadraticTo);
fCurrContourTallies.fQuadratics += 2;
fVerbs.push_back(Verb::kMonotonicQuadraticTo);
++fCurrContourTallies.fQuadratics;
}
using ExcludedTerm = GrPathUtils::ExcludedTerm;
@ -295,7 +297,7 @@ void GrCCPRGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const S
Sk2f p3 = Sk2f::Load(&devP3);
fCurrFanPoint = devP3;
// Don't crunch on the curve and inflate geometry if it is already flat (or just very small).
// Don't crunch on the curve and inflate geometry if it is nearly flat (or just very small).
if (are_collinear(p0, p1, p2) &&
are_collinear(p1, p2, p3) &&
are_collinear(p0, (p1 + p2) * .5f, p3)) {
@ -524,6 +526,15 @@ void GrCCPRGeometry::appendMonotonicCubics(const Sk2f& p0, const Sk2f& p1, const
}
SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
// Don't send curves to the GPU if we know they are nearly flat (or just very small).
// Since the cubic segment is known to be convex at this point, our flatness check is simple.
if (are_collinear(p0, (p1 + p2) * .5f, p3)) {
p3.store(&fPoints.push_back());
fVerbs.push_back(Verb::kLineTo);
return;
}
p1.store(&fPoints.push_back());
p2.store(&fPoints.push_back());
p3.store(&fPoints.push_back());
@ -561,7 +572,11 @@ void GrCCPRGeometry::appendCubicApproximation(const Sk2f& p0, const Sk2f& p1, co
return;
}
this->appendMonotonicQuadratics(p0, c, p3, SkToBool(maxSubdivisions));
if (maxSubdivisions) {
this->appendMonotonicQuadratics(p0, c, p3);
} else {
this->appendSingleMonotonicQuadratic(p0, c, p3);
}
}
GrCCPRGeometry::PrimitiveTallies GrCCPRGeometry::endContour() {

View File

@ -93,8 +93,8 @@ public:
PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour.
private:
inline void appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
bool allowChop = true);
inline void appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
inline void appendSingleMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
using AppendCubicFn = void(GrCCPRGeometry::*)(const Sk2f& p0, const Sk2f& p1,
const Sk2f& p2, const Sk2f& p3,

View File

@ -9,22 +9,6 @@
#include "glsl/GrGLSLFragmentShaderBuilder.h"
void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
const char* outputWind) const {
s->codeAppendf("float area_times_2 = determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0]));",
pts, pts, pts, pts);
// Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
s->codeAppendf("if (2 * abs(area_times_2) < length(%s[2] - %s[0])) {", pts, pts);
#ifndef SK_BUILD_FOR_MAC
s->codeAppend ( "return;");
#else
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
s->codeAppend ( "area_times_2 = 0;");
#endif
s->codeAppend ("}");
s->codeAppendf("%s = sign(area_times_2);", outputWind);
}
void GrCCPRQuadraticShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
const char* segmentId, const char* wind,
GeometryVars* vars) const {

View File

@ -22,10 +22,6 @@
*/
class GrCCPRQuadraticShader : public GrCCPRCoverageProcessor::Shader {
protected:
int getNumInputPoints() const final { return 3; }
void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* outputWind) const final;
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
const char* wind, GeometryVars*) const final;

View File

@ -10,12 +10,6 @@
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLVertexGeoBuilder.h"
void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
const char* outputWind) const {
s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));",
outputWind, pts, pts, pts, pts);
}
GrCCPRTriangleHullShader::WindHandling
GrCCPRTriangleHullShader::onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
const char* /*position*/, const char* /*coverage*/,

View File

@ -10,24 +10,14 @@
#include "ccpr/GrCCPRCoverageProcessor.h"
/**
* This class renders the coverage of triangles. Triangles are rendered in three passes, as
* described below.
*/
class GrCCPRTriangleShader : public GrCCPRCoverageProcessor::Shader {
public:
int getNumInputPoints() const final { return 3; }
int getNumSegments() const final { return 3; } // 3 wedges, 3 edges, 3 corners.
void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* outputWind) const final;
};
/**
* Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
* is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
* convex hull of those boxes.)
*/
class GrCCPRTriangleHullShader : public GrCCPRTriangleShader {
class GrCCPRTriangleHullShader : public GrCCPRCoverageProcessor::Shader {
GeometryType getGeometryType() const override { return GeometryType::kHull; }
int getNumSegments() const final { return 3; }
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) override;
@ -39,8 +29,9 @@ class GrCCPRTriangleHullShader : public GrCCPRTriangleShader {
* each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
* coverage=-1 on the outside edge to coverage=0 on the inside edge.
*/
class GrCCPRTriangleEdgeShader : public GrCCPRTriangleShader {
class GrCCPRTriangleEdgeShader : public GrCCPRCoverageProcessor::Shader {
GeometryType getGeometryType() const override { return GeometryType::kEdges; }
int getNumSegments() const final { return 3; }
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
const char* coverage, const char* wind) override;
@ -54,8 +45,9 @@ class GrCCPRTriangleEdgeShader : public GrCCPRTriangleShader {
* done previously so that it takes into account the region that is outside both edges at
* the same time.
*/
class GrCCPRTriangleCornerShader : public GrCCPRTriangleShader {
class GrCCPRTriangleCornerShader : public GrCCPRCoverageProcessor::Shader {
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
int getNumSegments() const final { return 3; }
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
const char* wind, GeometryVars*) const override;