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:
parent
9b685238f6
commit
8dfc70f7e2
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user