ccpr: Various cleanups

Renames GrCCCoverageProcessor::RenderPass to PrimitiveType and handles
corners as subpasses instead. Various touchups to coverage processors
now that the overhaul is complete. This change should be strictly a
refactor.

Bug: skia:
Change-Id: I52852463330d5ec71fae7e19fadccd9ede8b2c16
Reviewed-on: https://skia-review.googlesource.com/116169
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2018-03-26 19:15:22 -06:00 committed by Skia Commit-Bot
parent 9b685238f6
commit 8dfc70f7e2
7 changed files with 442 additions and 522 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -228,6 +228,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));
}

View File

@ -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);
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();
/**
* 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) {}
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);
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 ("}");
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]));");
}
// Corner boxes require attenuation.
v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
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->codeAppend ("coverage *= wind;");
outCoverages->fCoverage = "coverage";
v->codeAppend ("attenuated_coverage.x *= wind;");
outCoverages->fAttenuatedCoverage = "attenuated_coverage";
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);
}
private:
const int fNumSides;
};
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)) {", // Invert coverage?
proc.getAttrib(kAttribIdx_VertexData).fName,
kVertexData_InvertNegativeCoverageBit);
v->codeAppend ( "coverage = -1 - coverage;");
v->codeAppend ("}");
}
// Corner boxes require attenuation.
v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
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 ("}");
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;

View File

@ -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));
}
}

View File

@ -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.