aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--samplecode/SampleCCPRGeometry.cpp41
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor.cpp33
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor.h130
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp154
-rw-r--r--src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp563
-rw-r--r--src/gpu/ccpr/GrCCPathParser.cpp37
-rw-r--r--src/gpu/ccpr/GrCCPathParser.h4
7 files changed, 441 insertions, 521 deletions
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index c6174516d3..ea3eafb591 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -28,7 +28,7 @@
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
-using RenderPass = GrCCCoverageProcessor::RenderPass;
+using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
static constexpr float kDebugBloat = 40;
@@ -56,7 +56,7 @@ private:
void updateGpuData();
- RenderPass fRenderPass = RenderPass::kTriangles;
+ PrimitiveType fPrimitiveType = PrimitiveType::kTriangles;
SkCubicType fCubicType;
SkMatrix fCubicKLM;
@@ -90,7 +90,6 @@ private:
bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
void onPrepare(GrOpFlushState*) override {}
void onExecute(GrOpFlushState*) override;
- void drawRenderPass(GrOpFlushState*, RenderPass);
CCPRGeometryView* fView;
@@ -149,9 +148,9 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
SkPath outline;
outline.moveTo(fPoints[0]);
- if (RenderPass::kCubics == fRenderPass) {
+ if (PrimitiveType::kCubics == fPrimitiveType) {
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
- } else if (RenderPass::kQuadratics == fRenderPass) {
+ } else if (PrimitiveType::kQuadratics == fPrimitiveType) {
outline.quadTo(fPoints[1], fPoints[3]);
} else {
outline.lineTo(fPoints[1]);
@@ -205,8 +204,8 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
SkRect::MakeIWH(this->width(), this->height()));
// Add label.
- caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass));
- if (RenderPass::kCubics == fRenderPass) {
+ caption.appendf("RenderPass_%s", GrCCCoverageProcessor::PrimitiveTypeName(fPrimitiveType));
+ if (PrimitiveType::kCubics == fPrimitiveType) {
caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
}
} else {
@@ -218,7 +217,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
pointsPaint.setStrokeWidth(8);
pointsPaint.setAntiAlias(true);
- if (RenderPass::kCubics == fRenderPass) {
+ if (PrimitiveType::kCubics == fPrimitiveType) {
int w = this->width(), h = this->height();
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
@@ -240,7 +239,7 @@ void CCPRGeometryView::updateGpuData() {
fTriPointInstances.reset();
fQuadPointInstances.reset();
- if (RenderPass::kCubics == fRenderPass) {
+ if (PrimitiveType::kCubics == fPrimitiveType) {
double t[2], s[2];
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
GrCCGeometry geometry;
@@ -264,7 +263,7 @@ void CCPRGeometryView::updateGpuData() {
continue;
}
}
- } else if (RenderPass::kQuadratics == fRenderPass) {
+ } else if (PrimitiveType::kQuadratics == fPrimitiveType) {
GrCCGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.quadraticTo(fPoints[1], fPoints[3]);
@@ -290,27 +289,18 @@ void CCPRGeometryView::updateGpuData() {
}
void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state) {
- this->drawRenderPass(state, fView->fRenderPass);
-
- RenderPass cornerPass = RenderPass((int)fView->fRenderPass + 1);
- if (GrCCCoverageProcessor::DoesRenderPass(cornerPass, state->caps())) {
- this->drawRenderPass(state, cornerPass);
- }
-}
-
-void CCPRGeometryView::DrawCoverageCountOp::drawRenderPass(GrOpFlushState* state,
- RenderPass renderPass) {
GrResourceProvider* rp = state->resourceProvider();
GrContext* context = state->gpu()->getContext();
GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend()
? static_cast<GrGLGpu*>(state->gpu())
: nullptr;
- GrCCCoverageProcessor proc(rp, renderPass, GrCCCoverageProcessor::WindMethod::kCrossProduct);
+ GrCCCoverageProcessor proc(rp, fView->fPrimitiveType,
+ GrCCCoverageProcessor::WindMethod::kCrossProduct);
SkDEBUGCODE(proc.enableDebugBloat(kDebugBloat));
SkSTArray<1, GrMesh> mesh;
- if (GrCCCoverageProcessor::RenderPassIsCubic(renderPass)) {
+ if (PrimitiveType::kCubics == fView->fPrimitiveType) {
sk_sp<GrBuffer> instBuff(rp->createBuffer(
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
kVertex_GrBufferType, kDynamic_GrAccessPattern,
@@ -341,8 +331,7 @@ void CCPRGeometryView::DrawCoverageCountOp::drawRenderPass(GrOpFlushState* state
if (!mesh.empty()) {
SkASSERT(1 == mesh.count());
- GrGpuRTCommandBuffer* cmdBuff = state->rtCommandBuffer();
- cmdBuff->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds());
+ proc.draw(state, pipeline, mesh.begin(), nullptr, 1, this->bounds());
}
if (glGpu) {
@@ -375,7 +364,7 @@ private:
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
for (int i = 0; i < 4; ++i) {
- if (RenderPass::kCubics != fRenderPass && 2 == i) {
+ if (PrimitiveType::kCubics != fPrimitiveType && 2 == i) {
continue;
}
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
@@ -400,7 +389,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
SkUnichar unichar;
if (SampleCode::CharQ(*evt, &unichar)) {
if (unichar >= '1' && unichar <= '3') {
- fRenderPass = RenderPass((unichar - '1') * 2);
+ fPrimitiveType = PrimitiveType(unichar - '1');
this->updateAndInval();
return true;
}
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
index 8e7249e9e4..c44d464826 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
@@ -7,6 +7,8 @@
#include "GrCCCoverageProcessor.h"
+#include "GrGpuCommandBuffer.h"
+#include "GrOpFlushState.h"
#include "SkMakeUnique.h"
#include "ccpr/GrCCCubicShader.h"
#include "ccpr/GrCCQuadraticShader.h"
@@ -129,7 +131,10 @@ void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGe
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
GrProcessorKeyBuilder* b) const {
- int key = (int)fRenderPass << 2;
+ int key = (int)fPrimitiveType << 3;
+ if (GSSubpass::kCorners == fGSSubpass) {
+ key |= 4;
+ }
if (WindMethod::kInstanceData == fWindMethod) {
key |= 2;
}
@@ -146,20 +151,32 @@ void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
std::unique_ptr<Shader> shader;
- switch (fRenderPass) {
- case RenderPass::kTriangles:
- case RenderPass::kTriangleCorners:
+ switch (fPrimitiveType) {
+ case PrimitiveType::kTriangles:
shader = skstd::make_unique<GrCCTriangleShader>();
break;
- case RenderPass::kQuadratics:
- case RenderPass::kQuadraticCorners:
+ case PrimitiveType::kQuadratics:
shader = skstd::make_unique<GrCCQuadraticShader>();
break;
- case RenderPass::kCubics:
- case RenderPass::kCubicCorners:
+ case PrimitiveType::kCubics:
shader = skstd::make_unique<GrCCCubicShader>();
break;
}
return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
: this->createVSImpl(std::move(shader));
}
+
+void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline,
+ const GrMesh meshes[],
+ const GrPipeline::DynamicState dynamicStates[], int meshCount,
+ const SkRect& drawBounds) const {
+ GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer();
+ cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds);
+
+ // Geometry shader backend draws primitives in two subpasses.
+ if (Impl::kGeometryShader == fImpl) {
+ SkASSERT(GSSubpass::kHulls == fGSSubpass);
+ GrCCCoverageProcessor cornerProc(*this, GSSubpass::kCorners);
+ cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds);
+ }
+}
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
index 8b2b00392b..8824646a05 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.h
@@ -10,6 +10,7 @@
#include "GrCaps.h"
#include "GrGeometryProcessor.h"
+#include "GrPipeline.h"
#include "GrShaderCaps.h"
#include "SkNx.h"
#include "glsl/GrGLSLGeometryProcessor.h"
@@ -18,21 +19,29 @@
class GrGLSLFPFragmentBuilder;
class GrGLSLVertexGeoBuilder;
class GrMesh;
+class GrOpFlushState;
/**
* This is the geometry processor for the simple convex primitive shapes (triangles and closed,
* convex bezier curves) from which ccpr paths are composed. The output is a single-channel alpha
* value, positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
*
- * The caller is responsible to execute all render passes for all applicable primitives into a
- * cleared, floating point, alpha-only render target using SkBlendMode::kPlus (see RenderPass
- * below). Once all of a path's primitives have been drawn, the render target contains a composite
- * coverage count that can then be used to draw the path (see GrCCPathProcessor).
+ * The caller is responsible to draw all primitives as produced by GrCCGeometry into a cleared,
+ * floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's
+ * primitives have been drawn, the render target contains a composite coverage count that can then
+ * be used to draw the path (see GrCCPathProcessor).
*
- * To draw a renderer pass, see appendMesh below.
+ * To draw primitives, use appendMesh() and draw() (defined below).
*/
class GrCCCoverageProcessor : public GrGeometryProcessor {
public:
+ enum class PrimitiveType {
+ kTriangles,
+ kQuadratics,
+ kCubics,
+ };
+ static const char* PrimitiveTypeName(PrimitiveType);
+
// Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
// X,Y point values are transposed.
struct TriPointInstance {
@@ -54,50 +63,18 @@ public:
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
};
- // All primitive shapes (triangles and closed, convex bezier curves) may require two render
- // passes: One to draw a rough outline of the shape, and a second pass to touch up the corners.
- // Check DoesRenderPass() before attempting to draw a given RenderPass. Here we enumerate every
- // possible render pass needed in order to produce a complete coverage count mask. This is an
- // exhaustive list of all ccpr coverage shaders.
- enum class RenderPass {
- kTriangles,
- kTriangleCorners,
- kQuadratics,
- kQuadraticCorners,
- kCubics,
- kCubicCorners
- };
- static bool RenderPassIsCubic(RenderPass);
- static const char* RenderPassName(RenderPass);
-
- 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 {
kCrossProduct, // Calculate wind = +/-1 by sign of the cross product.
kInstanceData // Instance data provides custom, signed wind values of any magnitude.
// (For tightly-wound tessellated triangles.)
};
- GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, WindMethod windMethod)
+ GrCCCoverageProcessor(GrResourceProvider* rp, PrimitiveType type, WindMethod windMethod)
: INHERITED(kGrCCCoverageProcessor_ClassID)
- , fRenderPass(pass)
+ , fPrimitiveType(type)
, fWindMethod(windMethod)
, fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
: Impl::kVertexShader) {
- SkASSERT(DoesRenderPass(pass, *rp->caps()));
if (Impl::kGeometryShader == fImpl) {
this->initGS();
} else {
@@ -106,7 +83,7 @@ public:
}
// GrPrimitiveProcessor overrides.
- const char* name() const override { return RenderPassName(fRenderPass); }
+ const char* name() const override { return PrimitiveTypeName(fPrimitiveType); }
SkString dumpInfo() const override {
return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
}
@@ -132,6 +109,9 @@ public:
}
}
+ void draw(GrOpFlushState*, const GrPipeline&, const GrMesh[], const GrPipeline::DynamicState[],
+ int meshCount, const SkRect& drawBounds) const;
+
// The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
// provides details about shape-specific geometry.
class Shader {
@@ -224,13 +204,30 @@ private:
static constexpr float kAABloatRadius = 0.491111f;
// Number of bezier points for curves, or 3 for triangles.
- int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; }
+ int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; }
enum class Impl : bool {
kGeometryShader,
kVertexShader
};
+ // Geometry shader backend draws primitives in two subpasses.
+ enum class GSSubpass : bool {
+ kHulls,
+ kCorners
+ };
+
+ GrCCCoverageProcessor(const GrCCCoverageProcessor& proc, GSSubpass subpass)
+ : INHERITED(kGrCCCoverageProcessor_ClassID)
+ , fPrimitiveType(proc.fPrimitiveType)
+ , fWindMethod(proc.fWindMethod)
+ , fImpl(Impl::kGeometryShader)
+ SkDEBUGCODE(, fDebugBloat(proc.fDebugBloat))
+ , fGSSubpass(subpass) {
+ SkASSERT(Impl::kGeometryShader == proc.fImpl);
+ this->initGS();
+ }
+
void initGS();
void initVS(GrResourceProvider*);
@@ -242,20 +239,33 @@ private:
GrGLSLPrimitiveProcessor* createGSImpl(std::unique_ptr<Shader>) const;
GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const;
- const RenderPass fRenderPass;
+ const PrimitiveType fPrimitiveType;
const WindMethod fWindMethod;
const Impl fImpl;
SkDEBUGCODE(float fDebugBloat = 0);
+ // Used by GSImpl.
+ const GSSubpass fGSSubpass = GSSubpass::kHulls;
+
// Used by VSImpl.
- sk_sp<const GrBuffer> fVertexBuffer;
- sk_sp<const GrBuffer> fIndexBuffer;
- int fNumIndicesPerInstance;
- GrPrimitiveType fPrimitiveType;
+ sk_sp<const GrBuffer> fVSVertexBuffer;
+ sk_sp<const GrBuffer> fVSIndexBuffer;
+ int fVSNumIndicesPerInstance;
+ GrPrimitiveType fVSTriangleType;
typedef GrGeometryProcessor INHERITED;
};
+inline const char* GrCCCoverageProcessor::PrimitiveTypeName(PrimitiveType type) {
+ switch (type) {
+ case PrimitiveType::kTriangles: return "kTriangles";
+ case PrimitiveType::kQuadratics: return "kQuadratics";
+ case PrimitiveType::kCubics: return "kCubics";
+ }
+ SK_ABORT("Invalid PrimitiveType");
+ return "";
+}
+
inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) {
this->set(p[0], p[1], p[2], trans);
}
@@ -285,32 +295,4 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con
Sk2f::Store4(this, P0, P1, P2, W);
}
-inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
- switch (pass) {
- case RenderPass::kTriangles:
- case RenderPass::kTriangleCorners:
- case RenderPass::kQuadratics:
- case RenderPass::kQuadraticCorners:
- return false;
- case RenderPass::kCubics:
- case RenderPass::kCubicCorners:
- return true;
- }
- SK_ABORT("Invalid RenderPass");
- return false;
-}
-
-inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
- switch (pass) {
- case RenderPass::kTriangles: return "kTriangles";
- case RenderPass::kTriangleCorners: return "kTriangleCorners";
- case RenderPass::kQuadratics: return "kQuadratics";
- case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
- case RenderPass::kCubics: return "kCubics";
- case RenderPass::kCubicCorners: return "kCubicCorners";
- }
- SK_ABORT("Invalid RenderPass");
- return "";
-}
-
#endif
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
index 907de46019..0bb34b16a0 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp
@@ -229,6 +229,72 @@ public:
};
/**
+ * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
+ */
+class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
+public:
+ GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
+
+ 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.
+ //
+ // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
+ // the hull in two independent invocations. Each invocation designates the corner it will
+ // begin with as top-left.
+ g->codeAppend ("int i = sk_InvocationID * 2;");
+ g->codeAppendf("float2 topleft = %s[i];", hullPts);
+ g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
+ g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
+ g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
+
+ // Determine how much to outset the conservative raster hull from the relevant edges.
+ g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
+ "topleft.x > bottomleft.x ? -bloat : bloat);");
+ g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
+ "topright.x > topleft.x ? -bloat : +bloat);");
+ g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
+ "bottomright.x > topright.x ? -bloat : +bloat);");
+
+ // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
+ // boxes centered on the input points, split evenly between two invocations. This translates
+ // to a polygon with either one, two, or three vertices at each input point, depending on
+ // how sharp the corner is. For more details on conservative raster, see:
+ // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
+ g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
+ g->codeAppend ("if (all(left_up_notequal)) {");
+ // The top-left corner will have three conservative raster vertices.
+ // Emit the middle one first to the triangle strip.
+ g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
+ g->codeAppend ("}");
+ g->codeAppend ("if (any(left_up_notequal)) {");
+ // Second conservative raster vertex for the top-left corner.
+ g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn);
+ g->codeAppend ("}");
+
+ // Main interior body of this invocation's half of the hull.
+ g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
+ g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
+ g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
+
+ // Remaining two conservative raster vertices for the top-right corner.
+ g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
+ g->codeAppend ("if (any(up_right_notequal)) {");
+ g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn);
+ g->codeAppend ("}");
+ g->codeAppend ("if (all(up_right_notequal)) {");
+ g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
+ g->codeAppend ("}");
+
+ g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
+ }
+};
+
+/**
* 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.
*/
@@ -243,7 +309,7 @@ public:
const GrShaderVar& wind, const char* emitVertexFn) const override {
fShader->emitSetupCode(g, "pts", wind.c_str());
- bool isTriangle = RenderPass::kTriangleCorners == proc.fRenderPass;
+ bool isTriangle = PrimitiveType::kTriangles == proc.fPrimitiveType;
g->codeAppendf("int corneridx = sk_InvocationID;");
if (!isTriangle) {
g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1);
@@ -314,75 +380,9 @@ public:
}
};
-/**
- * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
- */
-class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
-public:
- GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
-
- 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.
- //
- // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
- // the hull in two independent invocations. Each invocation designates the corner it will
- // begin with as top-left.
- g->codeAppend ("int i = sk_InvocationID * 2;");
- g->codeAppendf("float2 topleft = %s[i];", hullPts);
- g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
- g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
- g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
-
- // Determine how much to outset the conservative raster hull from the relevant edges.
- g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
- "topleft.x > bottomleft.x ? -bloat : bloat);");
- g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
- "topright.x > topleft.x ? -bloat : +bloat);");
- g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
- "bottomright.x > topright.x ? -bloat : +bloat);");
-
- // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
- // boxes centered on the input points, split evenly between two invocations. This translates
- // to a polygon with either one, two, or three vertices at each input point, depending on
- // how sharp the corner is. For more details on conservative raster, see:
- // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
- g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
- g->codeAppend ("if (all(left_up_notequal)) {");
- // The top-left corner will have three conservative raster vertices.
- // Emit the middle one first to the triangle strip.
- g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
- g->codeAppend ("}");
- g->codeAppend ("if (any(left_up_notequal)) {");
- // Second conservative raster vertex for the top-left corner.
- g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn);
- g->codeAppend ("}");
-
- // Main interior body of this invocation's half of the hull.
- g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
- g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
- g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
-
- // Remaining two conservative raster vertices for the top-right corner.
- g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
- g->codeAppend ("if (any(up_right_notequal)) {");
- g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn);
- g->codeAppend ("}");
- g->codeAppend ("if (all(up_right_notequal)) {");
- g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
- g->codeAppend ("}");
-
- g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
- }
-};
-
void GrCCCoverageProcessor::initGS() {
SkASSERT(Impl::kGeometryShader == fImpl);
- if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
+ if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) {
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
@@ -408,17 +408,11 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC
}
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const {
- switch (fRenderPass) {
- case RenderPass::kTriangles:
- return new GSTriangleHullImpl(std::move(shadr));
- case RenderPass::kQuadratics:
- case RenderPass::kCubics:
- return new GSCurveHullImpl(std::move(shadr));
- case RenderPass::kTriangleCorners:
- case RenderPass::kQuadraticCorners:
- case RenderPass::kCubicCorners:
- return new GSCornerImpl(std::move(shadr));
+ if (GSSubpass::kHulls == fGSSubpass) {
+ return (PrimitiveType::kTriangles == fPrimitiveType)
+ ? (GSImpl*) new GSTriangleHullImpl(std::move(shadr))
+ : (GSImpl*) new GSCurveHullImpl(std::move(shadr));
}
- SK_ABORT("Invalid RenderPass");
- return nullptr;
+ SkASSERT(GSSubpass::kCorners == fGSSubpass);
+ return new GSCornerImpl(std::move(shadr));
}
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
index d0fc4e0f71..7af1a739fc 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp
@@ -10,92 +10,30 @@
#include "GrMesh.h"
#include "glsl/GrGLSLVertexGeoBuilder.h"
-using Shader = GrCCCoverageProcessor::Shader;
-
-static constexpr int kAttribIdx_X = 0;
-static constexpr int kAttribIdx_Y = 1;
-static constexpr int kAttribIdx_VertexData = 2;
-
-/**
- * This class and its subclasses implement the coverage processor with vertex shaders.
- */
+// This class implements the coverage processor with vertex shaders.
class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor {
-protected:
- VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
+public:
+ VSImpl(std::unique_ptr<Shader> shader, int numSides)
+ : fShader(std::move(shader)), fNumSides(numSides) {}
+private:
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
FPCoordTransformIter&& transformIter) final {
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
}
- struct Coverages {
- const char* fCoverage = nullptr; // half
- const char* fAttenuatedCoverage = nullptr; // half2
- };
-
- void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
- const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
-
- // Vertex shader.
- GrGLSLVertexBuilder* v = args.fVertBuilder;
- int numInputPoints = proc.numInputPoints();
-
- const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
- v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
- numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
- proc.getAttrib(kAttribIdx_Y).fName, swizzle);
-
- if (WindMethod::kCrossProduct == proc.fWindMethod) {
- v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
- "pts[0] - pts[2]));");
- if (4 == numInputPoints) {
- v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
- "pts[0] - pts[3]));");
- }
- v->codeAppend ("half wind = sign(area_x2);");
- } else {
- SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
- SkASSERT(3 == numInputPoints);
- SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
- v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
- }
-
- float bloat = kAABloatRadius;
-#ifdef SK_DEBUG
- if (proc.debugBloatEnabled()) {
- bloat *= proc.debugBloat();
- }
-#endif
- v->defineConstant("bloat", bloat);
-
- Coverages coverages;
- this->emitVertexPosition(proc, v, gpArgs, &coverages);
- SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType());
-
- GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
- SkString varyingCode;
- fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
- gpArgs->fPositionVar.c_str(), coverages.fCoverage,
- coverages.fAttenuatedCoverage);
- v->codeAppend(varyingCode.c_str());
-
- varyingHandler->emitAttributes(proc);
- SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
-
- // Fragment shader.
- fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
- }
-
- virtual void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, GrGPArgs*,
- Coverages* outCoverages) const = 0;
-
- virtual ~VSImpl() {}
+ void onEmitCode(EmitArgs&, GrGPArgs*) override;
const std::unique_ptr<Shader> fShader;
-
- typedef GrGLSLGeometryProcessor INHERITED;
+ const int fNumSides;
};
+static constexpr int kAttribIdx_X = 0; // Transposed X values of all input points.
+static constexpr int kAttribIdx_Y = 1; // Transposed Y values of all input points.
+static constexpr int kAttribIdx_VertexData = 2;
+
+// Vertex data tells the shader how to offset vertices for conservative raster, as well as how to
+// calculate coverage values for corners and edges.
static constexpr int kVertexData_LeftNeighborIdShift = 10;
static constexpr int kVertexData_RightNeighborIdShift = 8;
static constexpr int kVertexData_BloatIdxShift = 6;
@@ -104,10 +42,6 @@ static constexpr int kVertexData_IsCornerBit = 1 << 4;
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
static constexpr int kVertexData_IsHullBit = 1 << 2;
-/**
- * Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to
- * calculate initial coverage values for edges. See VSHullAndEdgeImpl.
- */
static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
int32_t bloatIdx, int32_t cornerID,
int32_t extraData = 0) {
@@ -244,7 +178,8 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = {
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
-static constexpr int32_t kHull4Vertices[] = {
+// Curves, including quadratics, are drawn with a four-sided hull.
+static constexpr int32_t kCurveVertices[] = {
hull_vertex_data(0, 0, 4),
hull_vertex_data(0, 1, 4),
hull_vertex_data(0, 2, 4),
@@ -269,16 +204,16 @@ static constexpr int32_t kHull4Vertices[] = {
corner_vertex_data(2, 3, 0, 3),
};
-GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
-static constexpr uint16_t kHull4IndicesAsStrips[] = {
+static constexpr uint16_t kCurveIndicesAsStrips[] = {
1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally).
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.
+ 17, 16, 18, 19 // Final corner.
};
-static constexpr uint16_t kHull4IndicesAsTris[] = {
+static constexpr uint16_t kCurveIndicesAsTris[] = {
// First half of the hull (split diagonally).
1, 0, 2,
0, 11, 2,
@@ -297,243 +232,265 @@ static constexpr uint16_t kHull4IndicesAsTris[] = {
13, 12, 14,
12, 15, 14,
- // Second corner.
+ // Final corner.
17, 16, 18,
16, 19, 18,
};
-GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
-
-
-/**
- * Generates a conservative raster hull around a triangle or curve. For triangles we generate
- * additional conservative rasters with coverage ramps around the edges and corners.
- *
- * Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
- * coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
- * 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
- * ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
- * previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
- * triangle.
- *
- * Curves are drawn in two separate passes. Here we just draw a conservative raster around the input
- * points. The Shader takes care of everything else for now. The final curve corners get touched up
- * in a later step by VSCornerImpl.
- */
-class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
-public:
- VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides)
- : VSImpl(std::move(shader)), fNumSides(numSides) {}
-
- void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v,
- GrGPArgs* gpArgs, Coverages* outCoverages) const override {
- 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;",
- proc.getAttrib(kAttribIdx_VertexData).fName,
- ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
- ((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
- (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
- (fNumSides - 1),
- proc.getAttrib(kAttribIdx_VertexData).fName);
-
- // Here we generate conservative raster geometry for the input polygon. It is the convex
- // hull of N pixel-size boxes, one centered on each the input points. Each corner has three
- // vertices, where one or two may cause degenerate triangles. The vertex data tells us how
- // to offset each vertex. Triangle edges and corners are also handled here using the same
- // concept. For more details on conservative raster, see:
- // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
- v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
- v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
- hullPts, kVertexData_LeftNeighborIdShift);
- v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
- hullPts, kVertexData_RightNeighborIdShift);
-
- v->codeAppend ("float2 leftbloat = sign(corner - left);");
- v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
- "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
-
- v->codeAppend ("float2 rightbloat = sign(right - corner);");
- v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
- "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
-
- v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
-
- v->codeAppend ("float2 bloatdir = leftbloat;");
-
- 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->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);");
-
- // 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
- // continue rotating 90 degrees clockwise until we reach the desired raster vertex for this
- // invocation. Corners with less than 3 corresponding raster vertices will result in
- // redundant vertices and degenerate triangles.
- v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
- proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
- v->codeAppend ("switch (bloatidx) {");
- 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.
- v->codeAppend ( "}");
- // fallthru.
- v->codeAppend ( "case 1:");
- v->codeAppendf( "if (any(left_right_notequal)) {");
- v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
- v->codeAppend ( "}");
- // fallthru.
- v->codeAppend ("}");
-
- v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
-
- v->codeAppend ("half left_coverage; {");
- Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
- v->codeAppend ("}");
-
- v->codeAppend ("half right_coverage; {");
- Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
- v->codeAppend ("}");
-
- v->codeAppend ("half attenuation; {");
- Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
- 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?
- proc.getAttrib(kAttribIdx_VertexData).fName,
- kVertexData_InvertNegativeCoverageBit);
- v->codeAppend ( "coverage = -1 - coverage;");
- v->codeAppend ("}");
+GR_DECLARE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
+
+// Generates a conservative raster hull around a triangle or curve. For triangles we generate
+// additional conservative rasters with coverage ramps around the edges and corners.
+//
+// Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
+// coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
+// 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
+// ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
+// previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
+// triangle.
+//
+// Curves are drawn in two separate passes. Here we just draw a conservative raster around the input
+// points. The Shader takes care of everything else for now. The final curve corners get touched up
+// in a later step by VSCornerImpl.
+void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
+ const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
+ GrGLSLVertexBuilder* v = args.fVertBuilder;
+ int numInputPoints = proc.numInputPoints();
+
+ const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
+ v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
+ numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
+ proc.getAttrib(kAttribIdx_Y).fName, swizzle);
+
+ if (WindMethod::kCrossProduct == proc.fWindMethod) {
+ v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
+ "pts[0] - pts[2]));");
+ if (4 == numInputPoints) {
+ v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
+ "pts[0] - pts[3]));");
}
+ v->codeAppend ("half wind = sign(area_x2);");
+ } else {
+ SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
+ SkASSERT(3 == numInputPoints);
+ SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
+ v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
+ }
- // Corner boxes require attenuation.
- v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
+ float bloat = kAABloatRadius;
+#ifdef SK_DEBUG
+ if (proc.debugBloatEnabled()) {
+ bloat *= proc.debugBloat();
+ }
+#endif
+ v->defineConstant("bloat", bloat);
+
+ 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;",
+ proc.getAttrib(kAttribIdx_VertexData).fName,
+ ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
+ ((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
+ (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
+ (fNumSides - 1),
+ proc.getAttrib(kAttribIdx_VertexData).fName);
+
+ // Here we generate conservative raster geometry for the input polygon. It is the convex
+ // hull of N pixel-size boxes, one centered on each the input points. Each corner has three
+ // vertices, where one or two may cause degenerate triangles. The vertex data tells us how
+ // to offset each vertex. Triangle edges and corners are also handled here using the same
+ // concept. For more details on conservative raster, see:
+ // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
+ v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
+ v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
+ hullPts, kVertexData_LeftNeighborIdShift);
+ v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
+ hullPts, kVertexData_RightNeighborIdShift);
+
+ v->codeAppend ("float2 leftbloat = sign(corner - left);");
+ v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
+ "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
+
+ v->codeAppend ("float2 rightbloat = sign(right - corner);");
+ v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
+ "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
+
+ v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
+
+ v->codeAppend ("float2 bloatdir = leftbloat;");
+
+ 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->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);");
+
+ // 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
+ // continue rotating 90 degrees clockwise until we reach the desired raster vertex for this
+ // invocation. Corners with less than 3 corresponding raster vertices will result in
+ // redundant vertices and degenerate triangles.
+ v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
+ proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
+ v->codeAppend ("switch (bloatidx) {");
+ 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.
+ v->codeAppend ( "}");
+ // fallthru.
+ v->codeAppend ( "case 1:");
+ v->codeAppendf( "if (any(left_right_notequal)) {");
+ v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
+ v->codeAppend ( "}");
+ // fallthru.
+ v->codeAppend ("}");
+
+ v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
+ gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
+
+ v->codeAppend ("half left_coverage; {");
+ Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
+ v->codeAppend ("}");
+
+ v->codeAppend ("half right_coverage; {");
+ Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
+ v->codeAppend ("}");
+
+ v->codeAppend ("half attenuation; {");
+ Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
+ 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)) {", // 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->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
+ proc.getAttrib(kAttribIdx_VertexData).fName,
+ kVertexData_InvertNegativeCoverageBit);
+ v->codeAppend ( "coverage = -1 - coverage;");
v->codeAppend ("}");
+ }
- v->codeAppend ("coverage *= wind;");
- outCoverages->fCoverage = "coverage";
+ // Corner boxes require attenuation.
+ v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
- v->codeAppend ("attenuated_coverage.x *= wind;");
- outCoverages->fAttenuatedCoverage = "attenuated_coverage";
+ 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;");
}
-
-private:
- const int fNumSides;
-};
+ // 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 ("}");
+
+ GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+ SkString varyingCode;
+ v->codeAppend ("coverage *= wind;");
+ v->codeAppend ("attenuated_coverage.x *= wind;");
+ fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
+ gpArgs->fPositionVar.c_str(), "coverage", "attenuated_coverage");
+ v->codeAppend(varyingCode.c_str());
+
+ varyingHandler->emitAttributes(proc);
+ SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
+
+ // Fragment shader.
+ fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
+}
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
SkASSERT(Impl::kVertexShader == fImpl);
const GrCaps& caps = *rp->caps();
- switch (fRenderPass) {
- case RenderPass::kTriangles: {
+ switch (fPrimitiveType) {
+ case PrimitiveType::kTriangles: {
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
- fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
- sizeof(kTriangleVertices),
- kTriangleVertices,
- gTriangleVertexBufferKey);
+ fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
+ sizeof(kTriangleVertices),
+ kTriangleVertices,
+ gTriangleVertexBufferKey);
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
if (caps.usePrimitiveRestart()) {
- fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
- sizeof(kTriangleIndicesAsStrips),
- kTriangleIndicesAsStrips,
- gTriangleIndexBufferKey);
- fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
+ fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+ sizeof(kTriangleIndicesAsStrips),
+ kTriangleIndicesAsStrips,
+ gTriangleIndexBufferKey);
+ fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
} else {
- fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
- sizeof(kTriangleIndicesAsTris),
- kTriangleIndicesAsTris,
- gTriangleIndexBufferKey);
- fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
+ fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+ sizeof(kTriangleIndicesAsTris),
+ kTriangleIndicesAsTris,
+ gTriangleIndexBufferKey);
+ fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
}
break;
}
- case RenderPass::kQuadratics:
- case RenderPass::kCubics: {
- GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
- fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices),
- kHull4Vertices, gHull4VertexBufferKey);
- GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
+ case PrimitiveType::kQuadratics:
+ case PrimitiveType::kCubics: {
+ GR_DEFINE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
+ fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
+ sizeof(kCurveVertices), kCurveVertices,
+ gCurveVertexBufferKey);
+ GR_DEFINE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
if (caps.usePrimitiveRestart()) {
- fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
- sizeof(kHull4IndicesAsStrips),
- kHull4IndicesAsStrips,
- gHull4IndexBufferKey);
- fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips);
+ fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+ sizeof(kCurveIndicesAsStrips),
+ kCurveIndicesAsStrips,
+ gCurveIndexBufferKey);
+ fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsStrips);
} else {
- fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
- sizeof(kHull4IndicesAsTris),
- kHull4IndicesAsTris,
- gHull4IndexBufferKey);
- fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris);
+ fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
+ sizeof(kCurveIndicesAsTris),
+ kCurveIndicesAsTris,
+ gCurveIndexBufferKey);
+ fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsTris);
}
break;
}
-
- case RenderPass::kTriangleCorners:
- case RenderPass::kQuadraticCorners:
- case RenderPass::kCubicCorners:
- SK_ABORT("Corners are not used by VSImpl.");
}
- if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
+ if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) {
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
SkASSERT(kAttribIdx_X == this->numAttribs());
@@ -563,32 +520,28 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
if (caps.usePrimitiveRestart()) {
this->setWillUsePrimitiveRestart();
- fPrimitiveType = GrPrimitiveType::kTriangleStrip;
+ fVSTriangleType = GrPrimitiveType::kTriangleStrip;
} else {
- fPrimitiveType = GrPrimitiveType::kTriangles;
+ fVSTriangleType = GrPrimitiveType::kTriangles;
}
}
void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceCount,
int baseInstance, SkTArray<GrMesh>* out) const {
SkASSERT(Impl::kVertexShader == fImpl);
- GrMesh& mesh = out->emplace_back(fPrimitiveType);
- mesh.setIndexedInstanced(fIndexBuffer.get(), fNumIndicesPerInstance, instanceBuffer,
+ GrMesh& mesh = out->emplace_back(fVSTriangleType);
+ mesh.setIndexedInstanced(fVSIndexBuffer.get(), fVSNumIndicesPerInstance, instanceBuffer,
instanceCount, baseInstance);
- mesh.setVertexData(fVertexBuffer.get(), 0);
+ mesh.setVertexData(fVSVertexBuffer.get(), 0);
}
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
- switch (fRenderPass) {
- case RenderPass::kTriangles:
- return new VSHullAndEdgeImpl(std::move(shadr), 3);
- case RenderPass::kQuadratics:
- case RenderPass::kCubics:
- return new VSHullAndEdgeImpl(std::move(shadr), 4);
- case RenderPass::kTriangleCorners:
- case RenderPass::kQuadraticCorners:
- case RenderPass::kCubicCorners:
- SK_ABORT("Corners are not used by VSImpl.");
+ switch (fPrimitiveType) {
+ case PrimitiveType::kTriangles:
+ return new VSImpl(std::move(shadr), 3);
+ case PrimitiveType::kQuadratics:
+ case PrimitiveType::kCubics:
+ return new VSImpl(std::move(shadr), 4);
}
SK_ABORT("Invalid RenderPass");
return nullptr;
diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp
index 1114367ac7..abeb164bbf 100644
--- a/src/gpu/ccpr/GrCCPathParser.cpp
+++ b/src/gpu/ccpr/GrCCPathParser.cpp
@@ -500,7 +500,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
const SkIRect& drawBounds) const {
- using RenderPass = GrCCCoverageProcessor::RenderPass;
+ using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
using WindMethod = GrCCCoverageProcessor::WindMethod;
SkASSERT(fInstanceBuffer);
@@ -511,54 +511,40 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
SkBlendMode::kPlus);
if (batchTotalCounts.fTriangles) {
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
+ this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
- WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles,
- drawBounds); // Might get skipped.
}
if (batchTotalCounts.fWoundTriangles) {
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
+ this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
drawBounds);
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
- WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
- drawBounds); // Might get skipped.
}
if (batchTotalCounts.fQuadratics) {
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics,
- WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
+ this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kQuadratics,
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
}
if (batchTotalCounts.fCubics) {
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics,
- WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
- this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
+ this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kCubics,
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
}
}
-void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline,
+void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
CoverageCountBatchID batchID,
- GrCCCoverageProcessor::RenderPass renderPass,
+ GrCCCoverageProcessor::PrimitiveType primitiveType,
GrCCCoverageProcessor::WindMethod windMethod,
int PrimitiveTallies::*instanceType,
const SkIRect& drawBounds) const {
SkASSERT(pipeline.getScissorState().enabled());
- if (!GrCCCoverageProcessor::DoesRenderPass(renderPass, flushState->caps())) {
- return;
- }
-
// Don't call reset(), as that also resets the reserve count.
fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
- GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod);
+ GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType, windMethod);
SkASSERT(batchID > 0);
SkASSERT(batchID < fCoverageCountBatches.count());
@@ -600,9 +586,8 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
if (!fMeshesScratchBuffer.empty()) {
- SkASSERT(flushState->rtCommandBuffer());
- flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
- fDynamicStatesScratchBuffer.begin(),
- fMeshesScratchBuffer.count(), SkRect::Make(drawBounds));
+ proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(),
+ fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(),
+ SkRect::Make(drawBounds));
}
}
diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h
index b1a1ee5906..b28ab3b489 100644
--- a/src/gpu/ccpr/GrCCPathParser.h
+++ b/src/gpu/ccpr/GrCCPathParser.h
@@ -127,8 +127,8 @@ private:
void parsePath(const SkPath&, const SkPoint* deviceSpacePts);
void endContourIfNeeded(bool insideContour);
- void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
- GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod,
+ void drawPrimitives(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
+ GrCCCoverageProcessor::PrimitiveType, GrCCCoverageProcessor::WindMethod,
int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
// Staging area for the path being parsed.