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:
parent
032d924ba8
commit
43646533fa
@ -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;
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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*/,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user