Revert "ccpr: Draw curves in a single pass"
This reverts commit df04ce2949
.
Reason for revert: Going to revisit AAA quality
Original change's description:
> ccpr: Draw curves in a single pass
>
> Throws out the complicated MSAA curve corner shaders, and instead just
> ramps coverage to zero at bloat vertices that fall outside the curve.
>
> Updates SampleCCPRGeometry to better visualize this new geometry by
> clearing to black and drawing with SkBlendMode::kPlus.
>
> Bug: skia:
> Change-Id: Ibe86cbc741d8b015127b10dd43e3b52e7cb35732
> Reviewed-on: https://skia-review.googlesource.com/112626
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>
TBR=bsalomon@google.com,csmartdalton@google.com
Change-Id: I014baa60b248d870717f5ee8794e0bed66da86e6
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/113181
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
fe462efbcb
commit
baf3e78092
@ -32,6 +32,10 @@ using RenderPass = GrCCCoverageProcessor::RenderPass;
|
|||||||
|
|
||||||
static constexpr float kDebugBloat = 40;
|
static constexpr float kDebugBloat = 40;
|
||||||
|
|
||||||
|
static int is_quadratic(RenderPass pass) {
|
||||||
|
return pass == RenderPass::kQuadratics || pass == RenderPass::kQuadraticCorners;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It
|
* This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It
|
||||||
* increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green,
|
* increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green,
|
||||||
@ -115,13 +119,14 @@ static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3]
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||||
canvas->clear(SK_ColorBLACK);
|
SkAutoCanvasRestore acr(canvas, true);
|
||||||
|
canvas->setMatrix(SkMatrix::I());
|
||||||
|
|
||||||
SkPath outline;
|
SkPath outline;
|
||||||
outline.moveTo(fPoints[0]);
|
outline.moveTo(fPoints[0]);
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
|
||||||
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
|
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
|
||||||
} else if (RenderPass::kQuadratics == fRenderPass) {
|
} else if (is_quadratic(fRenderPass)) {
|
||||||
outline.quadTo(fPoints[1], fPoints[3]);
|
outline.quadTo(fPoints[1], fPoints[3]);
|
||||||
} else {
|
} else {
|
||||||
outline.lineTo(fPoints[1]);
|
outline.lineTo(fPoints[1]);
|
||||||
@ -130,7 +135,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkPaint outlinePaint;
|
SkPaint outlinePaint;
|
||||||
outlinePaint.setColor(0x80ffffff);
|
outlinePaint.setColor(0x30000000);
|
||||||
outlinePaint.setStyle(SkPaint::kStroke_Style);
|
outlinePaint.setStyle(SkPaint::kStroke_Style);
|
||||||
outlinePaint.setStrokeWidth(0);
|
outlinePaint.setStrokeWidth(0);
|
||||||
outlinePaint.setAntiAlias(true);
|
outlinePaint.setAntiAlias(true);
|
||||||
@ -154,7 +159,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
|
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
|
||||||
rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
|
rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
|
||||||
caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass));
|
caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass));
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
|
||||||
caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
|
caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -166,7 +171,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
pointsPaint.setStrokeWidth(8);
|
pointsPaint.setStrokeWidth(8);
|
||||||
pointsPaint.setAntiAlias(true);
|
pointsPaint.setAntiAlias(true);
|
||||||
|
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
|
||||||
int w = this->width(), h = this->height();
|
int w = this->width(), h = this->height();
|
||||||
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
|
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
|
||||||
draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
|
draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
|
||||||
@ -179,7 +184,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
|
|
||||||
SkPaint captionPaint;
|
SkPaint captionPaint;
|
||||||
captionPaint.setTextSize(20);
|
captionPaint.setTextSize(20);
|
||||||
captionPaint.setColor(SK_ColorWHITE);
|
captionPaint.setColor(SK_ColorBLACK);
|
||||||
captionPaint.setAntiAlias(true);
|
captionPaint.setAntiAlias(true);
|
||||||
canvas->drawText(caption.c_str(), caption.size(), 10, 30, captionPaint);
|
canvas->drawText(caption.c_str(), caption.size(), 10, 30, captionPaint);
|
||||||
}
|
}
|
||||||
@ -188,7 +193,7 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
fTriPointInstances.reset();
|
fTriPointInstances.reset();
|
||||||
fQuadPointInstances.reset();
|
fQuadPointInstances.reset();
|
||||||
|
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
|
||||||
double t[2], s[2];
|
double t[2], s[2];
|
||||||
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
||||||
GrCCGeometry geometry;
|
GrCCGeometry geometry;
|
||||||
@ -212,7 +217,7 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (RenderPass::kQuadratics == fRenderPass) {
|
} else if (is_quadratic(fRenderPass)) {
|
||||||
GrCCGeometry geometry;
|
GrCCGeometry geometry;
|
||||||
geometry.beginContour(fPoints[0]);
|
geometry.beginContour(fPoints[0]);
|
||||||
geometry.quadraticTo(fPoints[1], fPoints[3]);
|
geometry.quadraticTo(fPoints[1], fPoints[3]);
|
||||||
@ -249,7 +254,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
|||||||
SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
|
SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
|
||||||
|
|
||||||
SkSTArray<1, GrMesh> mesh;
|
SkSTArray<1, GrMesh> mesh;
|
||||||
if (RenderPass::kCubics == fView->fRenderPass) {
|
if (GrCCCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) {
|
||||||
sk_sp<GrBuffer> instBuff(rp->createBuffer(
|
sk_sp<GrBuffer> instBuff(rp->createBuffer(
|
||||||
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
|
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
|
||||||
kVertex_GrBufferType, kDynamic_GrAccessPattern,
|
kVertex_GrBufferType, kDynamic_GrAccessPattern,
|
||||||
@ -270,11 +275,11 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
|
GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
|
||||||
SkBlendMode::kPlus);
|
SkBlendMode::kSrcOver);
|
||||||
|
|
||||||
if (glGpu) {
|
if (glGpu) {
|
||||||
glGpu->handleDirtyContext();
|
glGpu->handleDirtyContext();
|
||||||
// GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
|
GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
|
||||||
GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
|
GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +318,7 @@ private:
|
|||||||
|
|
||||||
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
|
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
if (RenderPass::kCubics != fRenderPass && 2 == i) {
|
if (!GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass) && 2 == i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
|
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
|
||||||
@ -337,7 +342,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
|
|||||||
}
|
}
|
||||||
SkUnichar unichar;
|
SkUnichar unichar;
|
||||||
if (SampleCode::CharQ(*evt, &unichar)) {
|
if (SampleCode::CharQ(*evt, &unichar)) {
|
||||||
if (unichar >= '1' && unichar <= '4') {
|
if (unichar >= '1' && unichar <= '6') {
|
||||||
fRenderPass = RenderPass(unichar - '1');
|
fRenderPass = RenderPass(unichar - '1');
|
||||||
this->updateAndInval();
|
this->updateAndInval();
|
||||||
return true;
|
return true;
|
||||||
|
@ -15,59 +15,35 @@
|
|||||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||||
|
|
||||||
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
|
||||||
GrProcessorKeyBuilder* b) const {
|
|
||||||
int key = (int)fRenderPass << 2;
|
|
||||||
if (WindMethod::kInstanceData == fWindMethod) {
|
|
||||||
key |= 2;
|
|
||||||
}
|
|
||||||
if (Impl::kVertexShader == fImpl) {
|
|
||||||
key |= 1;
|
|
||||||
}
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
uint32_t bloatBits;
|
|
||||||
memcpy(&bloatBits, &fDebugBloat, 4);
|
|
||||||
b->add32(bloatBits);
|
|
||||||
#endif
|
|
||||||
b->add32(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
|
||||||
std::unique_ptr<Shader> shader;
|
|
||||||
switch (fRenderPass) {
|
|
||||||
case RenderPass::kTriangles:
|
|
||||||
shader = skstd::make_unique<GrCCTriangleShader>();
|
|
||||||
break;
|
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
shader = skstd::make_unique<GrCCTriangleCornerShader>();
|
|
||||||
break;
|
|
||||||
case RenderPass::kQuadratics:
|
|
||||||
shader = skstd::make_unique<GrCCQuadraticShader>();
|
|
||||||
break;
|
|
||||||
case RenderPass::kCubics:
|
|
||||||
shader = skstd::make_unique<GrCCCubicShader>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
|
|
||||||
: this->createVSImpl(std::move(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc,
|
void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc,
|
||||||
GrGLSLFPFragmentBuilder* f,
|
GrGLSLFPFragmentBuilder* f,
|
||||||
const char* skOutputColor,
|
const char* skOutputColor,
|
||||||
const char* skOutputCoverage) const {
|
const char* skOutputCoverage) const {
|
||||||
f->codeAppendf("half coverage = 0;");
|
f->codeAppendf("half coverage = 0;");
|
||||||
this->onEmitFragmentCode(proc, f, "coverage");
|
this->onEmitFragmentCode(f, "coverage");
|
||||||
f->codeAppendf("%s.a = coverage;", skOutputColor);
|
f->codeAppendf("%s.a = coverage;", skOutputColor);
|
||||||
f->codeAppendf("%s = half4(1);", skOutputCoverage);
|
f->codeAppendf("%s = half4(1);", skOutputCoverage);
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
if (proc.debugVisualizationsEnabled()) {
|
if (proc.debugVisualizationsEnabled()) {
|
||||||
f->codeAppendf("%s = half4(-%s.a, %s.a, 0, abs(%s.a));",
|
f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);",
|
||||||
skOutputColor, skOutputColor, skOutputColor, skOutputColor);
|
skOutputColor, skOutputColor, skOutputColor);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GrCCCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder* s,
|
||||||
|
const char* leftPt,
|
||||||
|
const char* rightPt,
|
||||||
|
const char* outputDistanceEquation) {
|
||||||
|
s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
|
||||||
|
rightPt, leftPt, leftPt, rightPt);
|
||||||
|
s->codeAppend ("float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);");
|
||||||
|
// When nwidth=0, wind must also be 0 (and coverage * wind = 0). So it doesn't matter what we
|
||||||
|
// come up with here as long as it isn't NaN or Inf.
|
||||||
|
s->codeAppend ("n /= (0 != nwidth) ? nwidth : 1;");
|
||||||
|
s->codeAppendf("%s = float3(-n, dot(n, %s) - .5);", outputDistanceEquation, leftPt);
|
||||||
|
}
|
||||||
|
|
||||||
void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s,
|
void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s,
|
||||||
const char* leftPt,
|
const char* leftPt,
|
||||||
const char* rightPt,
|
const char* rightPt,
|
||||||
@ -102,3 +78,66 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGe
|
|||||||
// GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0.
|
// GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0.
|
||||||
s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage);
|
s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f,
|
||||||
|
const char* samplesName) {
|
||||||
|
// Standard DX11 sample locations.
|
||||||
|
#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
|
||||||
|
f->defineConstant("float2[8]", samplesName, "float2[8]("
|
||||||
|
"float2(+1, -3)/16, float2(-1, +3)/16, float2(+5, +1)/16, float2(-3, -5)/16, "
|
||||||
|
"float2(-5, +5)/16, float2(-7, -1)/16, float2(+3, +7)/16, float2(+7, -7)/16."
|
||||||
|
")");
|
||||||
|
return 8;
|
||||||
|
#else
|
||||||
|
f->defineConstant("float2[16]", samplesName, "float2[16]("
|
||||||
|
"float2(+1, +1)/16, float2(-1, -3)/16, float2(-3, +2)/16, float2(+4, -1)/16, "
|
||||||
|
"float2(-5, -2)/16, float2(+2, +5)/16, float2(+5, +3)/16, float2(+3, -5)/16, "
|
||||||
|
"float2(-2, +6)/16, float2( 0, -7)/16, float2(-4, -6)/16, float2(-6, +4)/16, "
|
||||||
|
"float2(-8, 0)/16, float2(+7, -4)/16, float2(+6, +7)/16, float2(-7, -8)/16."
|
||||||
|
")");
|
||||||
|
return 16;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
||||||
|
GrProcessorKeyBuilder* b) const {
|
||||||
|
int key = (int)fRenderPass << 2;
|
||||||
|
if (WindMethod::kInstanceData == fWindMethod) {
|
||||||
|
key |= 2;
|
||||||
|
}
|
||||||
|
if (Impl::kVertexShader == fImpl) {
|
||||||
|
key |= 1;
|
||||||
|
}
|
||||||
|
#ifdef SK_DEBUG
|
||||||
|
uint32_t bloatBits;
|
||||||
|
memcpy(&bloatBits, &fDebugBloat, 4);
|
||||||
|
b->add32(bloatBits);
|
||||||
|
#endif
|
||||||
|
b->add32(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
||||||
|
std::unique_ptr<Shader> shader;
|
||||||
|
switch (fRenderPass) {
|
||||||
|
case RenderPass::kTriangles:
|
||||||
|
shader = skstd::make_unique<GrCCTriangleShader>();
|
||||||
|
break;
|
||||||
|
case RenderPass::kTriangleCorners:
|
||||||
|
shader = skstd::make_unique<GrCCTriangleCornerShader>();
|
||||||
|
break;
|
||||||
|
case RenderPass::kQuadratics:
|
||||||
|
shader = skstd::make_unique<GrCCQuadraticHullShader>();
|
||||||
|
break;
|
||||||
|
case RenderPass::kQuadraticCorners:
|
||||||
|
shader = skstd::make_unique<GrCCQuadraticCornerShader>();
|
||||||
|
break;
|
||||||
|
case RenderPass::kCubics:
|
||||||
|
shader = skstd::make_unique<GrCCCubicHullShader>();
|
||||||
|
break;
|
||||||
|
case RenderPass::kCubicCorners:
|
||||||
|
shader = skstd::make_unique<GrCCCubicCornerShader>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
|
||||||
|
: this->createVSImpl(std::move(shader));
|
||||||
|
}
|
||||||
|
@ -53,15 +53,20 @@ public:
|
|||||||
void set(const SkPoint[4], float dx, float dy);
|
void set(const SkPoint[4], float dx, float dy);
|
||||||
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
|
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
|
||||||
};
|
};
|
||||||
// Here we enumerate every render pass needed in order to produce a complete coverage count
|
|
||||||
// mask. Triangles require two render passes: One to draw a rough outline, and a second pass to
|
// All primitive shapes (triangles and closed, convex bezier curves) require two
|
||||||
// touch up the corners. This is an exhaustive list of all ccpr coverage shaders.
|
// render passes: One to draw a rough outline of the shape, and a second pass to touch up the
|
||||||
|
// corners. Here we enumerate every 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 {
|
enum class RenderPass {
|
||||||
kTriangles,
|
kTriangles,
|
||||||
kTriangleCorners,
|
kTriangleCorners,
|
||||||
kQuadratics,
|
kQuadratics,
|
||||||
|
kQuadraticCorners,
|
||||||
kCubics,
|
kCubics,
|
||||||
|
kCubicCorners
|
||||||
};
|
};
|
||||||
|
static bool RenderPassIsCubic(RenderPass);
|
||||||
static const char* RenderPassName(RenderPass);
|
static const char* RenderPassName(RenderPass);
|
||||||
|
|
||||||
enum class WindMethod : bool {
|
enum class WindMethod : bool {
|
||||||
@ -147,6 +152,13 @@ public:
|
|||||||
void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||||
const char* skOutputColor, const char* skOutputCoverage) const;
|
const char* skOutputColor, const char* skOutputCoverage) const;
|
||||||
|
|
||||||
|
// Defines an equation ("dot(float3(pt, 1), distance_equation)") that is -1 on the outside
|
||||||
|
// border of a conservative raster edge and 0 on the inside. 'leftPt' and 'rightPt' must be
|
||||||
|
// ordered clockwise.
|
||||||
|
static void EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder*, const char* leftPt,
|
||||||
|
const char* rightPt,
|
||||||
|
const char* outputDistanceEquation);
|
||||||
|
|
||||||
// Calculates an edge's coverage at a conservative raster vertex. The edge is defined by two
|
// Calculates an edge's coverage at a conservative raster vertex. The edge is defined by two
|
||||||
// clockwise-ordered points, 'leftPt' and 'rightPt'. 'rasterVertexDir' is a pair of +/-1
|
// clockwise-ordered points, 'leftPt' and 'rightPt'. 'rasterVertexDir' is a pair of +/-1
|
||||||
// values that point in the direction of conservative raster bloat, starting from an
|
// values that point in the direction of conservative raster bloat, starting from an
|
||||||
@ -169,7 +181,7 @@ public:
|
|||||||
const char* wind) = 0;
|
const char* wind) = 0;
|
||||||
|
|
||||||
// Emits the fragment code that calculates a pixel's signed coverage value.
|
// Emits the fragment code that calculates a pixel's signed coverage value.
|
||||||
virtual void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*,
|
||||||
const char* outputCoverage) const = 0;
|
const char* outputCoverage) const = 0;
|
||||||
|
|
||||||
// Returns the name of a Shader's internal varying at the point where where its value is
|
// Returns the name of a Shader's internal varying at the point where where its value is
|
||||||
@ -179,6 +191,12 @@ public:
|
|||||||
SkASSERT(Scope::kVertToGeo != varying.scope());
|
SkASSERT(Scope::kVertToGeo != varying.scope());
|
||||||
return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut();
|
return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defines a global float2 array that contains MSAA sample locations as offsets from pixel
|
||||||
|
// center. Subclasses can use this for software multisampling.
|
||||||
|
//
|
||||||
|
// Returns the number of samples.
|
||||||
|
static int DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f, const char* samplesName);
|
||||||
};
|
};
|
||||||
|
|
||||||
class GSImpl;
|
class GSImpl;
|
||||||
@ -190,7 +208,7 @@ private:
|
|||||||
static constexpr float kAABloatRadius = 0.491111f;
|
static constexpr float kAABloatRadius = 0.491111f;
|
||||||
|
|
||||||
// Number of bezier points for curves, or 3 for triangles.
|
// Number of bezier points for curves, or 3 for triangles.
|
||||||
int numInputPoints() const { return RenderPass::kCubics == fRenderPass ? 4 : 3; }
|
int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; }
|
||||||
|
|
||||||
enum class Impl : bool {
|
enum class Impl : bool {
|
||||||
kGeometryShader,
|
kGeometryShader,
|
||||||
@ -251,12 +269,29 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con
|
|||||||
Sk2f::Store4(this, P0, P1, P2, W);
|
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) {
|
inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
|
||||||
switch (pass) {
|
switch (pass) {
|
||||||
case RenderPass::kTriangles: return "kTriangles";
|
case RenderPass::kTriangles: return "kTriangles";
|
||||||
case RenderPass::kTriangleCorners: return "kTriangleCorners";
|
case RenderPass::kTriangleCorners: return "kTriangleCorners";
|
||||||
case RenderPass::kQuadratics: return "kQuadratics";
|
case RenderPass::kQuadratics: return "kQuadratics";
|
||||||
|
case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
|
||||||
case RenderPass::kCubics: return "kCubics";
|
case RenderPass::kCubics: return "kCubics";
|
||||||
|
case RenderPass::kCubicCorners: return "kCubicCorners";
|
||||||
}
|
}
|
||||||
SK_ABORT("Invalid RenderPass");
|
SK_ABORT("Invalid RenderPass");
|
||||||
return "";
|
return "";
|
||||||
|
@ -76,7 +76,7 @@ protected:
|
|||||||
SkSTArray<2, GrShaderVar> emitArgs;
|
SkSTArray<2, GrShaderVar> emitArgs;
|
||||||
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
|
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
|
||||||
const char* coverage = nullptr;
|
const char* coverage = nullptr;
|
||||||
if (RenderPass::kTriangleCorners != proc.fRenderPass) {
|
if (RenderPass::kTriangles == proc.fRenderPass) {
|
||||||
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
||||||
}
|
}
|
||||||
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
||||||
@ -212,8 +212,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a conservative raster hull around a convex quadrilateral that encloses a cubic or
|
* Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
|
||||||
* quadratic, as well as its shared edge.
|
|
||||||
*/
|
*/
|
||||||
class GSHull4Impl : public GrCCCoverageProcessor::GSImpl {
|
class GSHull4Impl : public GrCCCoverageProcessor::GSImpl {
|
||||||
public:
|
public:
|
||||||
@ -232,85 +231,54 @@ public:
|
|||||||
// Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
|
// Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
|
||||||
// we can identify the points by their corresponding corner.
|
// we can identify the points by their corresponding corner.
|
||||||
//
|
//
|
||||||
// NOTE: For the hull we split the square down the diagonal from top-right to bottom-left,
|
// NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
|
||||||
// and generate it in two independent invocations. All invocations, including the shared
|
// the hull in two independent invocations. Each invocation designates the corner it will
|
||||||
// edge, designate the corner they will begin with as top-left.
|
// begin with as top-left.
|
||||||
g->codeAppendf("bool is_shared_edge = (2 == sk_InvocationID);");
|
g->codeAppend ("int i = sk_InvocationID * 2;");
|
||||||
g->codeAppendf("int i = !is_shared_edge ? sk_InvocationID * 2 : (%s > 0 ? 3 : 0);",
|
|
||||||
wind.c_str());
|
|
||||||
g->codeAppendf("float2 topleft = %s[i];", hullPts);
|
g->codeAppendf("float2 topleft = %s[i];", hullPts);
|
||||||
g->codeAppendf("float2 topright = %s[(i + (%s > 0 ? 1 : 3)) & 3];", hullPts, wind.c_str());
|
g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
|
||||||
g->codeAppendf("float2 bottomleft = %s[(i + (%s > 0 ? 3 : 1)) & 3];",
|
g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
|
||||||
hullPts, wind.c_str());
|
g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
|
||||||
g->codeAppendf("float2 bottomright = %s[(i + 2) & 3];", hullPts);
|
|
||||||
|
|
||||||
// Determine how much to outset the conservative raster hull from the relevant edges.
|
// Determine how much to outset the conservative raster hull from the relevant edges.
|
||||||
g->codeAppend ("float2 leftbloat = sign(topleft - bottomleft) * bloat;");
|
g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
|
||||||
g->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
|
"topleft.x > bottomleft.x ? -bloat : bloat);");
|
||||||
"0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
|
g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
|
||||||
|
"topright.x > topleft.x ? -bloat : +bloat);");
|
||||||
g->codeAppend ("float2 upbloat = sign(topright - topleft) * bloat;");
|
g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
|
||||||
g->codeAppend ("upbloat = float2(0 != upbloat.y ? upbloat.y : upbloat.x, "
|
"bottomright.x > topright.x ? -bloat : +bloat);");
|
||||||
"0 != upbloat.x ? -upbloat.x : -upbloat.y);");
|
|
||||||
|
|
||||||
g->codeAppend ("float2 rightbloat = sign(bottomright - topright) * bloat;");
|
|
||||||
g->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
|
|
||||||
"0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
|
|
||||||
|
|
||||||
// The hull raster has a coverage of +1 all around.
|
|
||||||
g->codeAppend ("half2 coverages = half2(+1);");
|
|
||||||
|
|
||||||
g->codeAppend ("if (is_shared_edge) {");
|
|
||||||
// On bloat vertices along the shared edge that fall outside the input
|
|
||||||
// points, ramp coverage to 0. We do this by using coverage=-1 to erase
|
|
||||||
// what the hull just wrote.
|
|
||||||
g->codeAppend ( "coverages = half2(-1, 0);");
|
|
||||||
// Reassign bloats to characterize a conservative raster around just the
|
|
||||||
// shared edge, rather than the entire hull.
|
|
||||||
g->codeAppend ( "leftbloat = rightbloat = -upbloat;");
|
|
||||||
g->codeAppend ("}");
|
|
||||||
|
|
||||||
// Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
|
// 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
|
// 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
|
// to a polygon with either one, two, or three vertices at each input point, depending on
|
||||||
// how sharp the corner is. The shared edge raster is the convex hull of 2 pixel-size boxes,
|
// how sharp the corner is. For more details on conservative raster, see:
|
||||||
// one at each endpoint. For more details on conservative raster, see:
|
|
||||||
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||||
g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
|
g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
|
||||||
g->codeAppend ("if (all(left_up_notequal)) {");
|
g->codeAppend ("if (all(left_up_notequal)) {");
|
||||||
// The top-left corner will have three conservative raster vertices.
|
// The top-left corner will have three conservative raster vertices.
|
||||||
// Emit the middle one first to the triangle strip.
|
// Emit the middle one first to the triangle strip.
|
||||||
g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x), coverages[0]);",
|
g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
|
||||||
emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
g->codeAppend ("if (any(left_up_notequal)) {");
|
g->codeAppend ("if (any(left_up_notequal)) {");
|
||||||
// Second conservative raster vertex for the top-left corner.
|
// Second conservative raster vertex for the top-left corner.
|
||||||
g->codeAppendf( "%s(topleft + leftbloat, coverages[1]);", emitVertexFn);
|
g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn);
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
|
|
||||||
g->codeAppendf("%s(topleft + upbloat, coverages[0]);", emitVertexFn);
|
// Main interior body of this invocation's half of the hull.
|
||||||
|
g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
|
||||||
g->codeAppend ("if (!is_shared_edge) {");
|
g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
|
||||||
// Main interior body of this invocation's half of the hull.
|
g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
|
||||||
g->codeAppendf( "%s(bottomleft + leftbloat, +1);", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
|
|
||||||
g->codeAppendf("%s(topright + (is_shared_edge ? rightbloat : upbloat), coverages[1]);",
|
|
||||||
emitVertexFn);
|
|
||||||
|
|
||||||
// Remaining two conservative raster vertices for the top-right corner.
|
// Remaining two conservative raster vertices for the top-right corner.
|
||||||
g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
|
g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
|
||||||
g->codeAppend ("if (any(up_right_notequal)) {");
|
g->codeAppend ("if (any(up_right_notequal)) {");
|
||||||
g->codeAppendf( "%s(topright + (is_shared_edge ? upbloat : rightbloat), "
|
g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn);
|
||||||
"coverages[0]);", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
g->codeAppend ("if (all(up_right_notequal)) {");
|
g->codeAppend ("if (all(up_right_notequal)) {");
|
||||||
g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x), coverages[0]);",
|
g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
|
||||||
emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
|
|
||||||
// 3 invocations: 2 hull invocations and 1 shared edge.
|
g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
|
||||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 3);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -344,15 +312,17 @@ private:
|
|||||||
|
|
||||||
void GrCCCoverageProcessor::initGS() {
|
void GrCCCoverageProcessor::initGS() {
|
||||||
SkASSERT(Impl::kGeometryShader == fImpl);
|
SkASSERT(Impl::kGeometryShader == fImpl);
|
||||||
if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) {
|
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
|
||||||
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
||||||
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
|
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
|
||||||
SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
|
SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
|
||||||
SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride());
|
SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride());
|
||||||
|
GR_STATIC_ASSERT(0 == offsetof(QuadPointInstance, fX));
|
||||||
} else {
|
} else {
|
||||||
this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType);
|
this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType);
|
||||||
SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2);
|
SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2);
|
||||||
SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride());
|
SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride());
|
||||||
|
GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX));
|
||||||
}
|
}
|
||||||
this->setWillUseGeoShader();
|
this->setWillUseGeoShader();
|
||||||
}
|
}
|
||||||
@ -378,6 +348,9 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Sh
|
|||||||
case RenderPass::kQuadratics:
|
case RenderPass::kQuadratics:
|
||||||
case RenderPass::kCubics:
|
case RenderPass::kCubics:
|
||||||
return new GSHull4Impl(std::move(shadr));
|
return new GSHull4Impl(std::move(shadr));
|
||||||
|
case RenderPass::kQuadraticCorners:
|
||||||
|
case RenderPass::kCubicCorners:
|
||||||
|
return new GSCornerImpl(std::move(shadr), 2);
|
||||||
}
|
}
|
||||||
SK_ABORT("Invalid RenderPass");
|
SK_ABORT("Invalid RenderPass");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -92,13 +92,13 @@ protected:
|
|||||||
static constexpr int kVertexData_LeftNeighborIdShift = 9;
|
static constexpr int kVertexData_LeftNeighborIdShift = 9;
|
||||||
static constexpr int kVertexData_RightNeighborIdShift = 7;
|
static constexpr int kVertexData_RightNeighborIdShift = 7;
|
||||||
static constexpr int kVertexData_BloatIdxShift = 5;
|
static constexpr int kVertexData_BloatIdxShift = 5;
|
||||||
static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 4;
|
static constexpr int kVertexData_InvertCoverageBit = 1 << 4;
|
||||||
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
|
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
|
||||||
static constexpr int kVertexData_IsHullBit = 1 << 2;
|
static constexpr int kVertexData_IsHullBit = 1 << 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to
|
* Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to
|
||||||
* calculate coverage values. See VSHullAndEdgeImpl.
|
* calculate initial coverage values for edges. See VSHullAndEdgeImpl.
|
||||||
*/
|
*/
|
||||||
static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
|
static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
|
||||||
int32_t bloatIdx, int32_t cornerID,
|
int32_t bloatIdx, int32_t cornerID,
|
||||||
@ -114,12 +114,15 @@ static constexpr int32_t hull_vertex_data(int32_t cornerID, int32_t bloatIdx, in
|
|||||||
kVertexData_IsHullBit);
|
kVertexData_IsHullBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int32_t edge_vertex_data(int32_t leftID, int rightID, int32_t bloatIdx,
|
static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int32_t bloatIdx,
|
||||||
int32_t extraData = 0) {
|
int n) {
|
||||||
return pack_vertex_data(leftID, leftID, bloatIdx, rightID, kVertexData_IsEdgeBit | extraData);
|
return pack_vertex_data(0 == endptIdx ? (edgeID + 1) % n : edgeID,
|
||||||
|
0 == endptIdx ? (edgeID + 1) % n : edgeID,
|
||||||
|
bloatIdx, 0 == endptIdx ? edgeID : (edgeID + 1) % n,
|
||||||
|
kVertexData_IsEdgeBit |
|
||||||
|
(!endptIdx ? kVertexData_InvertCoverageBit : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static constexpr int32_t kHull3AndEdgeVertices[] = {
|
static constexpr int32_t kHull3AndEdgeVertices[] = {
|
||||||
hull_vertex_data(0, 0, 3),
|
hull_vertex_data(0, 0, 3),
|
||||||
hull_vertex_data(0, 1, 3),
|
hull_vertex_data(0, 1, 3),
|
||||||
@ -131,26 +134,26 @@ static constexpr int32_t kHull3AndEdgeVertices[] = {
|
|||||||
hull_vertex_data(2, 1, 3),
|
hull_vertex_data(2, 1, 3),
|
||||||
hull_vertex_data(2, 2, 3),
|
hull_vertex_data(2, 2, 3),
|
||||||
|
|
||||||
edge_vertex_data(0, 1, 0),
|
edge_vertex_data(0, 0, 0, 3),
|
||||||
edge_vertex_data(0, 1, 1),
|
edge_vertex_data(0, 0, 1, 3),
|
||||||
edge_vertex_data(0, 1, 2),
|
edge_vertex_data(0, 0, 2, 3),
|
||||||
edge_vertex_data(1, 0, 0, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(0, 1, 0, 3),
|
||||||
edge_vertex_data(1, 0, 1, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(0, 1, 1, 3),
|
||||||
edge_vertex_data(1, 0, 2, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(0, 1, 2, 3),
|
||||||
|
|
||||||
edge_vertex_data(1, 2, 0),
|
edge_vertex_data(1, 0, 0, 3),
|
||||||
edge_vertex_data(1, 2, 1),
|
edge_vertex_data(1, 0, 1, 3),
|
||||||
edge_vertex_data(1, 2, 2),
|
edge_vertex_data(1, 0, 2, 3),
|
||||||
edge_vertex_data(2, 1, 0, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(1, 1, 0, 3),
|
||||||
edge_vertex_data(2, 1, 1, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(1, 1, 1, 3),
|
||||||
edge_vertex_data(2, 1, 2, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(1, 1, 2, 3),
|
||||||
|
|
||||||
edge_vertex_data(2, 0, 0),
|
edge_vertex_data(2, 0, 0, 3),
|
||||||
edge_vertex_data(2, 0, 1),
|
edge_vertex_data(2, 0, 1, 3),
|
||||||
edge_vertex_data(2, 0, 2),
|
edge_vertex_data(2, 0, 2, 3),
|
||||||
edge_vertex_data(0, 2, 0, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(2, 1, 0, 3),
|
||||||
edge_vertex_data(0, 2, 1, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(2, 1, 1, 3),
|
||||||
edge_vertex_data(0, 2, 2, kVertexData_InvertNegativeCoverageBit),
|
edge_vertex_data(2, 1, 2, 3),
|
||||||
};
|
};
|
||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey);
|
||||||
@ -198,7 +201,7 @@ static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = {
|
|||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey);
|
||||||
|
|
||||||
static constexpr int32_t kHull4AndEdgeVertices[] = {
|
static constexpr int32_t kHull4Vertices[] = {
|
||||||
hull_vertex_data(0, 0, 4),
|
hull_vertex_data(0, 0, 4),
|
||||||
hull_vertex_data(0, 1, 4),
|
hull_vertex_data(0, 1, 4),
|
||||||
hull_vertex_data(0, 2, 4),
|
hull_vertex_data(0, 2, 4),
|
||||||
@ -212,23 +215,17 @@ static constexpr int32_t kHull4AndEdgeVertices[] = {
|
|||||||
hull_vertex_data(3, 1, 4),
|
hull_vertex_data(3, 1, 4),
|
||||||
hull_vertex_data(3, 2, 4),
|
hull_vertex_data(3, 2, 4),
|
||||||
|
|
||||||
edge_vertex_data(0, 3, 0, kVertexData_InvertNegativeCoverageBit),
|
// No edges for now (beziers don't use edges).
|
||||||
edge_vertex_data(0, 3, 1),
|
|
||||||
edge_vertex_data(0, 3, 2),
|
|
||||||
edge_vertex_data(3, 0, 0),
|
|
||||||
edge_vertex_data(3, 0, 1),
|
|
||||||
edge_vertex_data(3, 0, 2, kVertexData_InvertNegativeCoverageBit),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeVertexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
|
||||||
|
|
||||||
static constexpr uint16_t kHull4AndEdgeIndicesAsStrips[] = {
|
static constexpr uint16_t kHull4IndicesAsStrips[] = {
|
||||||
1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally).
|
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.
|
7, 6, 8, 5, 9, 11, 10 // Second half of the hull.
|
||||||
13, 12, 14, 17, 15, 16 // Shared edge.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = {
|
static constexpr uint16_t kHull4IndicesAsTris[] = {
|
||||||
// First half of the hull (split diagonally).
|
// First half of the hull (split diagonally).
|
||||||
1, 0, 2,
|
1, 0, 2,
|
||||||
0, 11, 2,
|
0, 11, 2,
|
||||||
@ -242,30 +239,23 @@ static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = {
|
|||||||
8, 5, 9,
|
8, 5, 9,
|
||||||
5, 11, 9,
|
5, 11, 9,
|
||||||
9, 11, 10,
|
9, 11, 10,
|
||||||
|
|
||||||
// Shared edge.
|
|
||||||
13, 12, 14,
|
|
||||||
12, 17, 14,
|
|
||||||
14, 17, 15,
|
|
||||||
17, 16, 15,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a conservative raster hull around a triangle or curve. For triangles we generate
|
* Generates a conservative raster hull around a convex polygon. For triangles we generate
|
||||||
* additional conservative rasters with coverage ramps around the edges. For curves we
|
* additional conservative rasters around the edges and calculate coverage ramps.
|
||||||
* generate an additional raster with coverage ramps around its shared edge.
|
|
||||||
*
|
*
|
||||||
* Triangle rough outlines are drawn in two steps: (1) Draw a conservative raster of the entire
|
* Triangle rough outlines are drawn in two steps: (1) draw a conservative raster of the entire
|
||||||
* triangle, with a coverage of +1. (2) Draw conservative rasters around each edge, with a
|
* triangle, with a coverage of +1, and (2) draw conservative rasters around each edge, with a
|
||||||
* coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges
|
* coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges
|
||||||
* into smooth, antialiased ones. The final corners get touched up in a later step by VSCornerImpl.
|
* into smooth, antialiased ones.
|
||||||
*
|
*
|
||||||
* Curves are drawn in two steps: (1) Draw a conservative raster around the input points, passing
|
* Curve rough outlines are just the conservative raster of a convex quadrilateral that encloses the
|
||||||
* coverage=+1 to the Shader. (2) Draw an additional conservative raster around the curve's shared
|
* curve. The Shader takes care of everything else for now.
|
||||||
* edge, using coverage=-1 at bloat vertices that fall outside the input points. This erases what
|
*
|
||||||
* the hull just wrote and ramps coverage to zero.
|
* The final corners get touched up in a later step by VSCornerImpl.
|
||||||
*/
|
*/
|
||||||
class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
|
class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
|
||||||
public:
|
public:
|
||||||
@ -294,9 +284,10 @@ public:
|
|||||||
// Here we generate conservative raster geometry for the input polygon. It is the convex
|
// 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
|
// 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
|
// vertices, where one or two may cause degenerate triangles. The vertex data tells us how
|
||||||
// to offset each vertex. Edges are also handled here using the same concept. For more
|
// to offset each vertex. For more details on conservative raster, see:
|
||||||
// details on conservative raster, see:
|
|
||||||
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||||
|
//
|
||||||
|
// Triangle edges are also handled here using the same concept (see kHull3AndEdgeVertices).
|
||||||
v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
|
v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
|
||||||
v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
|
v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
|
||||||
hullPts, kVertexData_LeftNeighborIdShift);
|
hullPts, kVertexData_LeftNeighborIdShift);
|
||||||
@ -333,32 +324,29 @@ public:
|
|||||||
// fallthru.
|
// fallthru.
|
||||||
v->codeAppend ("}");
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
// For triangles, we also emit coverage in order to handle edges and corners.
|
||||||
|
const char* coverage = nullptr;
|
||||||
|
if (3 == fNumSides) {
|
||||||
|
v->codeAppend ("half coverage;");
|
||||||
|
Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage");
|
||||||
|
v->codeAppendf("if (0 != (%s & %i)) {", // Are we the opposite endpoint of an edge?
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName,
|
||||||
|
kVertexData_InvertCoverageBit);
|
||||||
|
v->codeAppend ( "coverage = -1 - coverage;");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a hull vertex?
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsHullBit);
|
||||||
|
v->codeAppend ( "coverage = +1;"); // Hull coverage is +1 all around.
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
coverage = "coverage";
|
||||||
|
}
|
||||||
|
|
||||||
v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
|
v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
|
||||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
||||||
|
|
||||||
// The hull has a coverage of +1 all around.
|
return coverage;
|
||||||
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);
|
|
||||||
Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
} else {
|
|
||||||
SkASSERT(4 == fNumSides);
|
|
||||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
|
|
||||||
v->codeAppend ( "coverage = -1;");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName,
|
|
||||||
kVertexData_InvertNegativeCoverageBit);
|
|
||||||
v->codeAppend ( "coverage = -1 - coverage;");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
return "coverage";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -437,7 +425,31 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RenderPass::kTriangleCorners: {
|
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);
|
||||||
|
if (caps.usePrimitiveRestart()) {
|
||||||
|
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
|
sizeof(kHull4IndicesAsStrips),
|
||||||
|
kHull4IndicesAsStrips,
|
||||||
|
gHull4IndexBufferKey);
|
||||||
|
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips);
|
||||||
|
} else {
|
||||||
|
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
|
sizeof(kHull4IndicesAsTris),
|
||||||
|
kHull4IndicesAsTris,
|
||||||
|
gHull4IndexBufferKey);
|
||||||
|
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RenderPass::kTriangleCorners:
|
||||||
|
case RenderPass::kQuadraticCorners:
|
||||||
|
case RenderPass::kCubicCorners: {
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey);
|
GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey);
|
||||||
if (caps.usePrimitiveRestart()) {
|
if (caps.usePrimitiveRestart()) {
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
@ -452,35 +464,14 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
|||||||
gCornerIndexBufferKey);
|
gCornerIndexBufferKey);
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris);
|
fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris);
|
||||||
}
|
}
|
||||||
break;
|
if (RenderPass::kTriangleCorners != fRenderPass) {
|
||||||
}
|
fNumIndicesPerInstance = fNumIndicesPerInstance * 2/3;
|
||||||
|
|
||||||
case RenderPass::kQuadratics:
|
|
||||||
case RenderPass::kCubics: {
|
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4AndEdgeVertexBufferKey);
|
|
||||||
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
|
||||||
sizeof(kHull4AndEdgeVertices),
|
|
||||||
kHull4AndEdgeVertices,
|
|
||||||
gHull4AndEdgeVertexBufferKey);
|
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey);
|
|
||||||
if (caps.usePrimitiveRestart()) {
|
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
|
||||||
sizeof(kHull4AndEdgeIndicesAsStrips),
|
|
||||||
kHull4AndEdgeIndicesAsStrips,
|
|
||||||
gHull4AndEdgeIndexBufferKey);
|
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4AndEdgeIndicesAsStrips);
|
|
||||||
} else {
|
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
|
||||||
sizeof(kHull4AndEdgeIndicesAsTris),
|
|
||||||
kHull4AndEdgeIndicesAsTris,
|
|
||||||
gHull4AndEdgeIndexBufferKey);
|
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4AndEdgeIndicesAsTris);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) {
|
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
|
||||||
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
||||||
|
|
||||||
SkASSERT(kAttribIdx_X == this->numAttribs());
|
SkASSERT(kAttribIdx_X == this->numAttribs());
|
||||||
@ -534,11 +525,13 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Sh
|
|||||||
switch (fRenderPass) {
|
switch (fRenderPass) {
|
||||||
case RenderPass::kTriangles:
|
case RenderPass::kTriangles:
|
||||||
return new VSHullAndEdgeImpl(std::move(shadr), 3);
|
return new VSHullAndEdgeImpl(std::move(shadr), 3);
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
return new VSCornerImpl(std::move(shadr));
|
|
||||||
case RenderPass::kQuadratics:
|
case RenderPass::kQuadratics:
|
||||||
case RenderPass::kCubics:
|
case RenderPass::kCubics:
|
||||||
return new VSHullAndEdgeImpl(std::move(shadr), 4);
|
return new VSHullAndEdgeImpl(std::move(shadr), 4);
|
||||||
|
case RenderPass::kTriangleCorners:
|
||||||
|
case RenderPass::kQuadraticCorners:
|
||||||
|
case RenderPass::kCubicCorners:
|
||||||
|
return new VSCornerImpl(std::move(shadr));
|
||||||
}
|
}
|
||||||
SK_ABORT("Invalid RenderPass");
|
SK_ABORT("Invalid RenderPass");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
using Shader = GrCCCoverageProcessor::Shader;
|
using Shader = GrCCCoverageProcessor::Shader;
|
||||||
|
|
||||||
void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||||
const char* /*repetitionID*/, const char* /*wind*/,
|
const char* repetitionID, const char* wind,
|
||||||
GeometryVars*) const {
|
GeometryVars* vars) const {
|
||||||
// Find the cubic's power basis coefficients.
|
// Find the cubic's power basis coefficients.
|
||||||
s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
|
s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
|
||||||
" 3, -6, 3, 0, "
|
" 3, -6, 3, 0, "
|
||||||
@ -58,44 +58,118 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
|||||||
// Evaluate the cubic at T=.5 for a mid-ish point.
|
// Evaluate the cubic at T=.5 for a mid-ish point.
|
||||||
s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
|
s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
|
||||||
|
|
||||||
// Orient the KLM matrix so L & M are both positive on the side of the curve we wish to fill.
|
// Orient the KLM matrix so L & M have matching signs on the side of the curve we wish to fill.
|
||||||
|
// We give L & M both the same sign as wind, in order to pass this value to the fragment shader.
|
||||||
|
// (Cubics are pre-chopped such that L & M do not change sign within any individual segment).
|
||||||
s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
|
s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
|
||||||
fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
||||||
s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
|
s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
|
||||||
"0, orientation[0], 0, "
|
"0, orientation[0] * %s, 0, "
|
||||||
"0, 0, orientation[1]);", fKLMMatrix.c_str());
|
"0, 0, orientation[1] * %s);", fKLMMatrix.c_str(), wind, wind);
|
||||||
|
|
||||||
|
// Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
|
||||||
|
s->declareGlobal(fEdgeDistanceEquation);
|
||||||
|
s->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind);
|
||||||
|
s->codeAppendf("float2 edgept0 = %s[edgeidx0];", pts);
|
||||||
|
s->codeAppendf("float2 edgept1 = %s[3 - edgeidx0];", pts);
|
||||||
|
Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
|
||||||
|
|
||||||
|
this->onEmitSetupCode(s, pts, repetitionID, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||||
GrGLSLVarying::Scope scope, SkString* code,
|
GrGLSLVarying::Scope scope, SkString* code,
|
||||||
const char* position, const char* inputCoverage,
|
const char* position, const char* inputCoverage,
|
||||||
const char* wind) {
|
const char* /*wind*/) {
|
||||||
|
SkASSERT(!inputCoverage);
|
||||||
|
|
||||||
|
fKLMD.reset(kFloat4_GrSLType, scope);
|
||||||
|
varyingHandler->addVarying("klmd", &fKLMD);
|
||||||
code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
|
code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
|
||||||
|
code->appendf("float d = dot(float3(%s, 1), %s);", position, fEdgeDistanceEquation.c_str());
|
||||||
|
code->appendf("%s = float4(klm, d);", OutName(fKLMD));
|
||||||
|
|
||||||
fKLMW.reset(kFloat4_GrSLType, scope);
|
this->onEmitVaryings(varyingHandler, scope, code);
|
||||||
varyingHandler->addVarying("klmw", &fKLMW);
|
}
|
||||||
code->appendf("%s.xyz = klm;", OutName(fKLMW));
|
|
||||||
code->appendf("%s.w = %s * %s;", OutName(fKLMW), inputCoverage, wind);
|
|
||||||
|
|
||||||
|
void GrCCCubicShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
|
||||||
|
const char* outputCoverage) const {
|
||||||
|
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
|
||||||
|
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
|
||||||
|
|
||||||
|
this->emitCoverage(f, outputCoverage);
|
||||||
|
|
||||||
|
// Wind is the sign of both L and/or M. Take the sign of whichever has the larger magnitude.
|
||||||
|
// (In reality, either would be fine because we chop cubics with more than a half pixel of
|
||||||
|
// padding around the L & M lines, so neither should approach zero.)
|
||||||
|
f->codeAppend ("half wind = sign(l + m);");
|
||||||
|
f->codeAppendf("%s *= wind;", outputCoverage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||||
|
GrGLSLVarying::Scope scope, SkString* code) {
|
||||||
fGradMatrix.reset(kFloat2x2_GrSLType, scope);
|
fGradMatrix.reset(kFloat2x2_GrSLType, scope);
|
||||||
varyingHandler->addVarying("grad_matrix", &fGradMatrix);
|
varyingHandler->addVarying("grad_matrix", &fGradMatrix);
|
||||||
|
// "klm" was just defined by the base class.
|
||||||
code->appendf("%s[0] = 3 * klm[0] * %s[0].xy;", OutName(fGradMatrix), fKLMMatrix.c_str());
|
code->appendf("%s[0] = 3 * klm[0] * %s[0].xy;", OutName(fGradMatrix), fKLMMatrix.c_str());
|
||||||
code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
|
code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
|
||||||
OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCCubicShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc,
|
void GrCCCubicHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
|
||||||
GrGLSLFPFragmentBuilder* f,
|
const char* outputCoverage) const {
|
||||||
const char* outputCoverage) const {
|
// k,l,m,d are defined by the base class.
|
||||||
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z;",
|
|
||||||
fKLMW.fsIn(), fKLMW.fsIn(), fKLMW.fsIn());
|
|
||||||
f->codeAppend ("float f = k*k*k - l*m;");
|
f->codeAppend ("float f = k*k*k - l*m;");
|
||||||
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
|
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
|
||||||
f->codeAppend ("float d = f * inversesqrt(dot(grad_f, grad_f));");
|
f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage);
|
||||||
#ifdef SK_DEBUG
|
f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat edge opposite the curve.
|
||||||
if (proc.debugVisualizationsEnabled()) {
|
}
|
||||||
f->codeAppendf("d /= %f;", proc.debugBloat());
|
|
||||||
}
|
void GrCCCubicCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||||
#endif
|
const char* repetitionID, GeometryVars* vars) const {
|
||||||
f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s.w;", outputCoverage, fKLMW.fsIn());
|
s->codeAppendf("float2 corner = %s[%s * 3];", pts, repetitionID);
|
||||||
|
vars->fCornerVars.fPoint = "corner";
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||||
|
GrGLSLVarying::Scope scope, SkString* code) {
|
||||||
|
using Interpolation = GrGLSLVaryingHandler::Interpolation;
|
||||||
|
|
||||||
|
fdKLMDdx.reset(kFloat4_GrSLType, scope);
|
||||||
|
varyingHandler->addVarying("dklmddx", &fdKLMDdx, Interpolation::kCanBeFlat);
|
||||||
|
code->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
|
||||||
|
OutName(fdKLMDdx), fKLMMatrix.c_str(), fKLMMatrix.c_str(),
|
||||||
|
fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str());
|
||||||
|
|
||||||
|
fdKLMDdy.reset(kFloat4_GrSLType, scope);
|
||||||
|
varyingHandler->addVarying("dklmddy", &fdKLMDdy, Interpolation::kCanBeFlat);
|
||||||
|
code->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
|
||||||
|
OutName(fdKLMDdy), fKLMMatrix.c_str(), fKLMMatrix.c_str(),
|
||||||
|
fKLMMatrix.c_str(), fEdgeDistanceEquation.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCCubicCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
|
||||||
|
const char* outputCoverage) const {
|
||||||
|
f->codeAppendf("float2x4 grad_klmd = float2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn());
|
||||||
|
|
||||||
|
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
|
||||||
|
// the same pixel because those cases should have been weeded out by this point.
|
||||||
|
// k,l,m,d are defined by the base class.
|
||||||
|
f->codeAppend ("float f = k*k*k - l*m;");
|
||||||
|
f->codeAppend ("float2 grad_f = float3(3*k*k, -m, -l) * float2x3(grad_klmd);");
|
||||||
|
f->codeAppendf("%s = -clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);",
|
||||||
|
outputCoverage);
|
||||||
|
f->codeAppendf("%s -= d;", outputCoverage);
|
||||||
|
|
||||||
|
// Use software msaa to estimate actual coverage at the corner pixels.
|
||||||
|
const int sampleCount = Shader::DefineSoftSampleLocations(f, "samples");
|
||||||
|
f->codeAppendf("float4 klmd_center = float4(%s.xyz, %s.w + 0.5);",
|
||||||
|
fKLMD.fsIn(), fKLMD.fsIn());
|
||||||
|
f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
|
||||||
|
f->codeAppend ( "float4 klmd = grad_klmd * samples[i] + klmd_center;");
|
||||||
|
f->codeAppend ( "half f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;");
|
||||||
|
f->codeAppendf( "%s += all(greaterThan(half4(f, klmd.y, klmd.z, klmd.w), "
|
||||||
|
"half4(0))) ? %f : 0;",
|
||||||
|
outputCoverage, 1.0 / sampleCount);
|
||||||
|
f->codeAppend ("}");
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,37 @@
|
|||||||
class GrCCCubicShader : public GrCCCoverageProcessor::Shader {
|
class GrCCCubicShader : public GrCCCoverageProcessor::Shader {
|
||||||
protected:
|
protected:
|
||||||
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
const char* wind, GeometryVars*) const override;
|
const char* wind, GeometryVars*) const final;
|
||||||
|
virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
|
GeometryVars*) const {}
|
||||||
|
|
||||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||||
const char* position, const char* inputCoverage, const char* wind) override;
|
const char* position, const char* inputCoverage, const char* wind) final;
|
||||||
|
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0;
|
||||||
|
|
||||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final;
|
||||||
const char* outputCoverage) const override;
|
virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0;
|
||||||
|
|
||||||
GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
|
GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
|
||||||
GrGLSLVarying fKLMW;
|
GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
|
||||||
|
GrGLSLVarying fKLMD;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GrCCCubicHullShader : public GrCCCubicShader {
|
||||||
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
|
||||||
|
void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
|
||||||
|
|
||||||
GrGLSLVarying fGradMatrix;
|
GrGLSLVarying fGradMatrix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GrCCCubicCornerShader : public GrCCCubicShader {
|
||||||
|
void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
|
GeometryVars*) const override;
|
||||||
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
|
||||||
|
void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
|
||||||
|
|
||||||
|
GrGLSLVarying fdKLMDdx;
|
||||||
|
GrGLSLVarying fdKLMDdy;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -530,11 +530,15 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
|
|||||||
if (batchTotalCounts.fQuadratics) {
|
if (batchTotalCounts.fQuadratics) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
||||||
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
|
||||||
|
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchTotalCounts.fCubics) {
|
if (batchTotalCounts.fCubics) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||||
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
|
||||||
|
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
using Shader = GrCCCoverageProcessor::Shader;
|
using Shader = GrCCCoverageProcessor::Shader;
|
||||||
|
|
||||||
void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||||
const char* /*repetitionID*/, const char* /*wind*/,
|
const char* repetitionID, const char* wind,
|
||||||
GeometryVars* vars) const {
|
GeometryVars* vars) const {
|
||||||
s->declareGlobal(fCanonicalMatrix);
|
s->declareGlobal(fCanonicalMatrix);
|
||||||
s->codeAppendf("%s = float3x3(0.0, 0, 1, "
|
s->codeAppendf("%s = float3x3(0.0, 0, 1, "
|
||||||
@ -25,6 +25,41 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p
|
|||||||
"%s[2], 1));",
|
"%s[2], 1));",
|
||||||
fCanonicalMatrix.c_str(), pts, pts, pts);
|
fCanonicalMatrix.c_str(), pts, pts, pts);
|
||||||
|
|
||||||
|
s->declareGlobal(fEdgeDistanceEquation);
|
||||||
|
s->codeAppendf("float2 edgept0 = %s[%s > 0 ? 2 : 0];", pts, wind);
|
||||||
|
s->codeAppendf("float2 edgept1 = %s[%s > 0 ? 0 : 2];", pts, wind);
|
||||||
|
Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
|
||||||
|
|
||||||
|
this->onEmitSetupCode(s, pts, repetitionID, vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||||
|
GrGLSLVarying::Scope scope, SkString* code,
|
||||||
|
const char* position, const char* inputCoverage,
|
||||||
|
const char* wind) {
|
||||||
|
SkASSERT(!inputCoverage);
|
||||||
|
|
||||||
|
fXYDW.reset(kFloat4_GrSLType, scope);
|
||||||
|
varyingHandler->addVarying("xydw", &fXYDW);
|
||||||
|
code->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
|
||||||
|
OutName(fXYDW), fCanonicalMatrix.c_str(), position);
|
||||||
|
code->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
|
||||||
|
OutName(fXYDW), fEdgeDistanceEquation.c_str(), position,
|
||||||
|
fEdgeDistanceEquation.c_str());
|
||||||
|
code->appendf("%s.w = %s;", OutName(fXYDW), wind);
|
||||||
|
|
||||||
|
this->onEmitVaryings(varyingHandler, scope, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCQuadraticShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
|
||||||
|
const char* outputCoverage) const {
|
||||||
|
this->emitCoverage(f, outputCoverage);
|
||||||
|
f->codeAppendf("%s *= %s.w;", outputCoverage, fXYDW.fsIn()); // Sign by wind.
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCQuadraticHullShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||||
|
const char* /*repetitionID*/,
|
||||||
|
GeometryVars* vars) const {
|
||||||
// Find the T value whose tangent is halfway between the tangents at the endpionts.
|
// Find the T value whose tangent is halfway between the tangents at the endpionts.
|
||||||
s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts);
|
s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts);
|
||||||
s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts);
|
s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts);
|
||||||
@ -41,31 +76,66 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p
|
|||||||
vars->fHullVars.fAlternatePoints = "quadratic_hull";
|
vars->fHullVars.fAlternatePoints = "quadratic_hull";
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
void GrCCQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||||
GrGLSLVarying::Scope scope, SkString* code,
|
GrGLSLVarying::Scope scope, SkString* code) {
|
||||||
const char* position, const char* inputCoverage,
|
fGrad.reset(kFloat2_GrSLType, scope);
|
||||||
const char* wind) {
|
varyingHandler->addVarying("grad", &fGrad);
|
||||||
fCoords.reset(kFloat4_GrSLType, scope);
|
code->appendf("%s = float2(2 * %s.x, -1) * float2x2(%s);",
|
||||||
varyingHandler->addVarying("coords", &fCoords);
|
OutName(fGrad), OutName(fXYDW), fCanonicalMatrix.c_str());
|
||||||
code->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
|
|
||||||
OutName(fCoords), fCanonicalMatrix.c_str(), position);
|
|
||||||
code->appendf("%s.zw = float2(2 * %s.x, -1) * float2x2(%s);",
|
|
||||||
OutName(fCoords), OutName(fCoords), fCanonicalMatrix.c_str());
|
|
||||||
|
|
||||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
|
||||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
|
||||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc,
|
void GrCCQuadraticHullShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
|
||||||
GrGLSLFPFragmentBuilder* f,
|
const char* outputCoverage) const {
|
||||||
|
f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
|
||||||
|
fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn(), fGrad.fsIn(), fGrad.fsIn());
|
||||||
|
f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
|
||||||
|
f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYDW.fsIn()); // Flat closing edge.
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCQuadraticCornerShader::onEmitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||||
|
const char* repetitionID,
|
||||||
|
GeometryVars* vars) const {
|
||||||
|
s->codeAppendf("float2 corner = %s[%s * 2];", pts, repetitionID);
|
||||||
|
vars->fCornerVars.fPoint = "corner";
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||||
|
GrGLSLVarying::Scope scope, SkString* code) {
|
||||||
|
using Interpolation = GrGLSLVaryingHandler::Interpolation;
|
||||||
|
|
||||||
|
fdXYDdx.reset(kFloat3_GrSLType, scope);
|
||||||
|
varyingHandler->addVarying("dXYDdx", &fdXYDdx, Interpolation::kCanBeFlat);
|
||||||
|
code->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
|
||||||
|
OutName(fdXYDdx), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(),
|
||||||
|
fEdgeDistanceEquation.c_str());
|
||||||
|
|
||||||
|
fdXYDdy.reset(kFloat3_GrSLType, scope);
|
||||||
|
varyingHandler->addVarying("dXYDdy", &fdXYDdy, Interpolation::kCanBeFlat);
|
||||||
|
code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
|
||||||
|
OutName(fdXYDdy), fCanonicalMatrix.c_str(), fCanonicalMatrix.c_str(),
|
||||||
|
fEdgeDistanceEquation.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrCCQuadraticCornerShader::emitCoverage(GrGLSLFPFragmentBuilder* f,
|
||||||
const char* outputCoverage) const {
|
const char* outputCoverage) const {
|
||||||
f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s.zw, %s.zw));",
|
f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
|
||||||
fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn(), fCoords.fsIn());
|
fXYDW.fsIn(), fXYDW.fsIn(), fXYDW.fsIn());
|
||||||
#ifdef SK_DEBUG
|
f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn());
|
||||||
if (proc.debugVisualizationsEnabled()) {
|
|
||||||
f->codeAppendf("d /= %f;", proc.debugBloat());
|
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
|
||||||
}
|
// the same pixel because those cases should have been weeded out by this point.
|
||||||
#endif
|
f->codeAppend ("float f = x*x - y;");
|
||||||
f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);");
|
||||||
|
f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage);
|
||||||
|
f->codeAppendf("%s -= d;", outputCoverage);
|
||||||
|
|
||||||
|
// Use software msaa to approximate coverage at the corner pixels.
|
||||||
|
int sampleCount = Shader::DefineSoftSampleLocations(f, "samples");
|
||||||
|
f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);", fXYDW.fsIn(), fXYDW.fsIn());
|
||||||
|
f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount);
|
||||||
|
f->codeAppend ( "float3 xyd = grad_xyd * samples[i] + xyd_center;");
|
||||||
|
f->codeAppend ( "half f = xyd.y - xyd.x * xyd.x;"); // f > 0 -> inside curve.
|
||||||
|
f->codeAppendf( "%s += all(greaterThan(float2(f,xyd.z), float2(0))) ? %f : 0;",
|
||||||
|
outputCoverage, 1.0 / sampleCount);
|
||||||
|
f->codeAppendf("}");
|
||||||
}
|
}
|
||||||
|
@ -23,17 +23,48 @@
|
|||||||
class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader {
|
class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader {
|
||||||
protected:
|
protected:
|
||||||
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
const char* wind, GeometryVars*) const override;
|
const char* wind, GeometryVars*) const final;
|
||||||
|
virtual void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
|
GeometryVars*) const = 0;
|
||||||
|
|
||||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||||
const char* position, const char* inputCoverage, const char* wind) override;
|
const char* position, const char* inputCoverage, const char* wind) final;
|
||||||
|
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {}
|
||||||
|
|
||||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final;
|
||||||
const char* outputCoverage) const override;
|
virtual void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const = 0;
|
||||||
|
|
||||||
const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType};
|
const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType};
|
||||||
GrGLSLVarying fCoords;
|
const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
|
||||||
GrGLSLVarying fCoverageTimesWind;
|
GrGLSLVarying fXYDW;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This pass draws a conservative raster hull around the quadratic bezier curve, computes the
|
||||||
|
* curve's coverage using the gradient-based AA technique outlined in the Loop/Blinn paper, and
|
||||||
|
* uses simple distance-to-edge to subtract out coverage for the flat closing edge [P2 -> P0]. Since
|
||||||
|
* the provided curves are monotonic, this will get every pixel right except the two corners.
|
||||||
|
*/
|
||||||
|
class GrCCQuadraticHullShader : public GrCCQuadraticShader {
|
||||||
|
void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
|
GeometryVars*) const override;
|
||||||
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
|
||||||
|
void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
|
||||||
|
|
||||||
|
GrGLSLVarying fGrad;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This pass fixes the corners of a closed quadratic segment with soft MSAA.
|
||||||
|
*/
|
||||||
|
class GrCCQuadraticCornerShader : public GrCCQuadraticShader {
|
||||||
|
void onEmitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||||
|
GeometryVars*) const override;
|
||||||
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) override;
|
||||||
|
void emitCoverage(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
|
||||||
|
|
||||||
|
GrGLSLVarying fdXYDdx;
|
||||||
|
GrGLSLVarying fdXYDdy;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,8 +22,7 @@ void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
|||||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCTriangleShader::onEmitFragmentCode(const GrCCCoverageProcessor&,
|
void GrCCTriangleShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
|
||||||
GrGLSLFPFragmentBuilder* f,
|
|
||||||
const char* outputCoverage) const {
|
const char* outputCoverage) const {
|
||||||
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
||||||
}
|
}
|
||||||
@ -107,8 +106,7 @@ void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandl
|
|||||||
code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind);
|
code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCTriangleCornerShader::onEmitFragmentCode(const GrCCCoverageProcessor&,
|
void GrCCTriangleCornerShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
|
||||||
GrGLSLFPFragmentBuilder* f,
|
|
||||||
const char* outputCoverage) const {
|
const char* outputCoverage) const {
|
||||||
// By the time we reach this shader, the pixel is in the following state:
|
// By the time we reach this shader, the pixel is in the following state:
|
||||||
//
|
//
|
||||||
|
@ -19,8 +19,7 @@
|
|||||||
class GrCCTriangleShader : public GrCCCoverageProcessor::Shader {
|
class GrCCTriangleShader : public GrCCCoverageProcessor::Shader {
|
||||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||||
const char* position, const char* inputCoverage, const char* wind) override;
|
const char* position, const char* inputCoverage, const char* wind) override;
|
||||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override;
|
||||||
const char* outputCoverage) const override;
|
|
||||||
|
|
||||||
GrGLSLVarying fCoverageTimesWind;
|
GrGLSLVarying fCoverageTimesWind;
|
||||||
};
|
};
|
||||||
@ -35,8 +34,7 @@ class GrCCTriangleCornerShader : public GrCCCoverageProcessor::Shader {
|
|||||||
const char* wind, GeometryVars*) const override;
|
const char* wind, GeometryVars*) const override;
|
||||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||||
const char* position, const char* inputCoverage, const char* wind) override;
|
const char* position, const char* inputCoverage, const char* wind) override;
|
||||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override;
|
||||||
const char* outputCoverage) const override;
|
|
||||||
|
|
||||||
GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2};
|
GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2};
|
||||||
GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};
|
GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};
|
||||||
|
Loading…
Reference in New Issue
Block a user