aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor.cpp28
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor.h62
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp165
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp273
-rw-r--r--src/gpu/ccpr/GrCCCubicShader.cpp120
-rw-r--r--src/gpu/ccpr/GrCCCubicShader.h38
-rw-r--r--src/gpu/ccpr/GrCCQuadraticShader.cpp146
-rw-r--r--src/gpu/ccpr/GrCCQuadraticShader.h47
-rw-r--r--src/gpu/ccpr/GrCCTriangleShader.h25
9 files changed, 326 insertions, 578 deletions
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
index 10b6736f33..8e7249e9e4 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
@@ -127,26 +127,6 @@ void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGe
outputAttenuation);
}
-int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f,
- const char* samplesName) {
- // Standard DX11 sample locations.
-#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
- f->defineConstant("float2[8]", samplesName, "float2[8]("
- "float2(+1, -3)/16, float2(-1, +3)/16, float2(+5, +1)/16, float2(-3, -5)/16, "
- "float2(-5, +5)/16, float2(-7, -1)/16, float2(+3, +7)/16, float2(+7, -7)/16."
- ")");
- return 8;
-#else
- f->defineConstant("float2[16]", samplesName, "float2[16]("
- "float2(+1, +1)/16, float2(-1, -3)/16, float2(-3, +2)/16, float2(+4, -1)/16, "
- "float2(-5, -2)/16, float2(+2, +5)/16, float2(+5, +3)/16, float2(+3, -5)/16, "
- "float2(-2, +6)/16, float2( 0, -7)/16, float2(-4, -6)/16, float2(-6, +4)/16, "
- "float2(-8, 0)/16, float2(+7, -4)/16, float2(+6, +7)/16, float2(-7, -8)/16."
- ")");
- return 16;
-#endif
-}
-
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
GrProcessorKeyBuilder* b) const {
int key = (int)fRenderPass << 2;
@@ -172,16 +152,12 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad
shader = skstd::make_unique<GrCCTriangleShader>();
break;
case RenderPass::kQuadratics:
- shader = skstd::make_unique<GrCCQuadraticHullShader>();
- break;
case RenderPass::kQuadraticCorners:
- shader = skstd::make_unique<GrCCQuadraticCornerShader>();
+ shader = skstd::make_unique<GrCCQuadraticShader>();
break;
case RenderPass::kCubics:
- shader = skstd::make_unique<GrCCCubicHullShader>();
- break;
case RenderPass::kCubicCorners:
- shader = skstd::make_unique<GrCCCubicCornerShader>();
+ shader = skstd::make_unique<GrCCCubicShader>();
break;
}
return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
index d826b810c9..8b2b00392b 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.h
@@ -70,9 +70,19 @@ public:
static bool RenderPassIsCubic(RenderPass);
static const char* RenderPassName(RenderPass);
- constexpr static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) {
- return RenderPass::kTriangleCorners != renderPass ||
- caps.shaderCaps()->geometryShaderSupport();
+ static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) {
+ switch (renderPass) {
+ case RenderPass::kTriangles:
+ case RenderPass::kQuadratics:
+ case RenderPass::kCubics:
+ return true;
+ case RenderPass::kTriangleCorners:
+ case RenderPass::kQuadraticCorners:
+ case RenderPass::kCubicCorners:
+ return caps.shaderCaps()->geometryShaderSupport();
+ }
+ SK_ABORT("Invalid RenderPass");
+ return false;
}
enum class WindMethod : bool {
@@ -126,34 +136,20 @@ public:
// provides details about shape-specific geometry.
class Shader {
public:
- union GeometryVars {
- struct {
- const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
- } fHullVars;
-
- struct {
- const char* fPoint; // float2
- } fCornerVars;
-
- GeometryVars() { memset(this, 0, sizeof(*this)); }
- };
-
- // Called before generating geometry. Subclasses must fill out the applicable fields in
- // GeometryVars (if any), and may also use this opportunity to setup internal member
- // variables that will be needed during onEmitVaryings (e.g. transformation matrices).
+ // Called before generating geometry. Subclasses may set up internal member variables during
+ // this time that will be needed during onEmitVaryings (e.g. transformation matrices).
//
- // repetitionID is a 0-based index and indicates which edge or corner is being generated.
- // It will be null when generating a hull.
- virtual void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts,
- const char* repetitionID, const char* wind,
- GeometryVars*) const {}
+ // If the optional 'tighterHull' parameter is not null and gets filled out by the subclass,
+ // the the Impl will generate geometry around those points rather than the input points.
+ virtual void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* wind,
+ const char** tighterHull = nullptr) const {}
void emitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope,
SkString* code, const char* position, const char* coverage,
- const char* attenuatedCoverage, const char* wind) {
+ const char* attenuatedCoverage) {
SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope);
this->onEmitVaryings(varyingHandler, scope, code, position, coverage,
- attenuatedCoverage, wind);
+ attenuatedCoverage);
}
void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
@@ -195,12 +191,13 @@ public:
protected:
// Here the subclass adds its internal varyings to the handler and produces code to
- // initialize those varyings from a given position, input coverage value, and wind.
+ // initialize those varyings from a given position and coverage values.
//
- // NOTE: the coverage inputs are only relevant for triangles. Otherwise they are null.
+ // NOTE: the coverage values are signed appropriately for wind.
+ // 'coverage' will only be +1 or -1 on curves.
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
const char* position, const char* coverage,
- const char* attenuatedCoverage, const char* wind) = 0;
+ const char* attenuatedCoverage) = 0;
// Emits the fragment code that calculates a pixel's signed coverage value.
virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*,
@@ -213,15 +210,12 @@ public:
SkASSERT(Scope::kVertToGeo != varying.scope());
return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut();
}
-
- // Defines a global float2 array that contains MSAA sample locations as offsets from pixel
- // center. Subclasses can use this for software multisampling.
- //
- // Returns the number of samples.
- static int DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f, const char* samplesName);
};
class GSImpl;
+ class GSTriangleHullImpl;
+ class GSCurveHullImpl;
+ class GSCornerImpl;
class VSImpl;
private:
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
index 82a292cb06..907de46019 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
@@ -12,7 +12,6 @@
using InputType = GrGLSLGeometryBuilder::InputType;
using OutputType = GrGLSLGeometryBuilder::OutputType;
-using Shader = GrCCCoverageProcessor::Shader;
/**
* This class and its subclasses implement the coverage processor with geometry shaders.
@@ -21,6 +20,9 @@ class GrCCCoverageProcessor::GSImpl : public GrGLSLGeometryProcessor {
protected:
GSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
+ virtual bool hasCoverage() const { return false; }
+ virtual bool hasAttenuatedCoverage() const { return false; }
+
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
FPCoordTransformIter&& transformIter) final {
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
@@ -76,19 +78,24 @@ protected:
SkSTArray<2, GrShaderVar> emitArgs;
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
const char* coverage = nullptr;
- if (RenderPass::kTriangles == proc.fRenderPass ||
- RenderPass::kTriangleCorners == proc.fRenderPass) {
+ if (this->hasCoverage()) {
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
}
const char* attenuatedCoverage = nullptr;
- if (RenderPass::kTriangleCorners == proc.fRenderPass) {
+ if (this->hasAttenuatedCoverage()) {
attenuatedCoverage = emitArgs.emplace_back("attenuated_coverage",
kHalf2_GrSLType).c_str();
}
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
SkString fnBody;
+ if (coverage) {
+ fnBody.appendf("%s *= %s;", coverage, wind.c_str());
+ }
+ if (attenuatedCoverage) {
+ fnBody.appendf("%s.x *= %s;", attenuatedCoverage, wind.c_str());
+ }
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody,
- position, coverage, attenuatedCoverage, wind.c_str());
+ position, coverage ? coverage : wind.c_str(), attenuatedCoverage);
g->emitVertex(&fnBody, position, rtAdjust);
return fnBody;
}().c_str(), &emitVertexFn);
@@ -101,11 +108,11 @@ protected:
#endif
g->defineConstant("bloat", bloat);
- this->onEmitGeometryShader(g, wind, emitVertexFn.c_str());
+ this->onEmitGeometryShader(proc, g, wind, emitVertexFn.c_str());
}
- virtual void onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
- const char* emitVertexFn) const = 0;
+ virtual void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder*,
+ const GrShaderVar& wind, const char* emitVertexFn) const = 0;
virtual ~GSImpl() {}
@@ -124,15 +131,15 @@ protected:
*
* The final corners get touched up in a later step by GSTriangleCornerImpl.
*/
-class GSTriangleImpl : public GrCCCoverageProcessor::GSImpl {
+class GrCCCoverageProcessor::GSTriangleHullImpl : public GrCCCoverageProcessor::GSImpl {
public:
- GSTriangleImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+ GSTriangleHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+
+ bool hasCoverage() const override { return true; }
- void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
- const char* emitVertexFn) const override {
- Shader::GeometryVars vars;
- fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars);
- SkASSERT(!vars.fHullVars.fAlternatePoints);
+ void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g,
+ const GrShaderVar& wind, const char* emitVertexFn) const override {
+ fShader->emitSetupCode(g, "pts", wind.c_str());
// Visualize the input triangle as upright and equilateral, with a flat base. Paying special
// attention to wind, we can identify the points as top, bottom-left, and bottom-right.
@@ -222,24 +229,31 @@ public:
};
/**
- * Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates
- * coverage and attenuation ramps to fix up the coverage values written by GSTriangleImpl.
+ * Generates conservative rasters around corners (aka pixel-size boxes) and calculates
+ * coverage and attenuation ramps to fix up the coverage values written by the hulls.
*/
-class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl {
+class GrCCCoverageProcessor::GSCornerImpl : public GrCCCoverageProcessor::GSImpl {
public:
- GSTriangleCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+ GSCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
- void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
- const char* emitVertexFn) const override {
- Shader::GeometryVars vars;
- fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars);
- SkASSERT(!vars.fHullVars.fAlternatePoints);
+ bool hasCoverage() const override { return true; }
+ bool hasAttenuatedCoverage() const override { return true; }
- g->codeAppendf("float2 corner = pts[sk_InvocationID];");
- g->codeAppendf("float2 left = pts[(sk_InvocationID + (%s > 0 ? 2 : 1)) %% 3];",
- wind.c_str());
- g->codeAppendf("float2 right = pts[(sk_InvocationID + (%s > 0 ? 1 : 2)) %% 3];",
- wind.c_str());
+ void onEmitGeometryShader(const GrCCCoverageProcessor& proc, GrGLSLGeometryBuilder* g,
+ const GrShaderVar& wind, const char* emitVertexFn) const override {
+ fShader->emitSetupCode(g, "pts", wind.c_str());
+
+ bool isTriangle = RenderPass::kTriangleCorners == proc.fRenderPass;
+ g->codeAppendf("int corneridx = sk_InvocationID;");
+ if (!isTriangle) {
+ g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1);
+ }
+
+ g->codeAppendf("float2 corner = pts[corneridx];");
+ g->codeAppendf("float2 left = pts[(corneridx + (%s > 0 ? %i : 1)) %% %i];",
+ wind.c_str(), proc.numInputPoints() - 1, proc.numInputPoints());
+ g->codeAppendf("float2 right = pts[(corneridx + (%s > 0 ? 1 : %i)) %% %i];",
+ wind.c_str(), proc.numInputPoints() - 1, proc.numInputPoints());
g->codeAppend ("float2 leftdir = corner - left;");
g->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
@@ -275,42 +289,42 @@ public:
//
// NOTE: Since this is not a linear mapping, it is important that the box's diagonal shared
// edge points in the direction of outbloat.
- g->codeAppendf("%s(corner - crossbloat * bloat, "
- "right_coverages[1] - left_coverages[1],"
- "half2(1 + left_coverages[1], 1));", emitVertexFn);
-
- g->codeAppendf("%s(corner + outbloat * bloat, "
- "1 + left_coverages[0] + right_coverages[0],"
- "half2(0, attenuation));", emitVertexFn);
-
- g->codeAppendf("%s(corner - outbloat * bloat, "
- "-1 - left_coverages[0] - right_coverages[0],"
- "half2(1 + left_coverages[0] + right_coverages[0], 1));", emitVertexFn);
-
- g->codeAppendf("%s(corner + crossbloat * bloat, "
- "left_coverages[1] - right_coverages[1],"
- "half2(1 + right_coverages[1], 1));", emitVertexFn);
-
- g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, 3);
+ g->codeAppendf("%s(corner - crossbloat * bloat, %s, half2(1 + left_coverages[1], 1));",
+ emitVertexFn,
+ // Erase what the hull wrote previously.
+ isTriangle ? "right_coverages[1] - left_coverages[1]" : "-1");
+
+ g->codeAppendf("%s(corner + outbloat * bloat, %s, half2(0, attenuation));",
+ emitVertexFn,
+ // Erase what the hull wrote previously.
+ isTriangle ? "1 + left_coverages[0] + right_coverages[0]" : "-1");
+
+ g->codeAppendf("%s(corner - outbloat * bloat, %s, "
+ "half2(1 + left_coverages[0] + right_coverages[0], 1));",
+ emitVertexFn,
+ // Erase what the hull wrote previously.
+ isTriangle ? "-1 - left_coverages[0] - right_coverages[0]" : "-1");
+
+ g->codeAppendf("%s(corner + crossbloat * bloat, %s, half2(1 + right_coverages[1], 1));",
+ emitVertexFn,
+ // Erase what the hull wrote previously.
+ isTriangle ? "left_coverages[1] - right_coverages[1]" : "-1");
+
+ g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, isTriangle ? 3 : 2);
}
};
/**
* Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
*/
-class GSHull4Impl : public GrCCCoverageProcessor::GSImpl {
+class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
public:
- GSHull4Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
-
- void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
- const char* emitVertexFn) const override {
- Shader::GeometryVars vars;
- fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars);
+ GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
- const char* hullPts = vars.fHullVars.fAlternatePoints;
- if (!hullPts) {
- hullPts = "pts";
- }
+ void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g,
+ const GrShaderVar& wind, const char* emitVertexFn) const override {
+ const char* hullPts = "pts";
+ fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts);
// Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
// we can identify the points by their corresponding corner.
@@ -366,34 +380,6 @@ public:
}
};
-/**
- * Generates conservative rasters around corners. (See comments for RenderPass)
- */
-class GSCornerImpl : public GrCCCoverageProcessor::GSImpl {
-public:
- GSCornerImpl(std::unique_ptr<Shader> shader, int numCorners)
- : GSImpl(std::move(shader)), fNumCorners(numCorners) {}
-
- void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
- const char* emitVertexFn) const override {
- Shader::GeometryVars vars;
- fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
-
- const char* corner = vars.fCornerVars.fPoint;
- SkASSERT(corner);
-
- g->codeAppendf("%s(%s + float2(-bloat, -bloat));", emitVertexFn, corner);
- g->codeAppendf("%s(%s + float2(-bloat, +bloat));", emitVertexFn, corner);
- g->codeAppendf("%s(%s + float2(+bloat, -bloat));", emitVertexFn, corner);
- g->codeAppendf("%s(%s + float2(+bloat, +bloat));", emitVertexFn, corner);
-
- g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, fNumCorners);
- }
-
-private:
- const int fNumCorners;
-};
-
void GrCCCoverageProcessor::initGS() {
SkASSERT(Impl::kGeometryShader == fImpl);
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
@@ -424,15 +410,14 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const {
switch (fRenderPass) {
case RenderPass::kTriangles:
- return new GSTriangleImpl(std::move(shadr));
- case RenderPass::kTriangleCorners:
- return new GSTriangleCornerImpl(std::move(shadr));
+ return new GSTriangleHullImpl(std::move(shadr));
case RenderPass::kQuadratics:
case RenderPass::kCubics:
- return new GSHull4Impl(std::move(shadr));
+ return new GSCurveHullImpl(std::move(shadr));
+ case RenderPass::kTriangleCorners:
case RenderPass::kQuadraticCorners:
case RenderPass::kCubicCorners:
- return new GSCornerImpl(std::move(shadr), 2);
+ return new GSCornerImpl(std::move(shadr));
}
SK_ABORT("Invalid RenderPass");
return nullptr;
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
index e0ec4d4142..d0fc4e0f71 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
@@ -76,7 +76,7 @@ protected:
SkString varyingCode;
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
gpArgs->fPositionVar.c_str(), coverages.fCoverage,
- coverages.fAttenuatedCoverage, "wind");
+ coverages.fAttenuatedCoverage);
v->codeAppend(varyingCode.c_str());
varyingHandler->emitAttributes(proc);
@@ -131,9 +131,9 @@ static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int3
(!endptIdx ? kVertexData_InvertNegativeCoverageBit : 0));
}
-static constexpr int32_t triangle_corner_vertex_data(int32_t cornerID, int32_t bloatIdx) {
- return pack_vertex_data((cornerID + 2) % 3, (cornerID + 1) % 3, bloatIdx, cornerID,
- kVertexData_IsCornerBit);
+static constexpr int32_t corner_vertex_data(int32_t leftID, int32_t cornerID, int32_t rightID,
+ int32_t bloatIdx) {
+ return pack_vertex_data(leftID, rightID, bloatIdx, cornerID, kVertexData_IsCornerBit);
}
static constexpr int32_t kTriangleVertices[] = {
@@ -168,20 +168,20 @@ static constexpr int32_t kTriangleVertices[] = {
edge_vertex_data(2, 1, 1, 3),
edge_vertex_data(2, 1, 2, 3),
- triangle_corner_vertex_data(0, 0),
- triangle_corner_vertex_data(0, 1),
- triangle_corner_vertex_data(0, 2),
- triangle_corner_vertex_data(0, 3),
+ corner_vertex_data(2, 0, 1, 0),
+ corner_vertex_data(2, 0, 1, 1),
+ corner_vertex_data(2, 0, 1, 2),
+ corner_vertex_data(2, 0, 1, 3),
- triangle_corner_vertex_data(1, 0),
- triangle_corner_vertex_data(1, 1),
- triangle_corner_vertex_data(1, 2),
- triangle_corner_vertex_data(1, 3),
+ corner_vertex_data(0, 1, 2, 0),
+ corner_vertex_data(0, 1, 2, 1),
+ corner_vertex_data(0, 1, 2, 2),
+ corner_vertex_data(0, 1, 2, 3),
- triangle_corner_vertex_data(2, 0),
- triangle_corner_vertex_data(2, 1),
- triangle_corner_vertex_data(2, 2),
- triangle_corner_vertex_data(2, 3),
+ corner_vertex_data(1, 2, 0, 0),
+ corner_vertex_data(1, 2, 0, 1),
+ corner_vertex_data(1, 2, 0, 2),
+ corner_vertex_data(1, 2, 0, 3),
};
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
@@ -258,14 +258,24 @@ static constexpr int32_t kHull4Vertices[] = {
hull_vertex_data(3, 1, 4),
hull_vertex_data(3, 2, 4),
- // No edges for now (beziers don't use edges).
+ corner_vertex_data(3, 0, 1, 0),
+ corner_vertex_data(3, 0, 1, 1),
+ corner_vertex_data(3, 0, 1, 2),
+ corner_vertex_data(3, 0, 1, 3),
+
+ corner_vertex_data(2, 3, 0, 0),
+ corner_vertex_data(2, 3, 0, 1),
+ corner_vertex_data(2, 3, 0, 2),
+ corner_vertex_data(2, 3, 0, 3),
};
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
static constexpr uint16_t kHull4IndicesAsStrips[] = {
1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally).
- 7, 6, 8, 5, 9, 11, 10 // Second half of the hull.
+ 7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull.
+ 13, 12, 14, 15, kRestartStrip, // First corner.
+ 17, 16, 18, 19 // Second corner.
};
static constexpr uint16_t kHull4IndicesAsTris[] = {
@@ -282,6 +292,14 @@ static constexpr uint16_t kHull4IndicesAsTris[] = {
8, 5, 9,
5, 11, 9,
9, 11, 10,
+
+ // First corner.
+ 13, 12, 14,
+ 12, 15, 14,
+
+ // Second corner.
+ 17, 16, 18,
+ 16, 19, 18,
};
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
@@ -309,13 +327,8 @@ public:
void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v,
GrGPArgs* gpArgs, Coverages* outCoverages) const override {
- Shader::GeometryVars vars;
- fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars);
-
- const char* hullPts = vars.fHullVars.fAlternatePoints;
- if (!hullPts) {
- hullPts = "pts";
- }
+ const char* hullPts = "pts";
+ fShader->emitSetupCode(v, "pts", "wind", &hullPts);
// Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0].
v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
@@ -350,29 +363,27 @@ public:
v->codeAppend ("float2 bloatdir = leftbloat;");
- if (3 == fNumSides) { // Only triangles emit corner boxes.
- v->codeAppend ("float2 leftdir = corner - left;");
- v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
+ v->codeAppend ("float2 leftdir = corner - left;");
+ v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
- v->codeAppend ("float2 rightdir = right - corner;");
- v->codeAppend ("rightdir = (float2(0) != rightdir)"
- "? normalize(rightdir) : float2(1, 0);");
+ v->codeAppend ("float2 rightdir = right - corner;");
+ v->codeAppend ("rightdir = (float2(0) != rightdir)"
+ "? normalize(rightdir) : float2(1, 0);");
- v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
- proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
+ v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
+ proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
- // In corner boxes, all 4 coverage values will not map linearly.
- // Therefore it is important to align the box so its diagonal shared
- // edge points out of the triangle, in the direction that ramps to 0.
- v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
- "leftdir.y > rightdir.y ? +1 : -1);");
+ // In corner boxes, all 4 coverage values will not map linearly.
+ // Therefore it is important to align the box so its diagonal shared
+ // edge points out of the triangle, in the direction that ramps to 0.
+ v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
+ "leftdir.y > rightdir.y ? +1 : -1);");
- // For corner boxes, we hack left_right_notequal to always true. This
- // in turn causes the upcoming code to always rotate, generating all
- // 4 vertices of the corner box.
- v->codeAppendf( "left_right_notequal = bool2(true);");
- v->codeAppend ("}");
- }
+ // For corner boxes, we hack left_right_notequal to always true. This
+ // in turn causes the upcoming code to always rotate, generating all
+ // 4 vertices of the corner box.
+ v->codeAppendf( "left_right_notequal = bool2(true);");
+ v->codeAppend ("}");
// At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if
// it's a corner box). We begin with this corner's first raster vertex (leftbloat), then
@@ -382,12 +393,10 @@ public:
v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
v->codeAppend ("switch (bloatidx) {");
- if (3 == fNumSides) { // Only triangles emit corner boxes.
- v->codeAppend ( "case 3:");
- // Only corners will have bloatidx=3, and corners always rotate.
- v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
- // fallthru.
- }
+ v->codeAppend ( "case 3:");
+ // Only corners will have bloatidx=3, and corners always rotate.
+ v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
+ // fallthru.
v->codeAppend ( "case 2:");
v->codeAppendf( "if (all(left_right_notequal)) {");
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
@@ -403,48 +412,25 @@ public:
v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
- // For triangles, we also emit coverage and attenuation.
- if (3 == fNumSides) {
- // The hull has a coverage of +1 all around.
- v->codeAppend ("half coverage = +1;");
+ v->codeAppend ("half left_coverage; {");
+ Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
+ v->codeAppend ("}");
- // Corner boxes require attenuation.
- v->codeAppend ("half2 attenuated_coverage = half2(0);");
+ v->codeAppend ("half right_coverage; {");
+ Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
+ v->codeAppend ("}");
- v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge OR corner?
- proc.getAttrib(kAttribIdx_VertexData).fName,
- kVertexData_IsEdgeBit | kVertexData_IsCornerBit);
- Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage");
- v->codeAppend ("}");
+ v->codeAppend ("half attenuation; {");
+ Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
+ v->codeAppend ("}");
- v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
- proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
- v->codeAppend ( "half left_coverage = coverage;");
-
- v->codeAppend ( "half right_coverage;");
- Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir",
- "right_coverage");
-
- v->codeAppend ( "half attenuation;");
- Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
-
- // For corners, "coverage" erases the values that were written
- // previously by the hull and edge geometry.
- v->codeAppend ( "coverage = -1 - left_coverage - right_coverage;");
-
- // The x and y components of "attenuated_coverage" are multiplied
- // together by the fragment shader. They ramp to 0 with attenuation
- // in the diagonal that points out of the triangle, and linearly from
- // left-edge coverage to right in the opposite diagonal. bloatidx=0
- // is the outermost vertex; the one that has attenuation.
- v->codeAppend ( "attenuated_coverage = (0 == bloatidx)"
- "? half2(0, attenuation) : half2(1);");
- v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {");
- v->codeAppend ( "attenuated_coverage.x += right_coverage;");
- v->codeAppend ( "}");
- v->codeAppend ( "if (bloatidx >= 2) {");
- v->codeAppend ( "attenuated_coverage.x += left_coverage;");
- v->codeAppend ( "}");
+ // Hulls have a coverage of +1 all around.
+ v->codeAppend ("half coverage = +1;");
+
+ if (3 == fNumSides) {
+ v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
+ proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
+ v->codeAppend ( "coverage = left_coverage;");
v->codeAppend ("}");
v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
@@ -452,52 +438,43 @@ public:
kVertexData_InvertNegativeCoverageBit);
v->codeAppend ( "coverage = -1 - coverage;");
v->codeAppend ("}");
-
- outCoverages->fCoverage = "coverage";
- outCoverages->fAttenuatedCoverage = "attenuated_coverage";
}
- }
-private:
- const int fNumSides;
-};
+ // Corner boxes require attenuation.
+ v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
-static constexpr uint16_t kCornerIndicesAsStrips[] = {
- 0, 1, 2, 3, kRestartStrip, // First corner.
- 4, 5, 6, 7 // Second corner.
-};
-
-static constexpr uint16_t kCornerIndicesAsTris[] = {
- // First corner.
- 0, 1, 2,
- 1, 3, 2,
-
- // Second corner.
- 4, 5, 6,
- 5, 7, 6,
-};
-
-GR_DECLARE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey);
-
-/**
- * Generates conservative rasters around corners. (See comments for RenderPass)
- */
-class VSCornerImpl : public GrCCCoverageProcessor::VSImpl {
-public:
- VSCornerImpl(std::unique_ptr<Shader> shader) : VSImpl(std::move(shader)) {}
-
- void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v, GrGPArgs* gpArgs,
- Coverages* /*outCoverages*/) const override {
- Shader::GeometryVars vars;
- v->codeAppend ("int corner_id = sk_VertexID / 4;");
- fShader->emitSetupCode(v, "pts", "corner_id", "wind", &vars);
+ v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
+ proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
+ // We use coverage=-1 to erase what the hull geometry wrote.
+ v->codeAppend ( "coverage = -1;");
+ if (3 == fNumSides) {
+ // Triangle corners also have to erase what the edge geometry wrote.
+ v->codeAppend ("coverage -= left_coverage + right_coverage;");
+ }
+ // The x and y components of "attenuated_coverage" are multiplied
+ // together by the fragment shader. They ramp to 0 with attenuation in
+ // the diagonal that points out of the corner, and linearly from
+ // left-edge coverage to right in the opposite diagonal.
+ // bloatidx=0 is the outermost vertex; the one that has attenuation.
+ v->codeAppend ( "attenuated_coverage = (0 == bloatidx)"
+ "? half2(0, attenuation) : half2(1);");
+ v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {");
+ v->codeAppend ( "attenuated_coverage.x += right_coverage;");
+ v->codeAppend ( "}");
+ v->codeAppend ( "if (bloatidx >= 2) {");
+ v->codeAppend ( "attenuated_coverage.x += left_coverage;");
+ v->codeAppend ( "}");
+ v->codeAppend ("}");
- v->codeAppendf("float2 vertex = %s;", vars.fCornerVars.fPoint);
- v->codeAppend ("vertex.x += (0 == (sk_VertexID & 2)) ? -bloat : +bloat;");
- v->codeAppend ("vertex.y += (0 == (sk_VertexID & 1)) ? -bloat : +bloat;");
+ v->codeAppend ("coverage *= wind;");
+ outCoverages->fCoverage = "coverage";
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
+ v->codeAppend ("attenuated_coverage.x *= wind;");
+ outCoverages->fAttenuatedCoverage = "attenuated_coverage";
}
+
+private:
+ const int fNumSides;
};
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
@@ -528,9 +505,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
break;
}
- case RenderPass::kTriangleCorners:
- SK_ABORT("RenderPass::kTriangleCorners is unused by VSImpl.");
-
case RenderPass::kQuadratics:
case RenderPass::kCubics: {
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
@@ -553,24 +527,10 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
break;
}
+ case RenderPass::kTriangleCorners:
case RenderPass::kQuadraticCorners:
- case RenderPass::kCubicCorners: {
- GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey);
- if (caps.usePrimitiveRestart()) {
- fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
- sizeof(kCornerIndicesAsStrips),
- kCornerIndicesAsStrips,
- gCornerIndexBufferKey);
- fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsStrips);
- } else {
- fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
- sizeof(kCornerIndicesAsTris),
- kCornerIndicesAsTris,
- gCornerIndexBufferKey);
- fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris);
- }
- break;
- }
+ case RenderPass::kCubicCorners:
+ SK_ABORT("Corners are not used by VSImpl.");
}
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
@@ -597,12 +557,9 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
SkASSERT(sizeof(TriPointInstance) == this->getInstanceStride());
}
- if (fVertexBuffer) {
- SkASSERT(kAttribIdx_VertexData == this->numAttribs());
- this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
-
- SkASSERT(sizeof(int32_t) == this->getVertexStride());
- }
+ SkASSERT(kAttribIdx_VertexData == this->numAttribs());
+ this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
+ SkASSERT(sizeof(int32_t) == this->getVertexStride());
if (caps.usePrimitiveRestart()) {
this->setWillUsePrimitiveRestart();
@@ -618,9 +575,7 @@ void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceC
GrMesh& mesh = out->emplace_back(fPrimitiveType);
mesh.setIndexedInstanced(fIndexBuffer.get(), fNumIndicesPerInstance, instanceBuffer,
instanceCount, baseInstance);
- if (fVertexBuffer) {
- mesh.setVertexData(fVertexBuffer.get(), 0);
- }
+ mesh.setVertexData(fVertexBuffer.get(), 0);
}
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
@@ -633,7 +588,7 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Sh
case RenderPass::kTriangleCorners:
case RenderPass::kQuadraticCorners:
case RenderPass::kCubicCorners:
- return new VSCornerImpl(std::move(shadr));
+ SK_ABORT("Corners are not used by VSImpl.");
}
SK_ABORT("Invalid RenderPass");
return nullptr;
diff --git a/src/gpu/ccpr/GrCCCubicShader.cpp b/src/gpu/ccpr/GrCCCubicShader.cpp
index 4dc100b892..edff5ff563 100644
--- a/src/gpu/ccpr/GrCCCubicShader.cpp
+++ b/src/gpu/ccpr/GrCCCubicShader.cpp
@@ -13,8 +13,7 @@
using Shader = GrCCCoverageProcessor::Shader;
void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
- const char* repetitionID, const char* wind,
- GeometryVars* vars) const {
+ const char* wind, const char** /*tighterHull*/) const {
// Find the cubic's power basis coefficients.
s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
" 3, -6, 3, 0, "
@@ -58,14 +57,12 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
// Evaluate the cubic at T=.5 for a mid-ish point.
s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
- // Orient the KLM matrix so L & M have matching signs on the side of the curve we wish to fill.
- // We give L & M both the same sign as wind, in order to pass this value to the fragment shader.
- // (Cubics are pre-chopped such that L & M do not change sign within any individual segment).
+ // Orient the KLM matrix so L & M are both positive on the side of the curve we wish to fill.
s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
fKLMMatrix.c_str(), fKLMMatrix.c_str());
s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
- "0, orientation[0] * %s, 0, "
- "0, 0, orientation[1] * %s);", fKLMMatrix.c_str(), wind, wind);
+ "0, orientation[0], 0, "
+ "0, 0, orientation[1]);", fKLMMatrix.c_str());
// Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
s->declareGlobal(fEdgeDistanceEquation);
@@ -73,105 +70,54 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
s->codeAppendf("float2 edgept0 = %s[edgeidx0];", pts);
s->codeAppendf("float2 edgept1 = %s[3 - edgeidx0];", pts);
Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
-
- this->onEmitSetupCode(s, pts, repetitionID, vars);
}
void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
GrGLSLVarying::Scope scope, SkString* code,
const char* position, const char* coverage,
- const char* attenuatedCoverage, const char* /*wind*/) {
- SkASSERT(!coverage);
- SkASSERT(!attenuatedCoverage);
-
+ const char* attenuatedCoverage) {
fKLMD.reset(kFloat4_GrSLType, scope);
varyingHandler->addVarying("klmd", &fKLMD);
code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
- code->appendf("float d = dot(float3(%s, 1), %s);", position, fEdgeDistanceEquation.c_str());
- code->appendf("%s = float4(klm, d);", OutName(fKLMD));
-
- this->onEmitVaryings(varyingHandler, scope, code);
-}
-
-void GrCCCubicShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
- const char* outputCoverage) const {
- f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
- fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
-
- this->emitCoverage(f, outputCoverage);
-
- // Wind is the sign of both L and/or M. Take the sign of whichever has the larger magnitude.
- // (In reality, either would be fine because we chop cubics with more than a half pixel of
- // padding around the L & M lines, so neither should approach zero.)
- f->codeAppend ("half wind = sign(l + m);");
- f->codeAppendf("%s *= wind;", outputCoverage);
-}
+ // We give L & M both the same sign as wind, in order to pass this value to the fragment shader.
+ // (Cubics are pre-chopped such that L & M do not change sign within any individual segment.)
+ code->appendf("%s.xyz = klm * float3(1, %s, %s);",
+ OutName(fKLMD), coverage, coverage); // coverage == wind on curves.
+ code->appendf("%s.w = dot(float3(%s, 1), %s);", // Flat edge opposite the curve.
+ OutName(fKLMD), position, fEdgeDistanceEquation.c_str());
-void GrCCCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
- GrGLSLVarying::Scope scope, SkString* code) {
fGradMatrix.reset(kFloat2x2_GrSLType, scope);
varyingHandler->addVarying("grad_matrix", &fGradMatrix);
- // "klm" was just defined by the base class.
code->appendf("%s[0] = 2*bloat * 3 * klm[0] * %s[0].xy;",
OutName(fGradMatrix), fKLMMatrix.c_str());
code->appendf("%s[1] = -2*bloat * (klm[1] * %s[2].xy + klm[2] * %s[1].xy);",
- OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
+ OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
+
+ if (attenuatedCoverage) {
+ fCornerCoverage.reset(kHalf2_GrSLType, scope);
+ varyingHandler->addVarying("corner_coverage", &fCornerCoverage);
+ code->appendf("%s = %s;", // Attenuated corner coverage.
+ OutName(fCornerCoverage), attenuatedCoverage);
+ }
}
-void GrCCCubicHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
- const char* outputCoverage) const {
- // k,l,m,d are defined by the base class.
+void GrCCCubicShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
+ const char* outputCoverage) const {
+ f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;", fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
f->codeAppend ("float f = k*k*k - l*m;");
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage);
- f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat edge opposite the curve.
-}
-
-void GrCCCubicCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
- const char* repetitionID, GeometryVars* vars) const {
- s->codeAppendf("float2 corner = %s[%s * 3];", pts, repetitionID);
- vars->fCornerVars.fPoint = "corner";
-}
-
-void GrCCCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
- GrGLSLVarying::Scope scope, SkString* code) {
- using Interpolation = GrGLSLVaryingHandler::Interpolation;
-
- fdKLMDdx.reset(kFloat4_GrSLType, scope);
- varyingHandler->addVarying("dklmddx", &fdKLMDdx, Interpolation::kCanBeFlat);
- code->appendf("%s = 2*bloat * float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
- OutName(fdKLMDdx), fKLMMatrix.c_str(), fKLMMatrix.c_str(),
- fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str());
-
- fdKLMDdy.reset(kFloat4_GrSLType, scope);
- varyingHandler->addVarying("dklmddy", &fdKLMDdy, Interpolation::kCanBeFlat);
- code->appendf("%s = 2*bloat * float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
- OutName(fdKLMDdy), fKLMMatrix.c_str(), fKLMMatrix.c_str(),
- fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str());
-}
-void GrCCCubicCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
- const char* outputCoverage) const {
- f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn());
+ f->codeAppendf("half d = min(%s.w, 0);", fKLMD.fsIn()); // Flat edge opposite the curve.
+ // Wind is the sign of both L and/or M. Take the sign of whichever has the larger magnitude.
+ // (In reality, either would be fine because we chop cubics with more than a half pixel of
+ // padding around the L & M lines, so neither should approach zero.)
+ f->codeAppend ("half wind = sign(l + m);");
+ f->codeAppendf("%s = (%s + d) * wind;", outputCoverage, outputCoverage);
- // Erase what the previous hull shader wrote. We don't worry about the two corners falling on
- // the same pixel because those cases should have been weeded out by this point.
- // k,l,m,d are defined by the base class.
- f->codeAppend ("float f = k*k*k - l*m;");
- f->codeAppend ("float2 grad_f = float3(3*k*k, -m, -l) * float2x3(grad_klmd);");
- f->codeAppendf("%s = -clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);",
- outputCoverage);
- f->codeAppendf("%s -= d;", outputCoverage);
-
- // Use software msaa to estimate actual coverage at the corner pixels.
- const int sampleCount = Shader::DefineSoftSampleLocations(f, "samples");
- f->codeAppendf("float4 klmd_center = float4(%s.xyz, %s.w + 0.5);",
- fKLMD.fsIn(), fKLMD.fsIn());
- f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
- f->codeAppend ( "float4 klmd = grad_klmd * samples[i] + klmd_center;");
- f->codeAppend ( "half f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;");
- f->codeAppendf( "%s += all(greaterThan(half4(f, klmd.y, klmd.z, klmd.w), "
- "half4(0))) ? %f : 0;",
- outputCoverage, 1.0 / sampleCount);
- f->codeAppend ("}");
+ if (fCornerCoverage.fsIn()) {
+ f->codeAppendf("%s = %s.x * %s.y + %s;", // Attenuated corner coverage.
+ outputCoverage, fCornerCoverage.fsIn(), fCornerCoverage.fsIn(),
+ outputCoverage);
+ }
}
diff --git a/src/gpu/ccpr/GrCCCubicShader.h b/src/gpu/ccpr/GrCCCubicShader.h
index dff7f6bbf5..39ca55fd9e 100644
--- a/src/gpu/ccpr/GrCCCubicShader.h
+++ b/src/gpu/ccpr/GrCCCubicShader.h
@@ -19,43 +19,23 @@
*
* The provided curve segments must be convex, monotonic with respect to the vector of their closing
* edge [P3 - P0], and must not contain or be near any inflection points or loop intersections.
- * (Use GrCCGeometry.)
+ * (Use GrCCGeometry::cubicTo().)
*/
class GrCCCubicShader : public GrCCCoverageProcessor::Shader {
-protected:
- void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- const char* wind, GeometryVars*) const final;
- virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- GeometryVars*) const {}
+ void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* wind,
+ const char** tighterHull) const override;
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
- const char* position, const char* coverage, const char* attenuatedCoverage,
- const char* wind) final;
- virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0;
+ const char* position, const char* coverage,
+ const char* attenuatedCoverage) override;
- void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final;
- virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0;
+ void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
- GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
- GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
+ const GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
+ const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
GrGLSLVarying fKLMD;
-};
-
-class GrCCCubicHullShader : public GrCCCubicShader {
- void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
- void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
-
GrGLSLVarying fGradMatrix;
-};
-
-class GrCCCubicCornerShader : public GrCCCubicShader {
- void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- GeometryVars*) const override;
- void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
- void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
-
- GrGLSLVarying fdKLMDdx;
- GrGLSLVarying fdKLMDdy;
+ GrGLSLVarying fCornerCoverage;
};
#endif
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.cpp b/src/gpu/ccpr/GrCCQuadraticShader.cpp
index 5bee85ef3f..287e63e8bf 100644
--- a/src/gpu/ccpr/GrCCQuadraticShader.cpp
+++ b/src/gpu/ccpr/GrCCQuadraticShader.cpp
@@ -14,8 +14,7 @@
using Shader = GrCCCoverageProcessor::Shader;
void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
- const char* repetitionID, const char* wind,
- GeometryVars* vars) const {
+ const char* wind, const char** tighterHull) const {
s->declareGlobal(fQCoordMatrix);
s->codeAppendf("%s = float2x2(1, 1, .5, 0) * inverse(float2x2(%s[2] - %s[0], %s[1] - %s[0]));",
fQCoordMatrix.c_str(), pts, pts, pts, pts);
@@ -28,113 +27,60 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p
s->codeAppendf("float2 edgept1 = %s[%s > 0 ? 0 : 2];", pts, wind);
Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
- this->onEmitSetupCode(s, pts, repetitionID, vars);
+ if (tighterHull) {
+ // Find the T value whose tangent is halfway between the tangents at the endpionts.
+ s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts);
+ s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts);
+ s->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
+ s->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
+ s->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s!=0; we cull flat curves on CPU.
+
+ // Clip the bezier triangle by the tangent at our new t value. This is a simple application
+ // for De Casteljau's algorithm.
+ s->codeAppendf("float4x2 quadratic_hull = float4x2(%s[0], "
+ "%s[0] + tan0 * t, "
+ "%s[1] + tan1 * t, "
+ "%s[2]);", pts, pts, pts, pts);
+ *tighterHull = "quadratic_hull";
+ }
}
void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
GrGLSLVarying::Scope scope, SkString* code,
const char* position, const char* coverage,
- const char* attenuatedCoverage, const char* wind) {
- SkASSERT(!coverage);
- SkASSERT(!attenuatedCoverage);
-
- fXYDW.reset(kFloat4_GrSLType, scope);
- varyingHandler->addVarying("xydw", &fXYDW);
- code->appendf("%s.xy = %s * (%s - %s);",
- OutName(fXYDW), fQCoordMatrix.c_str(), position, fQCoord0.c_str());
- code->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
- OutName(fXYDW), fEdgeDistanceEquation.c_str(), position,
- fEdgeDistanceEquation.c_str());
- code->appendf("%s.w = %s;", OutName(fXYDW), wind);
-
- this->onEmitVaryings(varyingHandler, scope, code);
+ const char* attenuatedCoverage) {
+ fCoord.reset(kFloat4_GrSLType, scope);
+ varyingHandler->addVarying("coord", &fCoord);
+ code->appendf("%s.xy = %s * (%s - %s);", // Quadratic coords.
+ OutName(fCoord), fQCoordMatrix.c_str(), position, fQCoord0.c_str());
+ code->appendf("%s.zw = 2*bloat * float2(2 * %s.x, -1) * %s;", // Gradient.
+ OutName(fCoord), OutName(fCoord), fQCoordMatrix.c_str());
+
+ // Coverages need full precision since distance to the opposite edge can be large.
+ fCoverages.reset(attenuatedCoverage ? kFloat4_GrSLType : kFloat2_GrSLType, scope);
+ varyingHandler->addVarying("coverages", &fCoverages);
+ code->appendf("%s.x = dot(%s, float3(%s, 1));", // Distance to flat edge opposite the curve.
+ OutName(fCoverages), fEdgeDistanceEquation.c_str(), position);
+ code->appendf("%s.y = %s;", OutName(fCoverages), coverage); // Wind.
+ if (attenuatedCoverage) {
+ code->appendf("%s.zw = %s;", // Attenuated corner coverage.
+ OutName(fCoverages), attenuatedCoverage);
+ }
}
void GrCCQuadraticShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
const char* outputCoverage) const {
- this->emitCoverage(f, outputCoverage);
- f->codeAppendf("%s *= %s.w;", outputCoverage, fXYDW.fsIn()); // Sign by wind.
-}
-
-void GrCCQuadraticHullShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
- const char* /*repetitionID*/,
- GeometryVars* vars) const {
- // Find the T value whose tangent is halfway between the tangents at the endpionts.
- s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts);
- s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts);
- s->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
- s->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
- s->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s != 0; we cull flat curves on CPU.
-
- // Clip the bezier triangle by the tangent at our new t value. This is a simple application for
- // De Casteljau's algorithm.
- s->codeAppendf("float4x2 quadratic_hull = float4x2(%s[0], "
- "%s[0] + tan0 * t, "
- "%s[1] + tan1 * t, "
- "%s[2]);", pts, pts, pts, pts);
- vars->fHullVars.fAlternatePoints = "quadratic_hull";
-}
-
-void GrCCQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
- GrGLSLVarying::Scope scope, SkString* code) {
- fGrad.reset(kFloat2_GrSLType, scope);
- varyingHandler->addVarying("grad", &fGrad);
- code->appendf("%s = 2*bloat * float2(2 * %s.x, -1) * %s;",
- OutName(fGrad), OutName(fXYDW), fQCoordMatrix.c_str());
-}
-
-void GrCCQuadraticHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
- const char* outputCoverage) const {
- f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
- fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn(), fGrad.fsIn(), fGrad.fsIn());
- f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
- f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYDW.fsIn()); // Flat closing edge.
-}
-
-void GrCCQuadraticCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
- const char* repetitionID,
- GeometryVars* vars) const {
- s->codeAppendf("float2 corner = %s[%s * 2];", pts, repetitionID);
- vars->fCornerVars.fPoint = "corner";
-}
-
-void GrCCQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
- GrGLSLVarying::Scope scope, SkString* code) {
- using Interpolation = GrGLSLVaryingHandler::Interpolation;
-
- fdXYDdx.reset(kFloat3_GrSLType, scope);
- varyingHandler->addVarying("dXYDdx", &fdXYDdx, Interpolation::kCanBeFlat);
- code->appendf("%s = 2*bloat * float3(%s[0].x, %s[0].y, %s.x);",
- OutName(fdXYDdx), fQCoordMatrix.c_str(), fQCoordMatrix.c_str(),
- fEdgeDistanceEquation.c_str());
-
- fdXYDdy.reset(kFloat3_GrSLType, scope);
- varyingHandler->addVarying("dXYDdy", &fdXYDdy, Interpolation::kCanBeFlat);
- code->appendf("%s = 2*bloat * float3(%s[1].x, %s[1].y, %s.y);",
- OutName(fdXYDdy), fQCoordMatrix.c_str(), fQCoordMatrix.c_str(),
- fEdgeDistanceEquation.c_str());
-}
-
-void GrCCQuadraticCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
- const char* outputCoverage) const {
- f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
- fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn());
- f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn());
-
- // Erase what the previous hull shader wrote. We don't worry about the two corners falling on
- // the same pixel because those cases should have been weeded out by this point.
+ f->codeAppendf("float x = %s.x, y = %s.y;", fCoord.fsIn(), fCoord.fsIn());
f->codeAppend ("float f = x*x - y;");
- f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);");
- f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage);
- f->codeAppendf("%s -= d;", outputCoverage);
+ f->codeAppendf("float2 grad = %s.zw;", fCoord.fsIn());
+ f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad, grad)), 0, 1);", outputCoverage);
+
+ f->codeAppendf("half d = min(%s.x, 0);", fCoverages.fsIn()); // Flat edge opposite the curve.
+ f->codeAppendf("half wind = %s.y;", fCoverages.fsIn());
+ f->codeAppendf("%s = (%s + d) * wind;", outputCoverage, outputCoverage);
- // Use software msaa to approximate coverage at the corner pixels.
- int sampleCount = Shader::DefineSoftSampleLocations(f, "samples");
- f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);", fXYDW.fsIn(), fXYDW.fsIn());
- f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
- f->codeAppend ( "float3 xyd = grad_xyd * samples[i] + xyd_center;");
- f->codeAppend ( "half f = xyd.y - xyd.x * xyd.x;"); // f > 0 -> inside curve.
- f->codeAppendf( "%s += all(greaterThan(float2(f,xyd.z), float2(0))) ? %f : 0;",
- outputCoverage, 1.0 / sampleCount);
- f->codeAppendf("}");
+ if (kFloat4_GrSLType == fCoverages.type()) {
+ f->codeAppendf("%s = %s.z * %s.w + %s;", // Attenuated corner coverage.
+ outputCoverage, fCoverages.fsIn(), fCoverages.fsIn(), outputCoverage);
+ }
}
diff --git a/src/gpu/ccpr/GrCCQuadraticShader.h b/src/gpu/ccpr/GrCCQuadraticShader.h
index 8635ba4ba0..30c5230154 100644
--- a/src/gpu/ccpr/GrCCQuadraticShader.h
+++ b/src/gpu/ccpr/GrCCQuadraticShader.h
@@ -18,55 +18,24 @@
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
*
* The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
- * (Use GrCCGeometry.)
+ * (Use GrCCGeometry::quadraticTo().)
*/
class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader {
protected:
- void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- const char* wind, GeometryVars*) const final;
- virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- GeometryVars*) const = 0;
+ void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* wind,
+ const char** tighterHull) const override;
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
- const char* position, const char* coverage, const char* attenuatedCoverage,
- const char* wind) final;
- virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {}
+ const char* position, const char* coverage,
+ const char* attenuatedCoverage) override;
- void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final;
- virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0;
+ void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
const GrShaderVar fQCoordMatrix{"qcoord_matrix", kFloat2x2_GrSLType};
const GrShaderVar fQCoord0{"qcoord0", kFloat2_GrSLType};
const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
- GrGLSLVarying fXYDW;
-};
-
-/**
- * This pass draws a conservative raster hull around the quadratic bezier curve, computes the
- * curve's coverage using the gradient-based AA technique outlined in the Loop/Blinn paper, and
- * uses simple distance-to-edge to subtract out coverage for the flat closing edge [P2 -> P0]. Since
- * the provided curves are monotonic, this will get every pixel right except the two corners.
- */
-class GrCCQuadraticHullShader : public GrCCQuadraticShader {
- void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- GeometryVars*) const override;
- void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
- void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
-
- GrGLSLVarying fGrad;
-};
-
-/**
- * This pass fixes the corners of a closed quadratic segment with soft MSAA.
- */
-class GrCCQuadraticCornerShader : public GrCCQuadraticShader {
- void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
- GeometryVars*) const override;
- void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
- void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
-
- GrGLSLVarying fdXYDdx;
- GrGLSLVarying fdXYDdy;
+ GrGLSLVarying fCoord;
+ GrGLSLVarying fCoverages;
};
#endif
diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h
index 3dcb351970..f9af13c3be 100644
--- a/src/gpu/ccpr/GrCCTriangleShader.h
+++ b/src/gpu/ccpr/GrCCTriangleShader.h
@@ -19,31 +19,28 @@
class GrCCTriangleShader : public GrCCCoverageProcessor::Shader {
void onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope,
SkString* code, const char* position, const char* coverage,
- const char* attenuatedCoverage, const char* wind) override {
+ const char* attenuatedCoverage) override {
if (!attenuatedCoverage) {
- fCoverageTimesWind.reset(kHalf_GrSLType, scope);
- varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
- code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), coverage, wind);
+ fCoverages.reset(kHalf_GrSLType, scope);
+ varyingHandler->addVarying("coverage", &fCoverages);
+ code->appendf("%s = %s;", OutName(fCoverages), coverage);
} else {
- fCoverageTimesWind.reset(kHalf3_GrSLType, scope);
- varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
- code->appendf("%s = half3(%s, %s);",
- OutName(fCoverageTimesWind), attenuatedCoverage, coverage);
- code->appendf("%s.yz *= %s;", OutName(fCoverageTimesWind), wind);
+ fCoverages.reset(kHalf3_GrSLType, scope);
+ varyingHandler->addVarying("coverages", &fCoverages);
+ code->appendf("%s = half3(%s, %s);", OutName(fCoverages), attenuatedCoverage, coverage);
}
}
void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override {
- if (kHalf_GrSLType == fCoverageTimesWind.type()) {
- f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
+ if (kHalf_GrSLType == fCoverages.type()) {
+ f->codeAppendf("%s = %s;", outputCoverage, fCoverages.fsIn());
} else {
f->codeAppendf("%s = %s.x * %s.y + %s.z;",
- outputCoverage, fCoverageTimesWind.fsIn(), fCoverageTimesWind.fsIn(),
- fCoverageTimesWind.fsIn());
+ outputCoverage, fCoverages.fsIn(), fCoverages.fsIn(), fCoverages.fsIn());
}
}
- GrGLSLVarying fCoverageTimesWind;
+ GrGLSLVarying fCoverages;
};
#endif