Refactor CCPR coverage shaders for a vertex impl
Decouples geometry generation and analytic coverage. This paves the way for a vertex shader implementation. TBR=egdaniel@google.com Bug: skia: Change-Id: I2a183401bfe70b9f14b9b1cf035de6020a2135fa Reviewed-on: https://skia-review.googlesource.com/60103 Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
6376517472
commit
6a3dbeed00
13
gn/gpu.gni
13
gn/gpu.gni
@ -296,17 +296,18 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/ccpr/GrCCPRCoverageOp.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRCoverageOp.h",
|
||||
"$_src/gpu/ccpr/GrCCPRCoverageProcessor.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRCoverageProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCPRCubicProcessor.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRCubicProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCPRCubicShader.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRCubicShader.h",
|
||||
"$_src/gpu/ccpr/GrCCPRGeometry.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRGeometry.h",
|
||||
"$_src/gpu/ccpr/GrCCPRPathProcessor.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRPathProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRQuadraticProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCPRTriangleProcessor.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRTriangleProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCPRQuadraticShader.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRQuadraticShader.h",
|
||||
"$_src/gpu/ccpr/GrCCPRTriangleShader.cpp",
|
||||
"$_src/gpu/ccpr/GrCCPRTriangleShader.h",
|
||||
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp",
|
||||
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.h",
|
||||
|
||||
|
@ -27,16 +27,16 @@
|
||||
|
||||
using TriangleInstance = GrCCPRCoverageProcessor::TriangleInstance;
|
||||
using CurveInstance = GrCCPRCoverageProcessor::CurveInstance;
|
||||
using Mode = GrCCPRCoverageProcessor::Mode;
|
||||
using RenderPass = GrCCPRCoverageProcessor::RenderPass;
|
||||
|
||||
static constexpr float kDebugBloat = 40;
|
||||
|
||||
static int num_points(Mode mode) {
|
||||
return mode >= Mode::kSerpentineHulls ? 4 : 3;
|
||||
static int num_points(RenderPass renderPass) {
|
||||
return renderPass >= RenderPass::kSerpentineHulls ? 4 : 3;
|
||||
}
|
||||
|
||||
static int is_quadratic(Mode mode) {
|
||||
return mode >= Mode::kQuadraticHulls && mode < Mode::kSerpentineHulls;
|
||||
static int is_quadratic(RenderPass renderPass) {
|
||||
return renderPass >= RenderPass::kQuadraticHulls && renderPass < RenderPass::kSerpentineHulls;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,7 +65,7 @@ private:
|
||||
|
||||
void updateGpuData();
|
||||
|
||||
Mode fMode = Mode::kTriangleHulls;
|
||||
RenderPass fRenderPass = RenderPass::kTriangleHulls;
|
||||
SkMatrix fCubicKLM;
|
||||
|
||||
SkPoint fPoints[4] = {
|
||||
@ -136,9 +136,9 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
|
||||
SkPath outline;
|
||||
outline.moveTo(fPoints[0]);
|
||||
if (4 == num_points(fMode)) {
|
||||
if (4 == num_points(fRenderPass)) {
|
||||
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
|
||||
} else if (is_quadratic(fMode)) {
|
||||
} else if (is_quadratic(fRenderPass)) {
|
||||
outline.quadTo(fPoints[1], fPoints[3]);
|
||||
} else {
|
||||
outline.lineTo(fPoints[1]);
|
||||
@ -172,7 +172,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
if (GrRenderTargetContext* rtc =
|
||||
canvas->internal_private_accessTopLayerRenderTargetContext()) {
|
||||
rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
|
||||
caption = GrCCPRCoverageProcessor::GetProcessorName(fMode);
|
||||
caption = GrCCPRCoverageProcessor::GetRenderPassName(fRenderPass);
|
||||
}
|
||||
|
||||
SkPaint pointsPaint;
|
||||
@ -180,7 +180,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
pointsPaint.setStrokeWidth(8);
|
||||
pointsPaint.setAntiAlias(true);
|
||||
|
||||
if (4 == num_points(fMode)) {
|
||||
if (4 == num_points(fRenderPass)) {
|
||||
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);
|
||||
@ -199,7 +199,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
}
|
||||
|
||||
void CCPRGeometryView::updateGpuData() {
|
||||
int vertexCount = num_points(fMode);
|
||||
int vertexCount = num_points(fRenderPass);
|
||||
|
||||
fGpuPoints.reset();
|
||||
fInstanceData.reset();
|
||||
@ -208,17 +208,17 @@ void CCPRGeometryView::updateGpuData() {
|
||||
if (4 == vertexCount) {
|
||||
double t[2], s[2];
|
||||
SkCubicType type = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
||||
if (Mode::kSerpentineHulls == fMode && SkCubicType::kLoop == type) {
|
||||
fMode = Mode::kLoopHulls;
|
||||
if (RenderPass::kSerpentineHulls == fRenderPass && SkCubicType::kLoop == type) {
|
||||
fRenderPass = RenderPass::kLoopHulls;
|
||||
}
|
||||
if (Mode::kSerpentineCorners == fMode && SkCubicType::kLoop == type) {
|
||||
fMode = Mode::kLoopCorners;
|
||||
if (RenderPass::kSerpentineCorners == fRenderPass && SkCubicType::kLoop == type) {
|
||||
fRenderPass = RenderPass::kLoopCorners;
|
||||
}
|
||||
if (Mode::kLoopHulls == fMode && SkCubicType::kLoop != type) {
|
||||
fMode = Mode::kSerpentineHulls;
|
||||
if (RenderPass::kLoopHulls == fRenderPass && SkCubicType::kLoop != type) {
|
||||
fRenderPass = RenderPass::kSerpentineHulls;
|
||||
}
|
||||
if (Mode::kLoopCorners == fMode && SkCubicType::kLoop != type) {
|
||||
fMode = Mode::kSerpentineCorners;
|
||||
if (RenderPass::kLoopCorners == fRenderPass && SkCubicType::kLoop != type) {
|
||||
fRenderPass = RenderPass::kSerpentineCorners;
|
||||
}
|
||||
|
||||
GrCCPRGeometry geometry;
|
||||
@ -245,7 +245,7 @@ void CCPRGeometryView::updateGpuData() {
|
||||
default: continue;
|
||||
}
|
||||
}
|
||||
} else if (is_quadratic(fMode)) {
|
||||
} else if (is_quadratic(fRenderPass)) {
|
||||
GrCCPRGeometry geometry;
|
||||
geometry.beginContour(fPoints[0]);
|
||||
geometry.quadraticTo(fPoints[1], fPoints[3]);
|
||||
@ -282,7 +282,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
||||
GrContext* context = state->gpu()->getContext();
|
||||
GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend() ?
|
||||
static_cast<GrGLGpu*>(state->gpu()) : nullptr;
|
||||
int vertexCount = num_points(fView->fMode);
|
||||
int vertexCount = num_points(fView->fRenderPass);
|
||||
|
||||
sk_sp<GrBuffer> pointsBuffer(rp->createBuffer(fView->fGpuPoints.count() * sizeof(SkPoint),
|
||||
kTexel_GrBufferType, kDynamic_GrAccessPattern,
|
||||
@ -305,7 +305,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
||||
GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
|
||||
SkBlendMode::kSrcOver);
|
||||
|
||||
GrCCPRCoverageProcessor ccprProc(fView->fMode, pointsBuffer.get());
|
||||
GrCCPRCoverageProcessor ccprProc(fView->fRenderPass, pointsBuffer.get());
|
||||
SkDEBUGCODE(ccprProc.enableDebugVisualizations(kDebugBloat);)
|
||||
|
||||
GrMesh mesh(4 == vertexCount ? GrPrimitiveType::kLinesAdjacency : GrPrimitiveType::kTriangles);
|
||||
@ -349,7 +349,7 @@ private:
|
||||
|
||||
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (4 != num_points(fMode) && 2 == i) {
|
||||
if (4 != num_points(fRenderPass) && 2 == i) {
|
||||
continue;
|
||||
}
|
||||
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
|
||||
@ -374,11 +374,11 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
|
||||
SkUnichar unichar;
|
||||
if (SampleCode::CharQ(*evt, &unichar)) {
|
||||
if (unichar >= '1' && unichar <= '7') {
|
||||
fMode = Mode(unichar - '1');
|
||||
if (fMode >= Mode::kLoopHulls) {
|
||||
fRenderPass = RenderPass(unichar - '1');
|
||||
if (fRenderPass >= RenderPass::kLoopHulls) {
|
||||
// '6' -> kSerpentineHulls, '7' -> kSerpentineCorners. updateGpuData converts to
|
||||
// kLoop* if needed.
|
||||
fMode = Mode(int(fMode) + 1);
|
||||
fRenderPass = RenderPass(int(fRenderPass) + 1);
|
||||
}
|
||||
this->updateAndInval();
|
||||
return true;
|
||||
|
@ -385,7 +385,7 @@ void GrCCPRCoverageOp::setBuffers(sk_sp<GrBuffer> pointsBuffer, sk_sp<GrBuffer>
|
||||
}
|
||||
|
||||
void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
|
||||
using Mode = GrCCPRCoverageProcessor::Mode;
|
||||
using RenderPass = GrCCPRCoverageProcessor::RenderPass;
|
||||
|
||||
SkDEBUGCODE(GrCCPRCoverageProcessor::Validate(flushState->drawOpArgs().fProxy));
|
||||
SkASSERT(fPointsBuffer);
|
||||
@ -399,34 +399,34 @@ void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
|
||||
|
||||
// Triangles.
|
||||
auto constexpr kTrianglesGrPrimitiveType = GrCCPRCoverageProcessor::kTrianglesGrPrimitiveType;
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleHulls,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleHulls,
|
||||
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleEdges,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleEdges,
|
||||
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kTriangleCorners,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleCorners,
|
||||
kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
|
||||
|
||||
// Quadratics.
|
||||
auto constexpr kQuadraticsGrPrimitiveType = GrCCPRCoverageProcessor::kQuadraticsGrPrimitiveType;
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticHulls,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticHulls,
|
||||
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kQuadraticCorners,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticCorners,
|
||||
kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
|
||||
|
||||
// Cubics.
|
||||
auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineHulls,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineHulls,
|
||||
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopHulls,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopHulls,
|
||||
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kSerpentineCorners,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kSerpentineCorners,
|
||||
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fSerpentines);
|
||||
this->drawMaskPrimitives(flushState, pipeline, Mode::kLoopCorners,
|
||||
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kLoopCorners,
|
||||
kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fLoops);
|
||||
}
|
||||
|
||||
void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||
GrCCPRCoverageProcessor::Mode mode,
|
||||
GrCCPRCoverageProcessor::RenderPass renderPass,
|
||||
GrPrimitiveType primType, int vertexCount,
|
||||
int PrimitiveTallies::* instanceType) const {
|
||||
using ScissorMode = GrCCPRCoverageOpsBuilder::ScissorMode;
|
||||
@ -463,7 +463,7 @@ void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPi
|
||||
SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
|
||||
|
||||
if (!fMeshesScratchBuffer.empty()) {
|
||||
GrCCPRCoverageProcessor proc(mode, fPointsBuffer.get());
|
||||
GrCCPRCoverageProcessor proc(renderPass, fPointsBuffer.get());
|
||||
SkASSERT(flushState->rtCommandBuffer());
|
||||
flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
|
||||
fDynamicStatesScratchBuffer.begin(),
|
||||
|
@ -149,9 +149,9 @@ private:
|
||||
const PrimitiveTallies baseInstances[kNumScissorModes],
|
||||
const PrimitiveTallies endInstances[kNumScissorModes]);
|
||||
|
||||
void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, const GrCCPRCoverageProcessor::Mode,
|
||||
GrPrimitiveType, int vertexCount,
|
||||
int PrimitiveTallies::* instanceType) const;
|
||||
void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&,
|
||||
const GrCCPRCoverageProcessor::RenderPass, GrPrimitiveType,
|
||||
int vertexCount, int PrimitiveTallies::* instanceType) const;
|
||||
|
||||
sk_sp<GrBuffer> fPointsBuffer;
|
||||
sk_sp<GrBuffer> fInstanceBuffer;
|
||||
|
@ -7,323 +7,91 @@
|
||||
|
||||
#include "GrCCPRCoverageProcessor.h"
|
||||
|
||||
#include "GrRenderTargetProxy.h"
|
||||
#include "ccpr/GrCCPRTriangleProcessor.h"
|
||||
#include "ccpr/GrCCPRQuadraticProcessor.h"
|
||||
#include "ccpr/GrCCPRCubicProcessor.h"
|
||||
#include "SkMakeUnique.h"
|
||||
#include "ccpr/GrCCPRCubicShader.h"
|
||||
#include "ccpr/GrCCPRQuadraticShader.h"
|
||||
#include "ccpr/GrCCPRTriangleShader.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryShaderBuilder.h"
|
||||
#include "glsl/GrGLSLProgramBuilder.h"
|
||||
#include "glsl/GrGLSLVertexShaderBuilder.h"
|
||||
|
||||
const char* GrCCPRCoverageProcessor::GetProcessorName(Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::kTriangleHulls:
|
||||
return "GrCCPRTriangleHullAndEdgeProcessor (hulls)";
|
||||
case Mode::kTriangleEdges:
|
||||
return "GrCCPRTriangleHullAndEdgeProcessor (edges)";
|
||||
case Mode::kTriangleCorners:
|
||||
return "GrCCPRTriangleCornerProcessor";
|
||||
case Mode::kQuadraticHulls:
|
||||
return "GrCCPRQuadraticHullProcessor";
|
||||
case Mode::kQuadraticCorners:
|
||||
return "GrCCPRQuadraticCornerProcessor";
|
||||
case Mode::kSerpentineHulls:
|
||||
return "GrCCPRCubicHullProcessor (serpentine)";
|
||||
case Mode::kLoopHulls:
|
||||
return "GrCCPRCubicHullProcessor (loop)";
|
||||
case Mode::kSerpentineCorners:
|
||||
return "GrCCPRCubicCornerProcessor (serpentine)";
|
||||
case Mode::kLoopCorners:
|
||||
return "GrCCPRCubicCornerProcessor (loop)";
|
||||
static GrVertexAttribType instance_array_format(GrCCPRCoverageProcessor::RenderPass renderPass) {
|
||||
switch (renderPass) {
|
||||
case GrCCPRCoverageProcessor::RenderPass::kTriangleHulls:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kTriangleEdges:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kTriangleCorners:
|
||||
return kInt4_GrVertexAttribType;
|
||||
case GrCCPRCoverageProcessor::RenderPass::kQuadraticHulls:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kQuadraticCorners:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kSerpentineHulls:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kLoopHulls:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kSerpentineCorners:
|
||||
case GrCCPRCoverageProcessor::RenderPass::kLoopCorners:
|
||||
return kInt2_GrVertexAttribType;
|
||||
}
|
||||
SK_ABORT("Unexpected ccpr coverage processor mode.");
|
||||
return nullptr;
|
||||
SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
|
||||
return kInt4_GrVertexAttribType;
|
||||
}
|
||||
|
||||
GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(Mode mode, GrBuffer* pointsBuffer)
|
||||
GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(RenderPass renderPass, GrBuffer* pointsBuffer)
|
||||
: INHERITED(kGrCCPRCoverageProcessor_ClassID)
|
||||
, fMode(mode)
|
||||
, fInstanceAttrib(this->addInstanceAttrib("instance", InstanceArrayFormat(mode))) {
|
||||
, fRenderPass(renderPass)
|
||||
, fInstanceAttrib(this->addInstanceAttrib("instance", instance_array_format(fRenderPass))) {
|
||||
fPointsBufferAccess.reset(kRG_float_GrPixelConfig, pointsBuffer, kVertex_GrShaderFlag);
|
||||
this->addBufferAccess(&fPointsBufferAccess);
|
||||
|
||||
this->setWillUseGeoShader();
|
||||
}
|
||||
|
||||
void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
||||
GrProcessorKeyBuilder* b) const {
|
||||
b->add32(int(fMode));
|
||||
}
|
||||
|
||||
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
||||
switch (fMode) {
|
||||
using GeometryType = GrCCPRTriangleHullAndEdgeProcessor::GeometryType;
|
||||
|
||||
case Mode::kTriangleHulls:
|
||||
return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kHulls);
|
||||
case Mode::kTriangleEdges:
|
||||
return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kEdges);
|
||||
case Mode::kTriangleCorners:
|
||||
return new GrCCPRTriangleCornerProcessor();
|
||||
case Mode::kQuadraticHulls:
|
||||
return new GrCCPRQuadraticHullProcessor();
|
||||
case Mode::kQuadraticCorners:
|
||||
return new GrCCPRQuadraticCornerProcessor();
|
||||
case Mode::kSerpentineHulls:
|
||||
return new GrCCPRCubicHullProcessor(GrCCPRCubicProcessor::CubicType::kSerpentine);
|
||||
case Mode::kLoopHulls:
|
||||
return new GrCCPRCubicHullProcessor(GrCCPRCubicProcessor::CubicType::kLoop);
|
||||
case Mode::kSerpentineCorners:
|
||||
return new GrCCPRCubicCornerProcessor(GrCCPRCubicProcessor::CubicType::kSerpentine);
|
||||
case Mode::kLoopCorners:
|
||||
return new GrCCPRCubicCornerProcessor(GrCCPRCubicProcessor::CubicType::kLoop);
|
||||
void GrCCPRCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
SkString* code, const char* position,
|
||||
const char* coverage, const char* wind) {
|
||||
WindHandling windHandling = this->onEmitVaryings(varyingHandler, code, position, coverage,
|
||||
wind);
|
||||
if (WindHandling::kNotHandled == windHandling) {
|
||||
varyingHandler->addFlatVarying("wind", &fWind, kLow_GrSLPrecision);
|
||||
code->appendf("%s = %s;", fWind.gsOut(), wind);
|
||||
}
|
||||
SK_ABORT("Unexpected ccpr coverage processor mode.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
using PrimitiveProcessor = GrCCPRCoverageProcessor::PrimitiveProcessor;
|
||||
|
||||
void PrimitiveProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
|
||||
const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
|
||||
|
||||
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
||||
switch (fCoverageType) {
|
||||
case CoverageType::kOne:
|
||||
case CoverageType::kShader:
|
||||
varyingHandler->addFlatVarying("wind", &fFragWind, kLow_GrSLPrecision);
|
||||
break;
|
||||
case CoverageType::kInterpolated:
|
||||
varyingHandler->addVarying("coverage_times_wind", &fFragCoverageTimesWind,
|
||||
kMedium_GrSLPrecision);
|
||||
break;
|
||||
void GrCCPRCoverageProcessor::Shader::emitFragmentCode(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLPPFragmentBuilder* f,
|
||||
const char* skOutputColor,
|
||||
const char* skOutputCoverage) const {
|
||||
f->codeAppendf("half coverage = 0;");
|
||||
this->onEmitFragmentCode(f, "coverage");
|
||||
if (fWind.fsIn()) {
|
||||
f->codeAppendf("%s.a = coverage * %s;", skOutputColor, fWind.fsIn());
|
||||
} else {
|
||||
f->codeAppendf("%s.a = coverage;", skOutputColor);
|
||||
}
|
||||
this->resetVaryings(varyingHandler);
|
||||
|
||||
varyingHandler->emitAttributes(proc);
|
||||
|
||||
this->emitVertexShader(proc, args.fVertBuilder, args.fTexelBuffers[0], args.fRTAdjustName,
|
||||
gpArgs);
|
||||
this->emitGeometryShader(proc, args.fGeomBuilder, args.fRTAdjustName);
|
||||
this->emitCoverage(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
||||
|
||||
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
|
||||
}
|
||||
|
||||
void PrimitiveProcessor::emitVertexShader(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLVertexBuilder* v,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* rtAdjust, GrGPArgs* gpArgs) const {
|
||||
v->codeAppendf("int packedoffset = %s[%i];", proc.instanceAttrib(), proc.atlasOffsetIdx());
|
||||
v->codeAppend ("float2 atlasoffset = float2((packedoffset<<16) >> 16, "
|
||||
"packedoffset >> 16);");
|
||||
|
||||
this->onEmitVertexShader(proc, v, pointsBuffer, "atlasoffset", rtAdjust, gpArgs);
|
||||
}
|
||||
|
||||
void PrimitiveProcessor::emitGeometryShader(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLGeometryBuilder* g, const char* rtAdjust) const {
|
||||
g->declareGlobal(fGeomWind);
|
||||
this->emitWind(g, rtAdjust, fGeomWind.c_str());
|
||||
|
||||
SkString emitVertexFn;
|
||||
SkSTArray<2, GrShaderVar> emitArgs;
|
||||
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType,
|
||||
GrShaderVar::kNonArray).c_str();
|
||||
const char* coverage = emitArgs.emplace_back("coverage", kFloat_GrSLType,
|
||||
GrShaderVar::kNonArray).c_str();
|
||||
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
||||
SkString fnBody;
|
||||
this->emitPerVertexGeometryCode(&fnBody, position, coverage, fGeomWind.c_str());
|
||||
if (fFragWind.gsOut()) {
|
||||
fnBody.appendf("%s = %s;", fFragWind.gsOut(), fGeomWind.c_str());
|
||||
}
|
||||
if (fFragCoverageTimesWind.gsOut()) {
|
||||
fnBody.appendf("%s = %s * %s;",
|
||||
fFragCoverageTimesWind.gsOut(), coverage, fGeomWind.c_str());
|
||||
}
|
||||
fnBody.append ("sk_Position = float4(position, 0, 1);");
|
||||
fnBody.append ("EmitVertex();");
|
||||
return fnBody;
|
||||
}().c_str(), &emitVertexFn);
|
||||
|
||||
g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
|
||||
|
||||
f->codeAppendf("%s = half4(1);", skOutputCoverage);
|
||||
#ifdef SK_DEBUG
|
||||
if (proc.debugVisualizationsEnabled()) {
|
||||
g->codeAppendf("bloat *= %f;", proc.debugBloat());
|
||||
f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);",
|
||||
skOutputColor, skOutputColor, skOutputColor);
|
||||
}
|
||||
#endif
|
||||
|
||||
return this->onEmitGeometryShader(g, emitVertexFn.c_str(), fGeomWind.c_str(), rtAdjust);
|
||||
}
|
||||
|
||||
int PrimitiveProcessor::emitHullGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
|
||||
const char* polygonPts, int numSides,
|
||||
const char* wedgeIdx, const char* midpoint) const {
|
||||
SkASSERT(numSides >= 3);
|
||||
|
||||
if (!midpoint) {
|
||||
g->codeAppendf("float2 midpoint = %s * float%i(%f);",
|
||||
polygonPts, numSides, 1.0 / numSides);
|
||||
midpoint = "midpoint";
|
||||
}
|
||||
|
||||
g->codeAppendf("int previdx = (%s + %i) %% %i, "
|
||||
"nextidx = (%s + 1) %% %i;",
|
||||
wedgeIdx, numSides - 1, numSides, wedgeIdx, numSides);
|
||||
|
||||
g->codeAppendf("float2 self = %s[%s];"
|
||||
"int leftidx = %s > 0 ? previdx : nextidx;"
|
||||
"int rightidx = %s > 0 ? nextidx : previdx;",
|
||||
polygonPts, wedgeIdx, fGeomWind.c_str(), fGeomWind.c_str());
|
||||
|
||||
// Which quadrant does the vector from self -> right fall into?
|
||||
g->codeAppendf("float2 right = %s[rightidx];", polygonPts);
|
||||
if (3 == numSides) {
|
||||
// TODO: evaluate perf gains.
|
||||
g->codeAppend ("float2 qsr = sign(right - self);");
|
||||
} else {
|
||||
SkASSERT(4 == numSides);
|
||||
g->codeAppendf("float2 diag = %s[(%s + 2) %% 4];", polygonPts, wedgeIdx);
|
||||
g->codeAppend ("float2 qsr = sign((right != self ? right : diag) - self);");
|
||||
}
|
||||
|
||||
// Which quadrant does the vector from left -> self fall into?
|
||||
g->codeAppendf("float2 qls = sign(self - %s[leftidx]);", polygonPts);
|
||||
|
||||
// d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
|
||||
// TODO: evaluate perf gains.
|
||||
const char* dr2 = "dr";
|
||||
if (3 == numSides) {
|
||||
// TODO: evaluate perf gains.
|
||||
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
|
||||
"qsr.x != 0 ? -qsr.x : +qsr.y);");
|
||||
g->codeAppend ("float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
|
||||
"qsr.x != 0 ? -qsr.x : -qsr.y);");
|
||||
g->codeAppend ("float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
|
||||
"qls.x != 0 ? -qls.x : +qls.y);");
|
||||
dr2 = "dr2";
|
||||
} else {
|
||||
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
|
||||
"qsr.x != 0 ? -qsr.x : 1);");
|
||||
g->codeAppend ("float2 dl = (qls == float2(0)) ? dr : "
|
||||
"float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
|
||||
}
|
||||
g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
|
||||
|
||||
// Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
|
||||
// Each invocation emits a different third.
|
||||
g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
|
||||
g->codeAppendf("%s(%s, 1);", emitVertexFn, midpoint);
|
||||
g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
|
||||
g->codeAppend ("if (any(dnotequal)) {");
|
||||
g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppend ("if (all(dnotequal)) {");
|
||||
g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppend ("EndPrimitive();");
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
int PrimitiveProcessor::emitEdgeGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
|
||||
const char* leftPt, const char* rightPt,
|
||||
const char* distanceEquation) const {
|
||||
if (!distanceEquation) {
|
||||
this->emitEdgeDistanceEquation(g, leftPt, rightPt, "float3 edge_distance_equation");
|
||||
distanceEquation = "edge_distance_equation";
|
||||
}
|
||||
|
||||
// qlr is defined in emitEdgeDistanceEquation.
|
||||
g->codeAppendf("float2x2 endpts = float2x2(%s - bloat * qlr, %s + bloat * qlr);",
|
||||
leftPt, rightPt);
|
||||
g->codeAppendf("half2 endpts_coverage = %s.xy * endpts + %s.z;",
|
||||
distanceEquation, distanceEquation);
|
||||
|
||||
// d1 is defined in emitEdgeDistanceEquation.
|
||||
g->codeAppend ("float2 d2 = d1;");
|
||||
g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
|
||||
g->codeAppend ("if (aligned) {");
|
||||
g->codeAppend ( "d1 -= qlr;");
|
||||
g->codeAppend ( "d2 += qlr;");
|
||||
g->codeAppend ("}");
|
||||
|
||||
// Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
|
||||
// invocation emits a different edge. Emit negative coverage that subtracts the appropiate
|
||||
// amount back out from the hull we drew above.
|
||||
g->codeAppend ("if (!aligned) {");
|
||||
g->codeAppendf( "%s(endpts[0], endpts_coverage[0]);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppendf("%s(%s + bloat * d1, -1);", emitVertexFn, leftPt);
|
||||
g->codeAppendf("%s(%s - bloat * d2, 0);", emitVertexFn, leftPt);
|
||||
g->codeAppendf("%s(%s + bloat * d2, -1);", emitVertexFn, rightPt);
|
||||
g->codeAppendf("%s(%s - bloat * d1, 0);", emitVertexFn, rightPt);
|
||||
g->codeAppend ("if (!aligned) {");
|
||||
g->codeAppendf( "%s(endpts[1], endpts_coverage[1]);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppend ("EndPrimitive();");
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
void PrimitiveProcessor::emitEdgeDistanceEquation(GrGLSLGeometryBuilder* g,
|
||||
const char* leftPt, const char* rightPt,
|
||||
const char* outputDistanceEquation) const {
|
||||
void GrCCPRCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLShaderBuilder* s,
|
||||
const char* leftPt,
|
||||
const char* rightPt,
|
||||
const char* outputDistanceEquation) {
|
||||
// Which quadrant does the vector from left -> right fall into?
|
||||
g->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
|
||||
g->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
|
||||
s->codeAppendf("float2 qlr = sign(%s - %s);", rightPt, leftPt);
|
||||
s->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
|
||||
|
||||
g->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
|
||||
s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
|
||||
rightPt, leftPt, leftPt, rightPt);
|
||||
g->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
|
||||
s->codeAppendf("float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
|
||||
leftPt, leftPt);
|
||||
// Clamp for when n=0. wind=0 when n=0 so as long as we don't get Inf or NaN we are fine.
|
||||
g->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
|
||||
s->codeAppendf("float scale = 1 / max(kk[0] - kk[1], 1e-30);");
|
||||
|
||||
g->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
|
||||
s->codeAppendf("%s = half3(-n, kk[1]) * scale;", outputDistanceEquation);
|
||||
}
|
||||
|
||||
int PrimitiveProcessor::emitCornerGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
|
||||
const char* pt) const {
|
||||
g->codeAppendf("%s(%s + float2(-bloat.x, -bloat.y), 1);", emitVertexFn, pt);
|
||||
g->codeAppendf("%s(%s + float2(-bloat.x, +bloat.y), 1);", emitVertexFn, pt);
|
||||
g->codeAppendf("%s(%s + float2(+bloat.x, -bloat.y), 1);", emitVertexFn, pt);
|
||||
g->codeAppendf("%s(%s + float2(+bloat.x, +bloat.y), 1);", emitVertexFn, pt);
|
||||
g->codeAppend ("EndPrimitive();");
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
void PrimitiveProcessor::emitCoverage(const GrCCPRCoverageProcessor& proc, GrGLSLFragmentBuilder* f,
|
||||
const char* outputColor, const char* outputCoverage) const {
|
||||
switch (fCoverageType) {
|
||||
case CoverageType::kOne:
|
||||
f->codeAppendf("%s.a = %s;", outputColor, fFragWind.fsIn());
|
||||
break;
|
||||
case CoverageType::kInterpolated:
|
||||
f->codeAppendf("%s.a = %s;", outputColor, fFragCoverageTimesWind.fsIn());
|
||||
break;
|
||||
case CoverageType::kShader:
|
||||
f->codeAppendf("half coverage = 0;");
|
||||
this->emitShaderCoverage(f, "coverage");
|
||||
f->codeAppendf("%s.a = coverage * %s;", outputColor, fFragWind.fsIn());
|
||||
break;
|
||||
}
|
||||
|
||||
f->codeAppendf("%s = half4(1);", outputCoverage);
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
if (proc.debugVisualizationsEnabled()) {
|
||||
f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);", outputColor, outputColor, outputColor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
|
||||
const char* samplesName) const {
|
||||
int GrCCPRCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentBuilder* 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]("
|
||||
@ -342,9 +110,74 @@ int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
|
||||
#endif
|
||||
}
|
||||
|
||||
void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
||||
GrProcessorKeyBuilder* b) const {
|
||||
b->add32((int)fRenderPass);
|
||||
}
|
||||
|
||||
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
||||
std::unique_ptr<Shader> shader;
|
||||
switch (fRenderPass) {
|
||||
using CubicType = GrCCPRCubicShader::CubicType;
|
||||
case RenderPass::kTriangleHulls:
|
||||
shader = skstd::make_unique<GrCCPRTriangleHullShader>();
|
||||
break;
|
||||
case RenderPass::kTriangleEdges:
|
||||
shader = skstd::make_unique<GrCCPRTriangleEdgeShader>();
|
||||
break;
|
||||
case RenderPass::kTriangleCorners:
|
||||
shader = skstd::make_unique<GrCCPRTriangleCornerShader>();
|
||||
break;
|
||||
case RenderPass::kQuadraticHulls:
|
||||
shader = skstd::make_unique<GrCCPRQuadraticHullShader>();
|
||||
break;
|
||||
case RenderPass::kQuadraticCorners:
|
||||
shader = skstd::make_unique<GrCCPRQuadraticCornerShader>();
|
||||
break;
|
||||
case RenderPass::kSerpentineHulls:
|
||||
shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kSerpentine);
|
||||
break;
|
||||
case RenderPass::kLoopHulls:
|
||||
shader = skstd::make_unique<GrCCPRCubicHullShader>(CubicType::kLoop);
|
||||
break;
|
||||
case RenderPass::kSerpentineCorners:
|
||||
shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kSerpentine);
|
||||
break;
|
||||
case RenderPass::kLoopCorners:
|
||||
shader = skstd::make_unique<GrCCPRCubicCornerShader>(CubicType::kLoop);
|
||||
break;
|
||||
}
|
||||
return CreateGSImpl(std::move(shader));
|
||||
}
|
||||
|
||||
const char* GrCCPRCoverageProcessor::GetRenderPassName(RenderPass renderPass) {
|
||||
switch (renderPass) {
|
||||
case RenderPass::kTriangleHulls:
|
||||
return "RenderPass::kTriangleHulls";
|
||||
case RenderPass::kTriangleEdges:
|
||||
return "RenderPass::kTriangleEdges";
|
||||
case RenderPass::kTriangleCorners:
|
||||
return "RenderPass::kTriangleCorners";
|
||||
case RenderPass::kQuadraticHulls:
|
||||
return "RenderPass::kQuadraticHulls";
|
||||
case RenderPass::kQuadraticCorners:
|
||||
return "RenderPass::kQuadraticCorners";
|
||||
case RenderPass::kSerpentineHulls:
|
||||
return "RenderPass::kSerpentineHulls";
|
||||
case RenderPass::kLoopHulls:
|
||||
return "RenderPass::kLoopHulls";
|
||||
case RenderPass::kSerpentineCorners:
|
||||
return "RenderPass::kSerpentineCorners";
|
||||
case RenderPass::kLoopCorners:
|
||||
return "RenderPass::kLoopCorners";
|
||||
}
|
||||
SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
|
||||
#include "GrRenderTarget.h"
|
||||
#include "GrRenderTargetProxy.h"
|
||||
|
||||
void GrCCPRCoverageProcessor::Validate(GrRenderTargetProxy* atlasProxy) {
|
||||
SkASSERT(kAtlasOrigin == atlasProxy->origin());
|
||||
|
@ -12,21 +12,22 @@
|
||||
#include "glsl/GrGLSLGeometryProcessor.h"
|
||||
#include "glsl/GrGLSLVarying.h"
|
||||
|
||||
class GrGLSLFragmentBuilder;
|
||||
class GrGLSLPPFragmentBuilder;
|
||||
class GrGLSLShaderBuilder;
|
||||
|
||||
/**
|
||||
* This is the geometry processor for the simple convex primitive shapes (triangles and closed curve
|
||||
* segments) from which ccpr paths are composed. The output is a single-channel alpha value,
|
||||
* positive for clockwise primitives and negative for counter-clockwise, that indicates coverage.
|
||||
* positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
|
||||
*
|
||||
* The caller is responsible to render all modes for all applicable primitives 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 GrCCPRPathProcessor).
|
||||
* 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 GrCCPRPathProcessor).
|
||||
*
|
||||
* Caller provides the primitives' (x,y) points in an fp32x2 (RG) texel buffer, and an instance
|
||||
* buffer with a single int32x4 attrib (for triangles) or int32x2 (for curves) defined below. There
|
||||
* are no vertex attribs.
|
||||
* Caller provides the primitives' (x,y) input points in an fp32x2 (RG) texel buffer, and an
|
||||
* instance buffer with a single int32x4 attrib (for triangles) or int32x2 (for curves) defined
|
||||
* below. There are no vertex attribs.
|
||||
*
|
||||
* Draw calls are instanced, with one vertex per bezier point (3 for triangles). They use the
|
||||
* corresponding GrPrimitiveType as defined below.
|
||||
@ -56,7 +57,12 @@ public:
|
||||
|
||||
GR_STATIC_ASSERT(2 * 4 == sizeof(CurveInstance));
|
||||
|
||||
enum class Mode {
|
||||
/**
|
||||
* All primitive shapes (triangles and convex closed curve segments) require more than one
|
||||
* render pass. 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 {
|
||||
// Triangles.
|
||||
kTriangleHulls,
|
||||
kTriangleEdges,
|
||||
@ -72,22 +78,132 @@ public:
|
||||
kSerpentineCorners,
|
||||
kLoopCorners
|
||||
};
|
||||
static constexpr GrVertexAttribType InstanceArrayFormat(Mode mode) {
|
||||
return mode < Mode::kQuadraticHulls ? kInt4_GrVertexAttribType : kInt2_GrVertexAttribType;
|
||||
}
|
||||
static const char* GetProcessorName(Mode);
|
||||
|
||||
GrCCPRCoverageProcessor(Mode, GrBuffer* pointsBuffer);
|
||||
static const char* GetRenderPassName(RenderPass);
|
||||
|
||||
/**
|
||||
* This serves as the base class for each RenderPass's Shader. It indicates what type of
|
||||
* geometry the Impl should generate and provides implementation-independent code to process
|
||||
* the inputs and calculate coverage in the fragment Shader.
|
||||
*/
|
||||
class Shader {
|
||||
public:
|
||||
using TexelBufferHandle = GrGLSLGeometryProcessor::TexelBufferHandle;
|
||||
|
||||
// This enum specifies the type of geometry that should be generated for a Shader instance.
|
||||
// Subclasses are limited to three built-in types of geometry to choose from:
|
||||
enum class GeometryType {
|
||||
// Generates a conservative raster hull around the input points. This is the geometry
|
||||
// that causes a pixel to be rasterized if it is touched anywhere by the input polygon.
|
||||
// Coverage is +1 all around.
|
||||
//
|
||||
// Logically, the conservative raster hull is equivalent to the convex hull of pixel
|
||||
// size boxes centered around each input point.
|
||||
kHull,
|
||||
|
||||
// Generates the conservative rasters of the input edges (i.e. convex hull of two
|
||||
// pixel-size boxes centered on both endpoints). Coverage is -1 on the outside border of
|
||||
// the edge geometry and 0 on the inside. This is the only geometry type that associates
|
||||
// coverage values with the output points. It effectively converts a jagged conservative
|
||||
// raster edge into a smooth antialiased edge.
|
||||
kEdges,
|
||||
|
||||
// Generates the conservative rasters of the corners specified by the geometry provider
|
||||
// (i.e. pixel-size box centered on the corner point). Coverage is +1 all around.
|
||||
kCorners
|
||||
};
|
||||
|
||||
virtual GeometryType getGeometryType() const = 0;
|
||||
virtual int getNumInputPoints() const = 0;
|
||||
|
||||
// Returns the number of independent geometric segments to generate for the render pass
|
||||
// (number of wedges for a hull, number of edges, or number of corners.)
|
||||
virtual int getNumSegments() const = 0;
|
||||
|
||||
// Appends an expression that fetches input point # "pointId" from the texel buffer.
|
||||
virtual void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const = 0;
|
||||
|
||||
// Determines the winding direction of the primitive. The subclass must write a value of
|
||||
// either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
|
||||
virtual void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
|
||||
const char* outputWind) const = 0;
|
||||
|
||||
union GeometryVars {
|
||||
struct {
|
||||
const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
|
||||
const char* fAlternateMidpoint; // float2 (if left null, finds euclidean midpoint).
|
||||
} fHullVars;
|
||||
|
||||
struct {
|
||||
const char* fPoint; // float2
|
||||
} fCornerVars;
|
||||
|
||||
GeometryVars() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Called before generating geometry. Subclasses must fill out the applicable fields in
|
||||
// GeometryVars (if any), and may also use this opportunity to setup internal member
|
||||
// variables that will be needed during onEmitVaryings (e.g. transformation matrices).
|
||||
virtual void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
|
||||
const char* bloat, const char* wind, const char* rtAdjust,
|
||||
GeometryVars*) const {}
|
||||
|
||||
void emitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
|
||||
const char* coverage, const char* wind);
|
||||
|
||||
void emitFragmentCode(const GrCCPRCoverageProcessor& proc, GrGLSLPPFragmentBuilder*,
|
||||
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 (see emitEdgeGeometry).
|
||||
static void EmitEdgeDistanceEquation(GrGLSLShaderBuilder*, const char* leftPt,
|
||||
const char* rightPt,
|
||||
const char* outputDistanceEquation);
|
||||
|
||||
// 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(GrGLSLPPFragmentBuilder* f, const char* samplesName);
|
||||
|
||||
virtual ~Shader() {}
|
||||
|
||||
protected:
|
||||
enum class WindHandling : bool {
|
||||
kHandled,
|
||||
kNotHandled
|
||||
};
|
||||
|
||||
// Here the subclass adds its internal varyings to the handler and produces code to
|
||||
// initialize those varyings from a given position, coverage value, and wind.
|
||||
//
|
||||
// Returns whether the subclass will handle wind modulation or if this base class should
|
||||
// take charge of multiplying the final coverage output by "wind".
|
||||
//
|
||||
// NOTE: the coverage parameter is only relevant for edges (see comments in GeometryType).
|
||||
// Otherwise it is +1 all around.
|
||||
virtual WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
|
||||
const char* position, const char* coverage,
|
||||
const char* wind) = 0;
|
||||
|
||||
// Emits the fragment code that calculates a pixel's coverage value. If using
|
||||
// WindHandling::kHandled, this value must be signed appropriately.
|
||||
virtual void onEmitFragmentCode(GrGLSLPPFragmentBuilder*,
|
||||
const char* outputCoverage) const = 0;
|
||||
|
||||
private:
|
||||
GrGLSLGeoToFrag fWind{kHalf_GrSLType};
|
||||
};
|
||||
|
||||
GrCCPRCoverageProcessor(RenderPass, GrBuffer* pointsBuffer);
|
||||
|
||||
const char* instanceAttrib() const { return fInstanceAttrib.fName; }
|
||||
int atlasOffsetIdx() const {
|
||||
return kInt4_GrVertexAttribType == InstanceArrayFormat(fMode) ? 3 : 1;
|
||||
}
|
||||
const char* name() const override { return GetProcessorName(fMode); }
|
||||
const char* name() const override { return GetRenderPassName(fRenderPass); }
|
||||
SkString dumpInfo() const override {
|
||||
return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
|
||||
}
|
||||
|
||||
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
|
||||
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
|
||||
|
||||
@ -101,152 +217,27 @@ public:
|
||||
static void Validate(GrRenderTargetProxy* atlasProxy);
|
||||
#endif
|
||||
|
||||
class PrimitiveProcessor;
|
||||
class GSImpl;
|
||||
|
||||
private:
|
||||
const Mode fMode;
|
||||
const Attribute& fInstanceAttrib;
|
||||
BufferAccess fPointsBufferAccess;
|
||||
SkDEBUGCODE(float fDebugBloat = false;)
|
||||
|
||||
typedef GrGeometryProcessor INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class represents the actual SKSL implementation for the various primitives and modes of
|
||||
* GrCCPRCoverageProcessor.
|
||||
*/
|
||||
class GrCCPRCoverageProcessor::PrimitiveProcessor : public GrGLSLGeometryProcessor {
|
||||
protected:
|
||||
// Slightly undershoot a bloat radius of 0.5 so vertices that fall on integer boundaries don't
|
||||
// accidentally bleed into neighbor pixels.
|
||||
static constexpr float kAABloatRadius = 0.491111f;
|
||||
|
||||
// Specifies how the fragment shader should calculate sk_FragColor.a.
|
||||
enum class CoverageType {
|
||||
kOne, // Output +1 all around, modulated by wind.
|
||||
kInterpolated, // Interpolate the coverage values that the geometry shader associates with
|
||||
// each point, modulated by wind.
|
||||
kShader // Call emitShaderCoverage and let the subclass decide, then a modulate by wind.
|
||||
};
|
||||
static GrGLSLPrimitiveProcessor* CreateGSImpl(std::unique_ptr<Shader>);
|
||||
|
||||
PrimitiveProcessor(CoverageType coverageType)
|
||||
: fCoverageType(coverageType)
|
||||
, fGeomWind("wind", kHalf_GrSLType, GrShaderVar::kNonArray, kLow_GrSLPrecision)
|
||||
, fFragWind(kHalf_GrSLType)
|
||||
, fFragCoverageTimesWind(kHalf_GrSLType) {}
|
||||
|
||||
// Called before generating shader code. Subclass should add its custom varyings to the handler
|
||||
// and update its corresponding internal member variables.
|
||||
virtual void resetVaryings(GrGLSLVaryingHandler*) {}
|
||||
|
||||
// Here the subclass fetches its vertex from the texel buffer, translates by atlasOffset, and
|
||||
// sets "fPositionVar" in the GrGPArgs.
|
||||
virtual void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
|
||||
const char* rtAdjust, GrGPArgs*) const = 0;
|
||||
|
||||
// Here the subclass determines the winding direction of its primitive. It must write a value of
|
||||
// either -1, 0, or +1 to "outputWind" (e.g. "sign(area)"). Fractional values are not valid.
|
||||
virtual void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust,
|
||||
const char* outputWind) const = 0;
|
||||
|
||||
// This is where the subclass generates the actual geometry to be rasterized by hardware:
|
||||
//
|
||||
// emitVertexFn(point1, coverage);
|
||||
// emitVertexFn(point2, coverage);
|
||||
// ...
|
||||
// EndPrimitive();
|
||||
//
|
||||
// Generally a subclass will want to use emitHullGeometry and/or emitEdgeGeometry rather than
|
||||
// calling emitVertexFn directly.
|
||||
//
|
||||
// Subclass must also call GrGLSLGeometryBuilder::configure.
|
||||
virtual void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* wind, const char* rtAdjust) const = 0;
|
||||
|
||||
// This is a hook to inject code in the geometry shader's "emitVertex" function. Subclass
|
||||
// should use this to write values to its custom varyings.
|
||||
// NOTE: even flat varyings should be rewritten at each vertex.
|
||||
virtual void emitPerVertexGeometryCode(SkString* fnBody, const char* position,
|
||||
const char* coverage, const char* wind) const {}
|
||||
|
||||
// Called when the subclass has selected CoverageType::kShader. Primitives should produce
|
||||
// coverage values between +0..1. Base class modulates the sign for wind.
|
||||
// TODO: subclasses might have good spots to stuff the winding information without burning a
|
||||
// whole new varying slot. Consider requiring them to generate the correct coverage sign.
|
||||
virtual void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const {
|
||||
SK_ABORT("Shader coverage not implemented when using CoverageType::kShader.");
|
||||
int atlasOffsetIdx() const {
|
||||
SkASSERT(kInt2_GrVertexAttribType == fInstanceAttrib.fType ||
|
||||
kInt4_GrVertexAttribType == fInstanceAttrib.fType);
|
||||
return kInt4_GrVertexAttribType == fInstanceAttrib.fType ? 3 : 1;
|
||||
}
|
||||
|
||||
// Emits one wedge of the conservative raster hull of a convex polygon. The complete hull has
|
||||
// one wedge for each side of the polygon (i.e. call this N times, generally from different
|
||||
// geometry shader invocations). Coverage is +1 all around.
|
||||
//
|
||||
// Logically, the conservative raster hull is equivalent to the convex hull of pixel-size boxes
|
||||
// centered on the vertices.
|
||||
//
|
||||
// Geometry shader must be configured to output triangle strips.
|
||||
//
|
||||
// Returns the maximum number of vertices that will be emitted.
|
||||
int emitHullGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* polygonPts,
|
||||
int numSides, const char* wedgeIdx, const char* midpoint = nullptr) const;
|
||||
const RenderPass fRenderPass;
|
||||
const Attribute& fInstanceAttrib;
|
||||
BufferAccess fPointsBufferAccess;
|
||||
SkDEBUGCODE(float fDebugBloat = 0;)
|
||||
|
||||
// Emits the conservative raster of an edge (i.e. convex hull of two pixel-size boxes centered
|
||||
// on the endpoints). Coverage is -1 on the outside border of the edge geometry and 0 on the
|
||||
// inside. This effectively converts a jagged conservative raster edge into a smooth antialiased
|
||||
// edge when using CoverageType::kInterpolated.
|
||||
//
|
||||
// If the subclass has already called emitEdgeDistanceEquation, then provide the distance
|
||||
// equation. Otherwise this function will call emitEdgeDistanceEquation implicitly.
|
||||
//
|
||||
// Geometry shader must be configured to output triangle strips.
|
||||
//
|
||||
// Returns the maximum number of vertices that will be emitted.
|
||||
int emitEdgeGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* leftPt,
|
||||
const char* rightPt, const char* distanceEquation = nullptr) 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 (see emitEdgeGeometry).
|
||||
void emitEdgeDistanceEquation(GrGLSLGeometryBuilder*, const char* leftPt, const char* rightPt,
|
||||
const char* outputDistanceEquation) const;
|
||||
|
||||
// Emits the conservative raster of a single point (i.e. pixel-size box centered on the point).
|
||||
// Coverage is +1 all around.
|
||||
//
|
||||
// Geometry shader must be configured to output triangle strips.
|
||||
//
|
||||
// Returns the number of vertices that were emitted.
|
||||
int emitCornerGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* pt) const;
|
||||
|
||||
// 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.
|
||||
int defineSoftSampleLocations(GrGLSLFragmentBuilder*, const char* samplesName) const;
|
||||
|
||||
private:
|
||||
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
||||
FPCoordTransformIter&& transformIter) final {
|
||||
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
||||
}
|
||||
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final;
|
||||
|
||||
void emitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer, const char* rtAdjust,
|
||||
GrGPArgs* gpArgs) const;
|
||||
void emitGeometryShader(const GrCCPRCoverageProcessor&, GrGLSLGeometryBuilder*,
|
||||
const char* rtAdjust) const;
|
||||
void emitCoverage(const GrCCPRCoverageProcessor&, GrGLSLFragmentBuilder*,
|
||||
const char* outputColor, const char* outputCoverage) const;
|
||||
|
||||
const CoverageType fCoverageType;
|
||||
GrShaderVar fGeomWind;
|
||||
GrGLSLGeoToFrag fFragWind;
|
||||
GrGLSLGeoToFrag fFragCoverageTimesWind;
|
||||
|
||||
typedef GrGLSLGeometryProcessor INHERITED;
|
||||
typedef GrGeometryProcessor INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
278
src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
Normal file
278
src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRCoverageProcessor.h"
|
||||
|
||||
#include "glsl/GrGLSLGeometryShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexShaderBuilder.h"
|
||||
|
||||
using Shader = GrCCPRCoverageProcessor::Shader;
|
||||
|
||||
/**
|
||||
* This class and its subclasses implement the coverage processor with geometry shaders.
|
||||
*/
|
||||
class GrCCPRCoverageProcessor::GSImpl : public GrGLSLGeometryProcessor {
|
||||
protected:
|
||||
GSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
|
||||
|
||||
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
||||
FPCoordTransformIter&& transformIter) final {
|
||||
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
||||
}
|
||||
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
|
||||
const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
|
||||
|
||||
// Vertex shader.
|
||||
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
||||
// The Intel GLSL compiler hits an internal assertion if we index the input attrib itself
|
||||
// with sk_VertexID.
|
||||
v->codeAppendf("int pointID = sk_VertexID;");
|
||||
v->codeAppend ("float2 self = ");
|
||||
fShader->appendInputPointFetch(proc, v, args.fTexelBuffers[0], "pointID");
|
||||
v->codeAppend (".xy;");
|
||||
v->codeAppendf("int packedoffset = %s[%i];",
|
||||
proc.fInstanceAttrib.fName, proc.atlasOffsetIdx());
|
||||
v->codeAppend ("float2 atlasoffset = float2((packedoffset << 16) >> 16, "
|
||||
"packedoffset >> 16);");
|
||||
v->codeAppend ("self += atlasoffset;");
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
|
||||
|
||||
// Geometry shader.
|
||||
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
||||
this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName);
|
||||
varyingHandler->emitAttributes(proc);
|
||||
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
|
||||
|
||||
// Fragment shader.
|
||||
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
||||
}
|
||||
|
||||
void emitGeometryShader(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLVaryingHandler* varyingHandler, GrGLSLGeometryBuilder* g,
|
||||
const char* rtAdjust) const {
|
||||
using InputType = GrGLSLGeometryBuilder::InputType;
|
||||
using OutputType = GrGLSLGeometryBuilder::OutputType;
|
||||
|
||||
int numPts = fShader->getNumInputPoints();
|
||||
SkASSERT(3 == numPts || 4 == numPts);
|
||||
|
||||
g->codeAppendf("float%ix2 pts = float%ix2(", numPts, numPts);
|
||||
for (int i = 0; i < numPts; ++i) {
|
||||
g->codeAppend (i ? ", " : "");
|
||||
g->codeAppendf("sk_in[%i].sk_Position.xy", i);
|
||||
}
|
||||
g->codeAppend (");");
|
||||
|
||||
GrShaderVar wind("wind", kHalf_GrSLType);
|
||||
g->declareGlobal(wind);
|
||||
fShader->emitWind(g, "pts", rtAdjust, wind.c_str());
|
||||
|
||||
SkString emitVertexFn;
|
||||
SkSTArray<2, GrShaderVar> emitArgs;
|
||||
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
|
||||
const char* coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
||||
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
||||
SkString fnBody;
|
||||
fShader->emitVaryings(varyingHandler, &fnBody, position, coverage, wind.c_str());
|
||||
fnBody.append("sk_Position = float4(position, 0, 1);");
|
||||
fnBody.append("EmitVertex();");
|
||||
return fnBody;
|
||||
}().c_str(), &emitVertexFn);
|
||||
|
||||
g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
|
||||
#ifdef SK_DEBUG
|
||||
if (proc.debugVisualizationsEnabled()) {
|
||||
g->codeAppendf("bloat *= %f;", proc.debugBloat());
|
||||
}
|
||||
#endif
|
||||
|
||||
Shader::GeometryVars vars;
|
||||
fShader->emitSetupCode(g, "pts", "sk_InvocationID", "bloat", wind.c_str(), rtAdjust, &vars);
|
||||
int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), rtAdjust, vars);
|
||||
|
||||
int numInputPoints = fShader->getNumInputPoints();
|
||||
SkASSERT(3 == numInputPoints || 4 == numInputPoints);
|
||||
InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
|
||||
: InputType::kLinesAdjacency;
|
||||
|
||||
g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
|
||||
}
|
||||
|
||||
virtual int onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
|
||||
const char* emitVertexFn, const char* rtAdjust,
|
||||
const Shader::GeometryVars&) const = 0;
|
||||
|
||||
virtual ~GSImpl() {}
|
||||
|
||||
const std::unique_ptr<Shader> fShader;
|
||||
|
||||
typedef GrGLSLGeometryProcessor INHERITED;
|
||||
};
|
||||
|
||||
class GSHullImpl : public GrCCPRCoverageProcessor::GSImpl {
|
||||
public:
|
||||
GSHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||
|
||||
int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn, const char* rtAdjust,
|
||||
const Shader::GeometryVars& vars) const override {
|
||||
int numSides = fShader->getNumSegments();
|
||||
SkASSERT(numSides >= 3);
|
||||
|
||||
const char* hullPts = vars.fHullVars.fAlternatePoints;
|
||||
if (!hullPts) {
|
||||
SkASSERT(fShader->getNumInputPoints() == numSides);
|
||||
hullPts = "pts";
|
||||
}
|
||||
|
||||
const char* midpoint = vars.fHullVars.fAlternateMidpoint;
|
||||
if (!midpoint) {
|
||||
g->codeAppendf("float2 midpoint = %s * float%i(%f);", hullPts, numSides, 1.0/numSides);
|
||||
midpoint = "midpoint";
|
||||
}
|
||||
|
||||
g->codeAppendf("int previdx = (sk_InvocationID + %i) %% %i, "
|
||||
"nextidx = (sk_InvocationID + 1) %% %i;",
|
||||
numSides - 1, numSides, numSides);
|
||||
|
||||
g->codeAppendf("float2 self = %s[sk_InvocationID];"
|
||||
"int leftidx = %s > 0 ? previdx : nextidx;"
|
||||
"int rightidx = %s > 0 ? nextidx : previdx;",
|
||||
hullPts, wind.c_str(), wind.c_str());
|
||||
|
||||
// Which quadrant does the vector from self -> right fall into?
|
||||
g->codeAppendf("float2 right = %s[rightidx];", hullPts);
|
||||
if (3 == numSides) {
|
||||
// TODO: evaluate perf gains.
|
||||
g->codeAppend ("float2 qsr = sign(right - self);");
|
||||
} else {
|
||||
SkASSERT(4 == numSides);
|
||||
g->codeAppendf("float2 diag = %s[(sk_InvocationID + 2) %% 4];", hullPts);
|
||||
g->codeAppend ("float2 qsr = sign((right != self ? right : diag) - self);");
|
||||
}
|
||||
|
||||
// Which quadrant does the vector from left -> self fall into?
|
||||
g->codeAppendf("float2 qls = sign(self - %s[leftidx]);", hullPts);
|
||||
|
||||
// d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
|
||||
// TODO: evaluate perf gains.
|
||||
const char* dr2 = "dr";
|
||||
if (3 == numSides) {
|
||||
// TODO: evaluate perf gains.
|
||||
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
|
||||
"qsr.x != 0 ? -qsr.x : +qsr.y);");
|
||||
g->codeAppend ("float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
|
||||
"qsr.x != 0 ? -qsr.x : -qsr.y);");
|
||||
g->codeAppend ("float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
|
||||
"qls.x != 0 ? -qls.x : +qls.y);");
|
||||
dr2 = "dr2";
|
||||
} else {
|
||||
g->codeAppend ("float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
|
||||
"qsr.x != 0 ? -qsr.x : 1);");
|
||||
g->codeAppend ("float2 dl = (qls == float2(0)) ? dr : "
|
||||
"float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
|
||||
}
|
||||
g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
|
||||
|
||||
// Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
|
||||
// Each invocation emits a different third.
|
||||
g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
|
||||
g->codeAppendf("%s(%s, 1);", emitVertexFn, midpoint);
|
||||
g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
|
||||
g->codeAppend ("if (any(dnotequal)) {");
|
||||
g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppend ("if (all(dnotequal)) {");
|
||||
g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppend ("EndPrimitive();");
|
||||
|
||||
return 5;
|
||||
}
|
||||
};
|
||||
|
||||
class GSEdgeImpl : public GrCCPRCoverageProcessor::GSImpl {
|
||||
public:
|
||||
GSEdgeImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||
|
||||
int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn, const char* rtAdjust,
|
||||
const Shader::GeometryVars&) const override {
|
||||
int numSides = fShader->getNumSegments();
|
||||
|
||||
g->codeAppendf("int nextidx = (sk_InvocationID + 1) %% %i;", numSides);
|
||||
g->codeAppendf("float2 left = pts[%s > 0 ? sk_InvocationID : nextidx], "
|
||||
"right = pts[%s > 0 ? nextidx : sk_InvocationID];",
|
||||
wind.c_str(), wind.c_str());
|
||||
|
||||
Shader::EmitEdgeDistanceEquation(g, "left", "right", "float3 edge_distance_equation");
|
||||
|
||||
// qlr is defined in EmitEdgeDistanceEquation. TODO: address in a followup CL!
|
||||
g->codeAppend ("float2x2 outer_pts = float2x2(left - bloat * qlr, right + bloat * qlr);");
|
||||
g->codeAppend ("half2 outer_coverage = edge_distance_equation.xy * outer_pts + "
|
||||
"edge_distance_equation.z;");
|
||||
|
||||
// d1 is defined in EmitEdgeDistanceEquation. TODO: address in a followup CL!
|
||||
g->codeAppend ("float2 d2 = d1;");
|
||||
g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
|
||||
g->codeAppend ("if (aligned) {");
|
||||
g->codeAppend ( "d1 -= qlr;");
|
||||
g->codeAppend ( "d2 += qlr;");
|
||||
g->codeAppend ("}");
|
||||
|
||||
// Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
|
||||
// invocation emits a different edge. Emit negative coverage that subtracts the appropiate
|
||||
// amount back out from the hull we drew above.
|
||||
g->codeAppend ("if (!aligned) {");
|
||||
g->codeAppendf( "%s(outer_pts[0], outer_coverage[0]);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppendf("%s(left + bloat * d1, -1);", emitVertexFn);
|
||||
g->codeAppendf("%s(left - bloat * d2, 0);", emitVertexFn);
|
||||
g->codeAppendf("%s(right + bloat * d2, -1);", emitVertexFn);
|
||||
g->codeAppendf("%s(right - bloat * d1, 0);", emitVertexFn);
|
||||
g->codeAppend ("if (!aligned) {");
|
||||
g->codeAppendf( "%s(outer_pts[1], outer_coverage[1]);", emitVertexFn);
|
||||
g->codeAppend ("}");
|
||||
g->codeAppend ("EndPrimitive();");
|
||||
|
||||
return 6;
|
||||
}
|
||||
};
|
||||
|
||||
class GSCornerImpl : public GrCCPRCoverageProcessor::GSImpl {
|
||||
public:
|
||||
GSCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||
|
||||
int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn, const char* rtAdjust,
|
||||
const Shader::GeometryVars& vars) const override {
|
||||
const char* corner = vars.fCornerVars.fPoint;
|
||||
SkASSERT(corner);
|
||||
|
||||
g->codeAppendf("%s(%s + float2(-bloat.x, -bloat.y), 1);", emitVertexFn, corner);
|
||||
g->codeAppendf("%s(%s + float2(-bloat.x, +bloat.y), 1);", emitVertexFn, corner);
|
||||
g->codeAppendf("%s(%s + float2(+bloat.x, -bloat.y), 1);", emitVertexFn, corner);
|
||||
g->codeAppendf("%s(%s + float2(+bloat.x, +bloat.y), 1);", emitVertexFn, corner);
|
||||
g->codeAppend ("EndPrimitive();");
|
||||
|
||||
return 4;
|
||||
}
|
||||
};
|
||||
|
||||
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::CreateGSImpl(std::unique_ptr<Shader> shader) {
|
||||
switch (shader->getGeometryType()) {
|
||||
case Shader::GeometryType::kHull:
|
||||
return new GSHullImpl(std::move(shader));
|
||||
case Shader::GeometryType::kEdges:
|
||||
return new GSEdgeImpl(std::move(shader));
|
||||
case Shader::GeometryType::kCorners:
|
||||
return new GSCornerImpl(std::move(shader));
|
||||
}
|
||||
SK_ABORT("Unexpected Shader::GeometryType.");
|
||||
return nullptr;
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRCubicProcessor.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexShaderBuilder.h"
|
||||
|
||||
void GrCCPRCubicProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLVertexBuilder* v,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* atlasOffset, const char* rtAdjust,
|
||||
GrGPArgs* gpArgs) const {
|
||||
v->codeAppend ("float2 self = ");
|
||||
v->appendTexelFetch(pointsBuffer,
|
||||
SkStringPrintf("%s.x + sk_VertexID", proc.instanceAttrib()).c_str());
|
||||
v->codeAppendf(".xy + %s;", atlasOffset);
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
|
||||
}
|
||||
|
||||
void GrCCPRCubicProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust,
|
||||
const char* outputWind) const {
|
||||
// We will define bezierpts in onEmitGeometryShader.
|
||||
g->codeAppend ("float area_times_2 = "
|
||||
"determinant(float3x3(1, bezierpts[0], "
|
||||
"1, bezierpts[2], "
|
||||
"0, bezierpts[3] - bezierpts[1]));");
|
||||
// Drop curves that are nearly flat. The KLM math becomes unstable in this case.
|
||||
g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[3] - bezierpts[0]) * %s.zx)) {",
|
||||
rtAdjust);
|
||||
#ifndef SK_BUILD_FOR_MAC
|
||||
g->codeAppend ( "return;");
|
||||
#else
|
||||
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
|
||||
g->codeAppend ( "area_times_2 = 0;");
|
||||
#endif
|
||||
g->codeAppend ("}");
|
||||
g->codeAppendf("%s = sign(area_times_2);", outputWind);
|
||||
}
|
||||
|
||||
void GrCCPRCubicProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
|
||||
const char* wind, const char* rtAdjust) const {
|
||||
// Prepend bezierpts at the start of the shader.
|
||||
g->codePrependf("float4x2 bezierpts = float4x2(sk_in[0].sk_Position.xy, "
|
||||
"sk_in[1].sk_Position.xy, "
|
||||
"sk_in[2].sk_Position.xy, "
|
||||
"sk_in[3].sk_Position.xy);");
|
||||
|
||||
// Evaluate the cubic at T=.5 for an mid-ish point.
|
||||
g->codeAppendf("float2 midpoint = bezierpts * float4(.125, .375, .375, .125);");
|
||||
|
||||
// Find the cubic's power basis coefficients.
|
||||
g->codeAppend ("float2x4 C = float4x4(-1, 3, -3, 1, "
|
||||
" 3, -6, 3, 0, "
|
||||
"-3, 3, 0, 0, "
|
||||
" 1, 0, 0, 0) * transpose(bezierpts);");
|
||||
|
||||
// Find the cubic's inflection function.
|
||||
g->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
|
||||
g->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
|
||||
g->codeAppend ("float D1 = +determinant(float2x2(C));");
|
||||
|
||||
// Calculate the KLM matrix.
|
||||
g->declareGlobal(fKLMMatrix);
|
||||
g->codeAppend ("float4 K, L, M;");
|
||||
g->codeAppend ("float2 l, m;");
|
||||
g->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
|
||||
if (CubicType::kSerpentine == fCubicType) {
|
||||
// This math also works out for the "cusp" and "cusp at infinity" cases.
|
||||
g->codeAppend ("float q = sqrt(max(3*discr, 0));");
|
||||
g->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
|
||||
g->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
|
||||
g->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
|
||||
g->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
|
||||
g->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
|
||||
g->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
|
||||
} else {
|
||||
g->codeAppend ("float q = sqrt(max(-discr, 0));");
|
||||
g->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
|
||||
g->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
|
||||
g->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
|
||||
g->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
|
||||
g->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
|
||||
g->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
|
||||
g->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
|
||||
}
|
||||
g->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
|
||||
g->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
|
||||
"C[1][0], C[1][middlerow], C[1][3], "
|
||||
" 0, 0, 1));");
|
||||
g->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
|
||||
"L[0], L[middlerow], L[3], "
|
||||
"M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
|
||||
|
||||
// Orient the KLM matrix so we fill the correct side of the curve.
|
||||
g->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
|
||||
fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
||||
g->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
|
||||
"0, orientation[0], 0, "
|
||||
"0, 0, orientation[1]);", fKLMMatrix.c_str());
|
||||
|
||||
g->declareGlobal(fKLMDerivatives);
|
||||
g->codeAppendf("%s[0] = %s[0].xy * %s.xz;",
|
||||
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
|
||||
g->codeAppendf("%s[1] = %s[1].xy * %s.xz;",
|
||||
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
|
||||
g->codeAppendf("%s[2] = %s[2].xy * %s.xz;",
|
||||
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
|
||||
|
||||
// Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
|
||||
g->declareGlobal(fEdgeDistanceEquation);
|
||||
g->codeAppendf("short edgeidx0 = %s > 0 ? 3 : 0;", wind);
|
||||
g->codeAppendf("float2 edgept0 = bezierpts[edgeidx0];");
|
||||
g->codeAppendf("float2 edgept1 = bezierpts[3 - edgeidx0];");
|
||||
this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
|
||||
|
||||
this->emitCubicGeometry(g, emitVertexFn, wind, rtAdjust);
|
||||
}
|
||||
|
||||
void GrCCPRCubicProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position,
|
||||
const char* /*coverage*/,
|
||||
const char* /*wind*/) const {
|
||||
fnBody->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
|
||||
fnBody->appendf("float d = dot(float3(%s, 1), %s);",
|
||||
position, fEdgeDistanceEquation.c_str());
|
||||
fnBody->appendf("%s = float4(klm, d);", fKLMD.gsOut());
|
||||
this->onEmitPerVertexGeometryCode(fnBody);
|
||||
}
|
||||
|
||||
void GrCCPRCubicHullProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
|
||||
const char* wind, const char* rtAdjust) const {
|
||||
// FIXME: we should clip this geometry at the tip of the curve.
|
||||
int maxVertices = this->emitHullGeometry(g, emitVertexFn, "bezierpts", 4, "sk_InvocationID",
|
||||
"midpoint");
|
||||
|
||||
g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency,
|
||||
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
|
||||
maxVertices, 4);
|
||||
}
|
||||
|
||||
void GrCCPRCubicHullProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
|
||||
// "klm" was just defined by the base class.
|
||||
fnBody->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str());
|
||||
fnBody->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
|
||||
fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str());
|
||||
}
|
||||
|
||||
void GrCCPRCubicHullProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* 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());
|
||||
f->codeAppend ("float f = k*k*k - l*m;");
|
||||
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
|
||||
f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage);
|
||||
f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat closing edge.
|
||||
}
|
||||
|
||||
void GrCCPRCubicCornerProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g,
|
||||
const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const {
|
||||
// We defined bezierpts in onEmitGeometryShader.
|
||||
g->declareGlobal(fEdgeDistanceDerivatives);
|
||||
g->codeAppendf("%s = %s.xy * %s.xz;",
|
||||
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
|
||||
|
||||
g->codeAppendf("float2 corner = bezierpts[sk_InvocationID * 3];");
|
||||
int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
|
||||
|
||||
g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency,
|
||||
GrGLSLGeometryBuilder::OutputType::kTriangleStrip, numVertices, 2);
|
||||
}
|
||||
|
||||
void GrCCPRCubicCornerProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
|
||||
fnBody->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
|
||||
fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
|
||||
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
|
||||
fnBody->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
|
||||
fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
|
||||
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
|
||||
|
||||
// Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z).
|
||||
GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
|
||||
}
|
||||
|
||||
void GrCCPRCubicCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* 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.
|
||||
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
|
||||
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
|
||||
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 = this->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 ("}");
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCPRCubicProcessor_DEFINED
|
||||
#define GrCCPRCubicProcessor_DEFINED
|
||||
|
||||
#include "ccpr/GrCCPRCoverageProcessor.h"
|
||||
|
||||
class GrGLSLGeometryBuilder;
|
||||
|
||||
/**
|
||||
* This class renders the coverage of convex closed cubic segments using the techniques outlined in
|
||||
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
|
||||
* Jim Blinn:
|
||||
*
|
||||
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
|
||||
*
|
||||
* The provided curves must be convex, monotonic with respect to the vector of their closing edge
|
||||
* [P3 - P0], and must not contain or be near any inflection points or loop intersections.
|
||||
* (Use GrCCPRGeometry.)
|
||||
*/
|
||||
class GrCCPRCubicProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
|
||||
public:
|
||||
enum class CubicType {
|
||||
kSerpentine,
|
||||
kLoop
|
||||
};
|
||||
|
||||
GrCCPRCubicProcessor(CubicType cubicType)
|
||||
: INHERITED(CoverageType::kShader)
|
||||
, fCubicType(cubicType)
|
||||
, fKLMMatrix("klm_matrix", kFloat3x3_GrSLType, GrShaderVar::kNonArray,
|
||||
kHigh_GrSLPrecision)
|
||||
, fKLMDerivatives("klm_derivatives", kFloat2_GrSLType, 3, kHigh_GrSLPrecision)
|
||||
, fEdgeDistanceEquation("edge_distance_equation", kFloat3_GrSLType,
|
||||
GrShaderVar::kNonArray, kHigh_GrSLPrecision)
|
||||
, fKLMD(kFloat4_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
varyingHandler->addVarying("klmd", &fKLMD, kHigh_GrSLPrecision);
|
||||
}
|
||||
|
||||
void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
|
||||
const char* rtAdjust, GrGPArgs*) const override;
|
||||
void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const final;
|
||||
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
|
||||
const char* wind) const final;
|
||||
|
||||
protected:
|
||||
virtual void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* wind, const char* rtAdjust) const = 0;
|
||||
virtual void onEmitPerVertexGeometryCode(SkString* fnBody) const = 0;
|
||||
|
||||
const CubicType fCubicType;
|
||||
GrShaderVar fKLMMatrix;
|
||||
GrShaderVar fKLMDerivatives;
|
||||
GrShaderVar fEdgeDistanceEquation;
|
||||
GrGLSLGeoToFrag fKLMD;
|
||||
|
||||
typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
|
||||
};
|
||||
|
||||
class GrCCPRCubicHullProcessor : public GrCCPRCubicProcessor {
|
||||
public:
|
||||
GrCCPRCubicHullProcessor(CubicType cubicType)
|
||||
: INHERITED(cubicType)
|
||||
, fGradMatrix(kFloat2x2_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
this->INHERITED::resetVaryings(varyingHandler);
|
||||
varyingHandler->addVarying("grad_matrix", &fGradMatrix, kHigh_GrSLPrecision);
|
||||
}
|
||||
|
||||
void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* wind, const char* rtAdjust) const override;
|
||||
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
|
||||
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
protected:
|
||||
GrGLSLGeoToFrag fGradMatrix;
|
||||
|
||||
typedef GrCCPRCubicProcessor INHERITED;
|
||||
};
|
||||
|
||||
class GrCCPRCubicCornerProcessor : public GrCCPRCubicProcessor {
|
||||
public:
|
||||
GrCCPRCubicCornerProcessor(CubicType cubicType)
|
||||
: INHERITED(cubicType)
|
||||
, fEdgeDistanceDerivatives("edge_distance_derivatives", kFloat2_GrSLType,
|
||||
GrShaderVar::kNonArray, kHigh_GrSLPrecision)
|
||||
, fdKLMDdx(kFloat4_GrSLType)
|
||||
, fdKLMDdy(kFloat4_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
this->INHERITED::resetVaryings(varyingHandler);
|
||||
varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx, kHigh_GrSLPrecision);
|
||||
varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy, kHigh_GrSLPrecision);
|
||||
}
|
||||
|
||||
void emitCubicGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* wind, const char* rtAdjust) const override;
|
||||
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
|
||||
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
protected:
|
||||
GrShaderVar fEdgeDistanceDerivatives;
|
||||
GrGLSLGeoToFrag fdKLMDdx;
|
||||
GrGLSLGeoToFrag fdKLMDdy;
|
||||
|
||||
typedef GrCCPRCubicProcessor INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
203
src/gpu/ccpr/GrCCPRCubicShader.cpp
Normal file
203
src/gpu/ccpr/GrCCPRCubicShader.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRCubicShader.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
|
||||
void GrCCPRCubicShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLShaderBuilder* s,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const {
|
||||
s->appendTexelFetch(pointsBuffer,
|
||||
SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
|
||||
}
|
||||
|
||||
void GrCCPRCubicShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* rtAdjust, const char* outputWind) const {
|
||||
|
||||
s->codeAppendf("float area_times_2 = determinant(float3x3(1, %s[0], "
|
||||
"1, %s[2], "
|
||||
"0, %s[3] - %s[1]));",
|
||||
pts, pts, pts, pts);
|
||||
// Drop curves that are nearly flat. The KLM math becomes unstable in this case.
|
||||
s->codeAppendf("if (2 * abs(area_times_2) < length((%s[3] - %s[0]) * %s.zx)) {",
|
||||
pts, pts, rtAdjust);
|
||||
#ifndef SK_BUILD_FOR_MAC
|
||||
s->codeAppend ( "return;");
|
||||
#else
|
||||
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
|
||||
s->codeAppend ( "area_times_2 = 0;");
|
||||
#endif
|
||||
s->codeAppend ("}");
|
||||
s->codeAppendf("%s = sign(area_times_2);", outputWind);
|
||||
}
|
||||
|
||||
void GrCCPRCubicShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* segmentId, const char* bloat, const char* wind,
|
||||
const char* rtAdjust, GeometryVars* vars) const {
|
||||
// Evaluate the cubic at T=.5 for an mid-ish point.
|
||||
s->codeAppendf("float2 midpoint = %s * float4(.125, .375, .375, .125);", pts);
|
||||
|
||||
// Find the cubic's power basis coefficients.
|
||||
s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
|
||||
" 3, -6, 3, 0, "
|
||||
"-3, 3, 0, 0, "
|
||||
" 1, 0, 0, 0) * transpose(%s);", pts);
|
||||
|
||||
// Find the cubic's inflection function.
|
||||
s->codeAppend ("float D3 = +determinant(float2x2(C[0].yz, C[1].yz));");
|
||||
s->codeAppend ("float D2 = -determinant(float2x2(C[0].xz, C[1].xz));");
|
||||
s->codeAppend ("float D1 = +determinant(float2x2(C));");
|
||||
|
||||
// Calculate the KLM matrix.
|
||||
s->declareGlobal(fKLMMatrix);
|
||||
s->codeAppend ("float4 K, L, M;");
|
||||
s->codeAppend ("float2 l, m;");
|
||||
s->codeAppend ("float discr = 3*D2*D2 - 4*D1*D3;");
|
||||
if (CubicType::kSerpentine == fCubicType) {
|
||||
// This math also works out for the "cusp" and "cusp at infinity" cases.
|
||||
s->codeAppend ("float q = sqrt(max(3*discr, 0));");
|
||||
s->codeAppend ("q = 3*D2 + (D2 >= 0 ? q : -q);");
|
||||
s->codeAppend ("l.ts = normalize(float2(q, 6*D1));");
|
||||
s->codeAppend ("m.ts = discr <= 0 ? l.ts : normalize(float2(2*D3, q));");
|
||||
s->codeAppend ("K = float4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);");
|
||||
s->codeAppend ("L = float4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;");
|
||||
s->codeAppend ("M = float4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;");
|
||||
} else {
|
||||
s->codeAppend ("float q = sqrt(max(-discr, 0));");
|
||||
s->codeAppend ("q = D2 + (D2 >= 0 ? q : -q);");
|
||||
s->codeAppend ("l.ts = normalize(float2(q, 2*D1));");
|
||||
s->codeAppend ("m.ts = discr >= 0 ? l.ts : normalize(float2(2 * (D2*D2 - D3*D1), D1*q));");
|
||||
s->codeAppend ("float4 lxm = float4(l.s * m.s, l.s * m.t, l.t * m.s, l.t * m.t);");
|
||||
s->codeAppend ("K = float4(0, lxm.x, -lxm.y - lxm.z, lxm.w);");
|
||||
s->codeAppend ("L = float4(-1,1,-1,1) * l.sstt * (lxm.xyzw + float4(0, 2*lxm.zy, 0));");
|
||||
s->codeAppend ("M = float4(-1,1,-1,1) * m.sstt * (lxm.xzyw + float4(0, 2*lxm.yz, 0));");
|
||||
}
|
||||
s->codeAppend ("short middlerow = abs(D2) > abs(D1) ? 2 : 1;");
|
||||
s->codeAppend ("float3x3 CI = inverse(float3x3(C[0][0], C[0][middlerow], C[0][3], "
|
||||
"C[1][0], C[1][middlerow], C[1][3], "
|
||||
" 0, 0, 1));");
|
||||
s->codeAppendf("%s = CI * float3x3(K[0], K[middlerow], K[3], "
|
||||
"L[0], L[middlerow], L[3], "
|
||||
"M[0], M[middlerow], M[3]);", fKLMMatrix.c_str());
|
||||
|
||||
// Orient the KLM matrix so we fill the correct side of the curve.
|
||||
s->codeAppendf("float2 orientation = sign(float3(midpoint, 1) * float2x3(%s[1], %s[2]));",
|
||||
fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
||||
s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
|
||||
"0, orientation[0], 0, "
|
||||
"0, 0, orientation[1]);", fKLMMatrix.c_str());
|
||||
|
||||
s->declareGlobal(fKLMDerivatives);
|
||||
s->codeAppendf("%s[0] = %s[0].xy * %s.xz;",
|
||||
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
|
||||
s->codeAppendf("%s[1] = %s[1].xy * %s.xz;",
|
||||
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
|
||||
s->codeAppendf("%s[2] = %s[2].xy * %s.xz;",
|
||||
fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust);
|
||||
|
||||
// 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, segmentId, rtAdjust, vars);
|
||||
}
|
||||
|
||||
GrCCPRCubicShader::WindHandling
|
||||
GrCCPRCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
|
||||
const char* position, const char* /*coverage*/,
|
||||
const char* /*wind*/) {
|
||||
varyingHandler->addVarying("klmd", &fKLMD, kHigh_GrSLPrecision);
|
||||
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);", fKLMD.gsOut());
|
||||
|
||||
this->onEmitVaryings(varyingHandler, code);
|
||||
return WindHandling::kNotHandled;
|
||||
}
|
||||
|
||||
void GrCCPRCubicHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* /*pts*/,
|
||||
const char* /*wedgeId*/, const char* /*rtAdjust*/,
|
||||
GeometryVars* vars) const {
|
||||
// "midpoint" was just defined by the base class.
|
||||
vars->fHullVars.fAlternateMidpoint = "midpoint";
|
||||
}
|
||||
|
||||
void GrCCPRCubicHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code) {
|
||||
// "klm" was just defined by the base class.
|
||||
varyingHandler->addVarying("grad_matrix", &fGradMatrix, kHigh_GrSLPrecision);
|
||||
code->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str());
|
||||
code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
|
||||
fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str());
|
||||
}
|
||||
|
||||
void GrCCPRCubicHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* 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());
|
||||
f->codeAppend ("float f = k*k*k - l*m;");
|
||||
f->codeAppendf("float2 grad_f = %s * float2(k, 1);", fGradMatrix.fsIn());
|
||||
f->codeAppendf("%s = clamp(0.5 - f * inversesqrt(dot(grad_f, grad_f)), 0, 1);", outputCoverage);
|
||||
f->codeAppendf("%s += min(d, 0);", outputCoverage); // Flat closing edge.
|
||||
}
|
||||
|
||||
void GrCCPRCubicCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* cornerId, const char* rtAdjust,
|
||||
GeometryVars* vars) const {
|
||||
s->declareGlobal(fEdgeDistanceDerivatives);
|
||||
s->codeAppendf("%s = %s.xy * %s.xz;",
|
||||
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
|
||||
|
||||
s->codeAppendf("float2 corner = %s[%s * 3];", pts, cornerId);
|
||||
vars->fCornerVars.fPoint = "corner";
|
||||
}
|
||||
|
||||
void GrCCPRCubicCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code) {
|
||||
varyingHandler->addFlatVarying("dklmddx", &fdKLMDdx, kHigh_GrSLPrecision);
|
||||
code->appendf("%s = float4(%s[0].x, %s[1].x, %s[2].x, %s.x);",
|
||||
fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
|
||||
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
|
||||
|
||||
varyingHandler->addFlatVarying("dklmddy", &fdKLMDdy, kHigh_GrSLPrecision);
|
||||
code->appendf("%s = float4(%s[0].y, %s[1].y, %s[2].y, %s.y);",
|
||||
fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(),
|
||||
fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str());
|
||||
|
||||
// Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z).
|
||||
GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);
|
||||
}
|
||||
|
||||
void GrCCPRCubicCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* 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.
|
||||
f->codeAppendf("float k = %s.x, l = %s.y, m = %s.z, d = %s.w;",
|
||||
fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn(), fKLMD.fsIn());
|
||||
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 ("}");
|
||||
}
|
94
src/gpu/ccpr/GrCCPRCubicShader.h
Normal file
94
src/gpu/ccpr/GrCCPRCubicShader.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCPRCubicShader_DEFINED
|
||||
#define GrCCPRCubicShader_DEFINED
|
||||
|
||||
#include "ccpr/GrCCPRCoverageProcessor.h"
|
||||
|
||||
/**
|
||||
* This class renders the coverage of convex closed cubic segments using the techniques outlined in
|
||||
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
|
||||
* Jim Blinn:
|
||||
*
|
||||
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
|
||||
*
|
||||
* The provided curve segments must be convex, monotonic with respect to the vector of their closing
|
||||
* edge [P3 - P0], and must not contain or be near any inflection points or loop intersections.
|
||||
* (Use GrCCPRGeometry.)
|
||||
*/
|
||||
class GrCCPRCubicShader : public GrCCPRCoverageProcessor::Shader {
|
||||
public:
|
||||
enum class CubicType {
|
||||
kSerpentine,
|
||||
kLoop
|
||||
};
|
||||
|
||||
protected:
|
||||
GrCCPRCubicShader(CubicType cubicType) : fCubicType(cubicType) {}
|
||||
|
||||
int getNumInputPoints() const final { return 4; }
|
||||
|
||||
void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const final;
|
||||
|
||||
void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
|
||||
const char* outputWind) const final;
|
||||
|
||||
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
|
||||
const char* bloat, const char* wind, const char* rtAdjust,
|
||||
GeometryVars*) const final;
|
||||
|
||||
virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
|
||||
const char* rtAdjust, GeometryVars*) const = 0;
|
||||
|
||||
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
|
||||
const char* coverage, const char* wind) final;
|
||||
|
||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
|
||||
|
||||
const CubicType fCubicType;
|
||||
GrShaderVar fKLMMatrix{"klm_matrix", kFloat3x3_GrSLType};
|
||||
GrShaderVar fKLMDerivatives{"klm_derivatives", kFloat2_GrSLType, 3};
|
||||
GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
|
||||
GrGLSLGeoToFrag fKLMD{kFloat4_GrSLType};
|
||||
};
|
||||
|
||||
class GrCCPRCubicHullShader : public GrCCPRCubicShader {
|
||||
public:
|
||||
GrCCPRCubicHullShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
|
||||
|
||||
private:
|
||||
GeometryType getGeometryType() const override { return GeometryType::kHull; }
|
||||
int getNumSegments() const override { return 4; } // 4 wedges.
|
||||
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
|
||||
const char* rtAdjust, GeometryVars*) const override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
GrGLSLGeoToFrag fGradMatrix{kFloat2x2_GrSLType};
|
||||
};
|
||||
|
||||
class GrCCPRCubicCornerShader : public GrCCPRCubicShader {
|
||||
public:
|
||||
GrCCPRCubicCornerShader(CubicType cubicType) : GrCCPRCubicShader(cubicType) {}
|
||||
|
||||
private:
|
||||
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
|
||||
int getNumSegments() const override { return 2; } // 2 corners.
|
||||
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
|
||||
const char* rtAdjust, GeometryVars*) const override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
GrShaderVar fEdgeDistanceDerivatives{"edge_distance_derivatives", kFloat2_GrSLType};
|
||||
GrGLSLGeoToFrag fdKLMDdx{kFloat4_GrSLType};
|
||||
GrGLSLGeoToFrag fdKLMDdy{kFloat4_GrSLType};
|
||||
};
|
||||
|
||||
#endif
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRQuadraticProcessor.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexShaderBuilder.h"
|
||||
|
||||
void GrCCPRQuadraticProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLVertexBuilder* v,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* atlasOffset, const char* rtAdjust,
|
||||
GrGPArgs* gpArgs) const {
|
||||
v->codeAppend ("float2 self = ");
|
||||
v->appendTexelFetch(pointsBuffer,
|
||||
SkStringPrintf("%s.x + sk_VertexID", proc.instanceAttrib()).c_str());
|
||||
v->codeAppendf(".xy + %s;", atlasOffset);
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust,
|
||||
const char* outputWind) const {
|
||||
// We will define bezierpts in onEmitGeometryShader.
|
||||
g->codeAppend ("float area_times_2 = determinant(float2x2(bezierpts[1] - bezierpts[0], "
|
||||
"bezierpts[2] - bezierpts[0]));");
|
||||
// Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
|
||||
g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[2] - bezierpts[0]) * %s.zx)) {",
|
||||
rtAdjust);
|
||||
#ifndef SK_BUILD_FOR_MAC
|
||||
g->codeAppend ( "return;");
|
||||
#else
|
||||
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
|
||||
g->codeAppend ( "area_times_2 = 0;");
|
||||
#endif
|
||||
g->codeAppend ("}");
|
||||
g->codeAppendf("%s = sign(area_times_2);", outputWind);
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
|
||||
const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const {
|
||||
// Prepend bezierpts at the start of the shader.
|
||||
g->codePrependf("float3x2 bezierpts = float3x2(sk_in[0].sk_Position.xy, "
|
||||
"sk_in[1].sk_Position.xy, "
|
||||
"sk_in[2].sk_Position.xy);");
|
||||
|
||||
g->declareGlobal(fCanonicalMatrix);
|
||||
g->codeAppendf("%s = float3x3(0.0, 0, 1, "
|
||||
"0.5, 0, 1, "
|
||||
"1.0, 1, 1) * "
|
||||
"inverse(float3x3(bezierpts[0], 1, "
|
||||
"bezierpts[1], 1, "
|
||||
"bezierpts[2], 1));",
|
||||
fCanonicalMatrix.c_str());
|
||||
|
||||
g->declareGlobal(fCanonicalDerivatives);
|
||||
g->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
|
||||
fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
|
||||
|
||||
g->declareGlobal(fEdgeDistanceEquation);
|
||||
g->codeAppendf("float2 edgept0 = bezierpts[%s > 0 ? 2 : 0];", wind);
|
||||
g->codeAppendf("float2 edgept1 = bezierpts[%s > 0 ? 0 : 2];", wind);
|
||||
this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
|
||||
|
||||
this->emitQuadraticGeometry(g, emitVertexFn, rtAdjust);
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position,
|
||||
const char* /*coverage*/,
|
||||
const char* /*wind*/) const {
|
||||
fnBody->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
|
||||
fXYD.gsOut(), fCanonicalMatrix.c_str(), position);
|
||||
fnBody->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
|
||||
fXYD.gsOut(), fEdgeDistanceEquation.c_str(), position,
|
||||
fEdgeDistanceEquation.c_str());
|
||||
this->onEmitPerVertexGeometryCode(fnBody);
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticHullProcessor::emitQuadraticGeometry(GrGLSLGeometryBuilder* g,
|
||||
const char* emitVertexFn,
|
||||
const char* /*rtAdjust*/) const {
|
||||
// Find the t value whose tangent is halfway between the tangents at the endpionts.
|
||||
// (We defined bezierpts in onEmitGeometryShader.)
|
||||
g->codeAppend ("float2 tan0 = bezierpts[1] - bezierpts[0];");
|
||||
g->codeAppend ("float2 tan1 = bezierpts[2] - bezierpts[1];");
|
||||
g->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
|
||||
g->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
|
||||
g->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s=0 is weeded out by this point.
|
||||
|
||||
// Clip the bezier triangle by the tangent at our new t value. This is a simple application for
|
||||
// De Casteljau's algorithm.
|
||||
g->codeAppendf("float4x2 quadratic_hull = float4x2(bezierpts[0], "
|
||||
"bezierpts[0] + tan0 * t, "
|
||||
"bezierpts[1] + tan1 * t, "
|
||||
"bezierpts[2]);");
|
||||
|
||||
int maxVerts = this->emitHullGeometry(g, emitVertexFn, "quadratic_hull", 4, "sk_InvocationID");
|
||||
|
||||
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
|
||||
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
|
||||
maxVerts, 4);
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticHullProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
|
||||
fnBody->appendf("%s = float2(2 * %s.x, -1) * %s;",
|
||||
fGradXY.gsOut(), fXYD.gsOut(), fCanonicalDerivatives.c_str());
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticHullProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
|
||||
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn(), fGradXY.fsIn(), fGradXY.fsIn());
|
||||
f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
|
||||
f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYD.fsIn()); // Flat closing edge.
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticCornerProcessor::emitQuadraticGeometry(GrGLSLGeometryBuilder* g,
|
||||
const char* emitVertexFn,
|
||||
const char* rtAdjust) const {
|
||||
g->declareGlobal(fEdgeDistanceDerivatives);
|
||||
g->codeAppendf("%s = %s.xy * %s.xz;",
|
||||
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
|
||||
|
||||
g->codeAppendf("float2 corner = bezierpts[sk_InvocationID * 2];");
|
||||
int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
|
||||
|
||||
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
|
||||
GrGLSLGeometryBuilder::OutputType::kTriangleStrip, numVertices, 2);
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticCornerProcessor::onEmitPerVertexGeometryCode(SkString* fnBody) const {
|
||||
fnBody->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
|
||||
fdXYDdx.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
|
||||
fEdgeDistanceDerivatives.c_str());
|
||||
fnBody->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
|
||||
fdXYDdy.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
|
||||
fEdgeDistanceDerivatives.c_str());
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
|
||||
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn());
|
||||
f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn());
|
||||
|
||||
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
|
||||
// the same pixel because those cases should have been weeded out by this point.
|
||||
f->codeAppend ("float f = x*x - y;");
|
||||
f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);");
|
||||
f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage);
|
||||
f->codeAppendf("%s -= d;", outputCoverage);
|
||||
|
||||
// Use software msaa to approximate coverage at the corner pixels.
|
||||
int sampleCount = this->defineSoftSampleLocations(f, "samples");
|
||||
f->codeAppendf("float3 xyd_center = float3(%s.xy, %s.z + 0.5);",
|
||||
fXYD.fsIn(), fXYD.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("}");
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCPRQuadraticProcessor_DEFINED
|
||||
#define GrCCPRQuadraticProcessor_DEFINED
|
||||
|
||||
#include "ccpr/GrCCPRCoverageProcessor.h"
|
||||
|
||||
/**
|
||||
* This class renders the coverage of closed quadratic curves using the techniques outlined in
|
||||
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
|
||||
* Jim Blinn:
|
||||
*
|
||||
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
|
||||
*
|
||||
* The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
|
||||
* (Use GrCCPRGeometry.)
|
||||
*/
|
||||
class GrCCPRQuadraticProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
|
||||
public:
|
||||
GrCCPRQuadraticProcessor()
|
||||
: INHERITED(CoverageType::kShader)
|
||||
, fCanonicalMatrix("canonical_matrix", kFloat3x3_GrSLType, GrShaderVar::kNonArray)
|
||||
, fCanonicalDerivatives("canonical_derivatives", kFloat2x2_GrSLType,
|
||||
GrShaderVar::kNonArray)
|
||||
, fEdgeDistanceEquation("edge_distance_equation", kFloat3_GrSLType,
|
||||
GrShaderVar::kNonArray)
|
||||
, fXYD(kFloat3_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
varyingHandler->addVarying("xyd", &fXYD, kHigh_GrSLPrecision);
|
||||
}
|
||||
|
||||
void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
|
||||
const char* rtAdjust, GrGPArgs*) const override;
|
||||
void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const final;
|
||||
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
|
||||
const char* wind) const final;
|
||||
|
||||
protected:
|
||||
virtual void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* rtAdjust) const = 0;
|
||||
virtual void onEmitPerVertexGeometryCode(SkString* fnBody) const = 0;
|
||||
|
||||
GrShaderVar fCanonicalMatrix;
|
||||
GrShaderVar fCanonicalDerivatives;
|
||||
GrShaderVar fEdgeDistanceEquation;
|
||||
GrGLSLGeoToFrag fXYD;
|
||||
|
||||
typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 GrCCPRQuadraticHullProcessor : public GrCCPRQuadraticProcessor {
|
||||
public:
|
||||
GrCCPRQuadraticHullProcessor()
|
||||
: fGradXY(kFloat2_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
this->INHERITED::resetVaryings(varyingHandler);
|
||||
varyingHandler->addVarying("grad_xy", &fGradXY, kHigh_GrSLPrecision);
|
||||
}
|
||||
|
||||
void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* rtAdjust) const override;
|
||||
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
|
||||
void emitShaderCoverage(GrGLSLFragmentBuilder* f, const char* outputCoverage) const override;
|
||||
|
||||
private:
|
||||
GrGLSLGeoToFrag fGradXY;
|
||||
|
||||
typedef GrCCPRQuadraticProcessor INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* This pass fixes the corners of a closed quadratic segment with soft MSAA.
|
||||
*/
|
||||
class GrCCPRQuadraticCornerProcessor : public GrCCPRQuadraticProcessor {
|
||||
public:
|
||||
GrCCPRQuadraticCornerProcessor()
|
||||
: fEdgeDistanceDerivatives("edge_distance_derivatives", kFloat2_GrSLType,
|
||||
GrShaderVar::kNonArray)
|
||||
, fdXYDdx(kFloat3_GrSLType)
|
||||
, fdXYDdy(kFloat3_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
this->INHERITED::resetVaryings(varyingHandler);
|
||||
varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx, kHigh_GrSLPrecision);
|
||||
varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy, kHigh_GrSLPrecision);
|
||||
}
|
||||
|
||||
void emitQuadraticGeometry(GrGLSLGeometryBuilder*, const char* emitVertexFn,
|
||||
const char* rtAdjust) const override;
|
||||
void onEmitPerVertexGeometryCode(SkString* fnBody) const override;
|
||||
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
private:
|
||||
GrShaderVar fEdgeDistanceDerivatives;
|
||||
GrGLSLGeoToFrag fdXYDdx;
|
||||
GrGLSLGeoToFrag fdXYDdy;
|
||||
|
||||
typedef GrCCPRQuadraticProcessor INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
157
src/gpu/ccpr/GrCCPRQuadraticShader.cpp
Normal file
157
src/gpu/ccpr/GrCCPRQuadraticShader.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRQuadraticShader.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
|
||||
void GrCCPRQuadraticShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLShaderBuilder* s,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const {
|
||||
s->appendTexelFetch(pointsBuffer,
|
||||
SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
|
||||
const char* outputWind) const {
|
||||
s->codeAppendf("float area_times_2 = determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0]));",
|
||||
pts, pts, pts, pts);
|
||||
// Drop curves that are nearly flat, in favor of the higher quality triangle antialiasing.
|
||||
s->codeAppendf("if (2 * abs(area_times_2) < length((%s[2] - %s[0]) * %s.zx)) {",
|
||||
pts, pts, rtAdjust);
|
||||
#ifndef SK_BUILD_FOR_MAC
|
||||
s->codeAppend ( "return;");
|
||||
#else
|
||||
// Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0.
|
||||
s->codeAppend ( "area_times_2 = 0;");
|
||||
#endif
|
||||
s->codeAppend ("}");
|
||||
s->codeAppendf("%s = sign(area_times_2);", outputWind);
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* segmentId, const char* bloat,
|
||||
const char* wind, const char* rtAdjust,
|
||||
GeometryVars* vars) const {
|
||||
s->declareGlobal(fCanonicalMatrix);
|
||||
s->codeAppendf("%s = float3x3(0.0, 0, 1, "
|
||||
"0.5, 0, 1, "
|
||||
"1.0, 1, 1) * "
|
||||
"inverse(float3x3(%s[0], 1, "
|
||||
"%s[1], 1, "
|
||||
"%s[2], 1));",
|
||||
fCanonicalMatrix.c_str(), pts, pts, pts);
|
||||
|
||||
s->declareGlobal(fCanonicalDerivatives);
|
||||
s->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
|
||||
fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
|
||||
|
||||
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, segmentId, rtAdjust, vars);
|
||||
}
|
||||
|
||||
GrCCPRQuadraticShader::WindHandling
|
||||
GrCCPRQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
|
||||
const char* position, const char* /*coverage*/,
|
||||
const char* /*wind*/) {
|
||||
varyingHandler->addVarying("xyd", &fXYD, kHigh_GrSLPrecision);
|
||||
code->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
|
||||
fXYD.gsOut(), fCanonicalMatrix.c_str(), position);
|
||||
code->appendf("%s.z = dot(%s.xy, %s) + %s.z;",
|
||||
fXYD.gsOut(), fEdgeDistanceEquation.c_str(), position,
|
||||
fEdgeDistanceEquation.c_str());
|
||||
|
||||
this->onEmitVaryings(varyingHandler, code);
|
||||
return WindHandling::kNotHandled;
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* /*wedgeId*/, const char* /*rtAdjust*/,
|
||||
GeometryVars* vars) const {
|
||||
// Find the T value whose tangent is halfway between the tangents at the endpionts.
|
||||
s->codeAppendf("float2 tan0 = %s[1] - %s[0];", pts, pts);
|
||||
s->codeAppendf("float2 tan1 = %s[2] - %s[1];", pts, pts);
|
||||
s->codeAppend ("float2 midnorm = normalize(tan0) - normalize(tan1);");
|
||||
s->codeAppend ("float2 T = midnorm * float2x2(tan0 - tan1, tan0);");
|
||||
s->codeAppend ("float t = clamp(T.t / T.s, 0, 1);"); // T.s != 0; we cull flat curves on CPU.
|
||||
|
||||
// Clip the bezier triangle by the tangent at our new t value. This is a simple application for
|
||||
// De Casteljau's algorithm.
|
||||
s->codeAppendf("float4x2 quadratic_hull = float4x2(%s[0], "
|
||||
"%s[0] + tan0 * t, "
|
||||
"%s[1] + tan1 * t, "
|
||||
"%s[2]);", pts, pts, pts, pts);
|
||||
vars->fHullVars.fAlternatePoints = "quadratic_hull";
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticHullShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
SkString* code) {
|
||||
varyingHandler->addVarying("grad", &fGrad, kHigh_GrSLPrecision);
|
||||
code->appendf("%s = float2(2 * %s.x, -1) * %s;",
|
||||
fGrad.gsOut(), fXYD.gsOut(), fCanonicalDerivatives.c_str());
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("float d = (%s.x * %s.x - %s.y) * inversesqrt(dot(%s, %s));",
|
||||
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn(), fGrad.fsIn(), fGrad.fsIn());
|
||||
f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
|
||||
f->codeAppendf("%s += min(%s.z, 0);", outputCoverage, fXYD.fsIn()); // Flat closing edge.
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* cornerId, const char* rtAdjust,
|
||||
GeometryVars* vars) const {
|
||||
s->declareGlobal(fEdgeDistanceDerivatives);
|
||||
s->codeAppendf("%s = %s.xy * %s.xz;",
|
||||
fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
|
||||
|
||||
s->codeAppendf("float2 corner = %s[%s * 2];", pts, cornerId);
|
||||
vars->fCornerVars.fPoint = "corner";
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
SkString* code) {
|
||||
varyingHandler->addFlatVarying("dXYDdx", &fdXYDdx, kHigh_GrSLPrecision);
|
||||
code->appendf("%s = float3(%s[0].x, %s[0].y, %s.x);",
|
||||
fdXYDdx.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
|
||||
fEdgeDistanceDerivatives.c_str());
|
||||
|
||||
varyingHandler->addFlatVarying("dXYDdy", &fdXYDdy, kHigh_GrSLPrecision);
|
||||
code->appendf("%s = float3(%s[1].x, %s[1].y, %s.y);",
|
||||
fdXYDdy.gsOut(), fCanonicalDerivatives.c_str(), fCanonicalDerivatives.c_str(),
|
||||
fEdgeDistanceDerivatives.c_str());
|
||||
}
|
||||
|
||||
void GrCCPRQuadraticCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("float x = %s.x, y = %s.y, d = %s.z;",
|
||||
fXYD.fsIn(), fXYD.fsIn(), fXYD.fsIn());
|
||||
f->codeAppendf("float2x3 grad_xyd = float2x3(%s, %s);", fdXYDdx.fsIn(), fdXYDdy.fsIn());
|
||||
|
||||
// Erase what the previous hull shader wrote. We don't worry about the two corners falling on
|
||||
// the same pixel because those cases should have been weeded out by this point.
|
||||
f->codeAppend ("float f = x*x - y;");
|
||||
f->codeAppend ("float2 grad_f = float2(2*x, -1) * float2x2(grad_xyd);");
|
||||
f->codeAppendf("%s = -(0.5 - f * inversesqrt(dot(grad_f, grad_f)));", outputCoverage);
|
||||
f->codeAppendf("%s -= d;", outputCoverage);
|
||||
|
||||
// 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);", fXYD.fsIn(), fXYD.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("}");
|
||||
}
|
87
src/gpu/ccpr/GrCCPRQuadraticShader.h
Normal file
87
src/gpu/ccpr/GrCCPRQuadraticShader.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCPRQuadraticShader_DEFINED
|
||||
#define GrCCPRQuadraticShader_DEFINED
|
||||
|
||||
#include "ccpr/GrCCPRCoverageProcessor.h"
|
||||
|
||||
/**
|
||||
* This class renders the coverage of closed quadratic curves using the techniques outlined in
|
||||
* "Resolution Independent Curve Rendering using Programmable Graphics Hardware" by Charles Loop and
|
||||
* Jim Blinn:
|
||||
*
|
||||
* https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
|
||||
*
|
||||
* The provided curves must be monotonic with respect to the vector of their closing edge [P2 - P0].
|
||||
* (Use GrCCPRGeometry.)
|
||||
*/
|
||||
class GrCCPRQuadraticShader : public GrCCPRCoverageProcessor::Shader {
|
||||
protected:
|
||||
int getNumInputPoints() const final { return 3; }
|
||||
|
||||
void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const override;
|
||||
|
||||
void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
|
||||
const char* outputWind) const final;
|
||||
|
||||
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
|
||||
const char* bloat, const char* wind, const char* rtAdjust,
|
||||
GeometryVars*) const final;
|
||||
|
||||
virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
|
||||
const char* rtAdjust, GeometryVars*) const = 0;
|
||||
|
||||
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
|
||||
const char* coverage, const char* wind) final;
|
||||
|
||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) = 0;
|
||||
|
||||
const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType};
|
||||
const GrShaderVar fCanonicalDerivatives{"canonical_derivatives", kFloat2x2_GrSLType};
|
||||
const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation", kFloat3_GrSLType};
|
||||
GrGLSLGeoToFrag fXYD{kFloat3_GrSLType};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 GrCCPRQuadraticHullShader : public GrCCPRQuadraticShader {
|
||||
int getNumSegments() const final { return 4; } // 4 wedges.
|
||||
|
||||
GeometryType getGeometryType() const override { return GeometryType::kHull; }
|
||||
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
|
||||
const char* rtAdjust, GeometryVars*) const override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
GrGLSLGeoToFrag fGrad{kFloat2_GrSLType};
|
||||
};
|
||||
|
||||
/**
|
||||
* This pass fixes the corners of a closed quadratic segment with soft MSAA.
|
||||
*/
|
||||
class GrCCPRQuadraticCornerShader : public GrCCPRQuadraticShader {
|
||||
int getNumSegments() const final { return 2; } // 2 corners.
|
||||
|
||||
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
|
||||
void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
|
||||
const char* rtAdjust, GeometryVars*) const override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
const GrShaderVar fEdgeDistanceDerivatives{"edge_distance_derivatives", kFloat2_GrSLType};
|
||||
GrGLSLGeoToFrag fdXYDdx{kFloat3_GrSLType};
|
||||
GrGLSLGeoToFrag fdXYDdy{kFloat3_GrSLType};
|
||||
};
|
||||
|
||||
#endif
|
@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRTriangleProcessor.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexShaderBuilder.h"
|
||||
|
||||
void GrCCPRTriangleProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLVertexBuilder* v,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* atlasOffset, const char* rtAdjust,
|
||||
GrGPArgs* gpArgs) const {
|
||||
// Copy the input attrib to an intermediate array. The Intel GLSL compiler hits an internal
|
||||
// assertion if we index the input attrib itself with sk_VertexID.
|
||||
v->codeAppendf("int indices[3] = int[3](%s.x, %s.y, %s.z);",
|
||||
proc.instanceAttrib(), proc.instanceAttrib(), proc.instanceAttrib());
|
||||
v->codeAppend ("float2 self = ");
|
||||
v->appendTexelFetch(pointsBuffer, "indices[sk_VertexID]");
|
||||
v->codeAppendf(".xy + %s;", atlasOffset);
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
|
||||
}
|
||||
|
||||
void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const {
|
||||
// Prepend in_vertices at the start of the shader.
|
||||
g->codePrependf("float3x2 in_vertices = float3x2(sk_in[0].sk_Position.xy, "
|
||||
"sk_in[1].sk_Position.xy, "
|
||||
"sk_in[2].sk_Position.xy);");
|
||||
}
|
||||
|
||||
void GrCCPRTriangleProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* /*rtAdjust*/,
|
||||
const char* outputWind) const {
|
||||
// We will define in_vertices in defineInputVertices.
|
||||
g->codeAppendf("%s = sign(determinant(float2x2(in_vertices[1] - in_vertices[0], "
|
||||
"in_vertices[2] - in_vertices[0])));",
|
||||
outputWind);
|
||||
}
|
||||
|
||||
void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
|
||||
const char* emitVertexFn,
|
||||
const char* wind,
|
||||
const char* rtAdjust) const {
|
||||
this->defineInputVertices(g);
|
||||
int maxOutputVertices = 0;
|
||||
|
||||
if (GeometryType::kEdges != fGeometryType) {
|
||||
maxOutputVertices += this->emitHullGeometry(g, emitVertexFn, "in_vertices", 3,
|
||||
"sk_InvocationID");
|
||||
}
|
||||
|
||||
if (GeometryType::kHulls != fGeometryType) {
|
||||
g->codeAppend ("int edgeidx0 = sk_InvocationID, "
|
||||
"edgeidx1 = (edgeidx0 + 1) % 3;");
|
||||
g->codeAppendf("float2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind);
|
||||
g->codeAppendf("float2 edgept1 = in_vertices[%s > 0 ? edgeidx1 : edgeidx0];", wind);
|
||||
|
||||
maxOutputVertices += this->emitEdgeGeometry(g, emitVertexFn, "edgept0", "edgept1");
|
||||
}
|
||||
|
||||
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
|
||||
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
|
||||
maxOutputVertices, 3);
|
||||
}
|
||||
|
||||
void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
|
||||
const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const {
|
||||
this->defineInputVertices(g);
|
||||
|
||||
g->codeAppend ("float2 corner = in_vertices[sk_InvocationID];");
|
||||
g->codeAppend ("float2x2 vectors = float2x2(corner - in_vertices[(sk_InvocationID + 2) % 3], "
|
||||
"corner - in_vertices[(sk_InvocationID + 1) % 3]);");
|
||||
|
||||
// Make sure neither vector is 0 in order to avoid a divide-by-zero. Wind will be zero anyway if
|
||||
// this is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
|
||||
g->codeAppendf("for (int i = 0; i < 2; ++i) {");
|
||||
g->codeAppendf( "vectors[i] = any(notEqual(vectors[i], float2(0))) ? "
|
||||
"vectors[i] : float2(1);");
|
||||
g->codeAppendf("}");
|
||||
|
||||
// Find the vector that bisects the region outside the incoming edges. Each edge is responsible
|
||||
// to subtract the outside region on its own the side of the bisector.
|
||||
g->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
|
||||
g->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
|
||||
g->codeAppendf("float2 bisect = dot(leftdir, rightdir) >= 0 ? leftdir + rightdir : "
|
||||
"float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
|
||||
|
||||
// In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge method
|
||||
// actually finds is coverage inside a logical "AA box", one that is rotated inline with the
|
||||
// edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set up
|
||||
// transformations into normalized logical AA box space for both incoming edges. These will tell
|
||||
// the fragment shader where the corner is located within each edge's AA box.
|
||||
g->declareGlobal(fAABoxMatrices);
|
||||
g->declareGlobal(fAABoxTranslates);
|
||||
g->declareGlobal(fGeoShaderBisects);
|
||||
g->codeAppendf("for (int i = 0; i < 2; ++i) {");
|
||||
// The X component runs parallel to the edge (i.e. distance to the corner).
|
||||
g->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
|
||||
g->codeAppendf( "float nwidth = dot(abs(n), bloat) * 2;");
|
||||
g->codeAppendf( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
|
||||
g->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
|
||||
g->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
|
||||
|
||||
// The Y component runs perpendicular to the edge (i.e. distance-to-edge).
|
||||
// NOTE: once we are back in device space and bloat.x == bloat.y, we will not need to find and
|
||||
// divide by nwidth a second time.
|
||||
g->codeAppendf( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
|
||||
g->codeAppendf( "nwidth = dot(abs(n), bloat) * 2;");
|
||||
g->codeAppendf( "n /= nwidth;");
|
||||
g->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
|
||||
g->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
|
||||
|
||||
// Translate the bisector into logical AA box space.
|
||||
// NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
|
||||
// bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
|
||||
g->codeAppendf( "%s[i] = -bisect * %s[i];",
|
||||
fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
|
||||
g->codeAppendf("}");
|
||||
|
||||
int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner");
|
||||
|
||||
g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
|
||||
GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
|
||||
numVertices, 3);
|
||||
}
|
||||
|
||||
void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody,
|
||||
const char* position,
|
||||
const char* /*coverage*/,
|
||||
const char* wind) const {
|
||||
fnBody->appendf("for (int i = 0; i < 2; ++i) {");
|
||||
fnBody->appendf( "%s[i] = %s * %s[i] + %s[i];",
|
||||
fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
|
||||
fAABoxTranslates.c_str());
|
||||
fnBody->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
|
||||
fnBody->appendf("}");
|
||||
}
|
||||
|
||||
void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
// By the time we reach this shader, the pixel is in the following state:
|
||||
//
|
||||
// 1. The hull shader has emitted a coverage of 1.
|
||||
// 2. Both edges have subtracted the area on their outside.
|
||||
//
|
||||
// This generally works, but it is a problem for corner pixels. There is a region within corner
|
||||
// pixels that is outside both edges at the same time. This means the region has been double
|
||||
// subtracted (once by each edge). The purpose of this shader is to fix these corner pixels.
|
||||
//
|
||||
// More specifically, each edge redoes its coverage analysis so that it only subtracts the
|
||||
// outside area that falls on its own side of the bisector line.
|
||||
//
|
||||
// NOTE: unless the edges fall on multiples of 90 deg from one another, they will have different
|
||||
// AA boxes. (For an explanation of AA boxes, see comments in onEmitGeometryShader.) This means
|
||||
// the coverage analysis will only be approximate. It seems acceptable, but if we want exact
|
||||
// coverage we will need to switch to a more expensive model.
|
||||
f->codeAppendf("%s = 0;", outputCoverage);
|
||||
|
||||
// Loop through both edges.
|
||||
f->codeAppendf("for (int i = 0; i < 2; ++i) {");
|
||||
f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
|
||||
f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
|
||||
|
||||
// Find the point at which the bisector exits the logical AA box.
|
||||
// (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
|
||||
f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
|
||||
f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
|
||||
": d.x / bisect.x;");
|
||||
f->codeAppendf( "half2 exit = corner + bisect * T;");
|
||||
|
||||
// These lines combined (and the final multiply by .5) accomplish the following:
|
||||
// 1. Add back the area beyond the corner that was subtracted out previously.
|
||||
// 2. Subtract out the area beyond the corner, but under the bisector.
|
||||
// The other edge will take care of the area on its own side of the bisector.
|
||||
f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
|
||||
f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
|
||||
f->codeAppendf("}");
|
||||
|
||||
f->codeAppendf("%s *= .5;", outputCoverage);
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCPRTriangleProcessor_DEFINED
|
||||
#define GrCCPRTriangleProcessor_DEFINED
|
||||
|
||||
#include "ccpr/GrCCPRCoverageProcessor.h"
|
||||
|
||||
/**
|
||||
* This class renders the coverage of triangles.
|
||||
*
|
||||
* Triangles are rendered in three passes:
|
||||
*
|
||||
* Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
|
||||
* is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
|
||||
* convex hull of those boxes.)
|
||||
*
|
||||
* Pass 2: Smooth the edges that were over-rendered during Pass 1. Draw the conservative raster of
|
||||
* each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
|
||||
* coverage=-1 on the outside edge to coverage=0 on the inside edge.
|
||||
*
|
||||
* Pass 3: Touch up the corner pixels to have the correct coverage.
|
||||
*/
|
||||
class GrCCPRTriangleProcessor : public GrCCPRCoverageProcessor::PrimitiveProcessor {
|
||||
public:
|
||||
GrCCPRTriangleProcessor(CoverageType initialCoverage) : INHERITED(initialCoverage) {}
|
||||
|
||||
void onEmitVertexShader(const GrCCPRCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer, const char* atlasOffset,
|
||||
const char* rtAdjust, GrGPArgs*) const override;
|
||||
void emitWind(GrGLSLGeometryBuilder*, const char* rtAdjust, const char* outputWind) const final;
|
||||
|
||||
protected:
|
||||
void defineInputVertices(GrGLSLGeometryBuilder*) const;
|
||||
|
||||
private:
|
||||
typedef GrCCPRCoverageProcessor::PrimitiveProcessor INHERITED;
|
||||
};
|
||||
|
||||
class GrCCPRTriangleHullAndEdgeProcessor : public GrCCPRTriangleProcessor {
|
||||
public:
|
||||
enum class GeometryType {
|
||||
kHulls,
|
||||
kEdges,
|
||||
kHullsAndEdges
|
||||
};
|
||||
|
||||
GrCCPRTriangleHullAndEdgeProcessor(GeometryType geometryType)
|
||||
: INHERITED(GeometryType::kHulls == geometryType ?
|
||||
CoverageType::kOne : CoverageType::kInterpolated)
|
||||
, fGeometryType(geometryType) {}
|
||||
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const override;
|
||||
|
||||
private:
|
||||
const GeometryType fGeometryType;
|
||||
|
||||
typedef GrCCPRTriangleProcessor INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* This pass fixes the corner pixels of a triangle. It touches up the simple distance-to-edge
|
||||
* coverage analysis done previously so that it takes into account the region that is outside both
|
||||
* edges at the same time.
|
||||
*/
|
||||
class GrCCPRTriangleCornerProcessor : public GrCCPRTriangleProcessor {
|
||||
public:
|
||||
GrCCPRTriangleCornerProcessor()
|
||||
: INHERITED(CoverageType::kShader)
|
||||
, fAABoxMatrices("aa_box_matrices", kFloat2x2_GrSLType, 2)
|
||||
, fAABoxTranslates("aa_box_translates", kFloat2_GrSLType, 2)
|
||||
, fGeoShaderBisects("bisects", kFloat2_GrSLType, 2)
|
||||
, fCornerLocationInAABoxes(kFloat2x2_GrSLType)
|
||||
, fBisectInAABoxes(kFloat2x2_GrSLType) {}
|
||||
|
||||
void resetVaryings(GrGLSLVaryingHandler* varyingHandler) override {
|
||||
this->INHERITED::resetVaryings(varyingHandler);
|
||||
varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
|
||||
varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
|
||||
}
|
||||
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder*, const char* emitVertexFn, const char* wind,
|
||||
const char* rtAdjust) const override;
|
||||
void emitPerVertexGeometryCode(SkString* fnBody, const char* position, const char* coverage,
|
||||
const char* wind) const override;
|
||||
void emitShaderCoverage(GrGLSLFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
private:
|
||||
GrShaderVar fAABoxMatrices;
|
||||
GrShaderVar fAABoxTranslates;
|
||||
GrShaderVar fGeoShaderBisects;
|
||||
GrGLSLGeoToFrag fCornerLocationInAABoxes;
|
||||
GrGLSLGeoToFrag fBisectInAABoxes;
|
||||
|
||||
typedef GrCCPRTriangleProcessor INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
168
src/gpu/ccpr/GrCCPRTriangleShader.cpp
Normal file
168
src/gpu/ccpr/GrCCPRTriangleShader.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrCCPRTriangleShader.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexShaderBuilder.h"
|
||||
|
||||
void GrCCPRTriangleShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
|
||||
GrGLSLShaderBuilder* s,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const {
|
||||
s->appendTexelFetch(pointsBuffer,
|
||||
SkStringPrintf("%s[%s]", proc.instanceAttrib(), pointId).c_str());
|
||||
}
|
||||
|
||||
void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
|
||||
const char* outputWind) const {
|
||||
s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));",
|
||||
outputWind, pts, pts, pts, pts);
|
||||
}
|
||||
|
||||
GrCCPRTriangleHullShader::WindHandling
|
||||
GrCCPRTriangleHullShader::onEmitVaryings(GrGLSLVaryingHandler*, SkString* code,
|
||||
const char* /*position*/, const char* /*coverage*/,
|
||||
const char* /*wind*/) {
|
||||
return WindHandling::kNotHandled; // No varyings.Let the base class handle wind.
|
||||
}
|
||||
|
||||
void GrCCPRTriangleHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("%s = 1;", outputCoverage);
|
||||
}
|
||||
|
||||
GrCCPRTriangleEdgeShader::WindHandling
|
||||
GrCCPRTriangleEdgeShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
|
||||
const char* position, const char* coverage,
|
||||
const char* wind) {
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind, kLow_GrSLPrecision);
|
||||
code->appendf("%s = %s * %s;", fCoverageTimesWind.gsOut(), coverage, wind);
|
||||
return WindHandling::kHandled;
|
||||
}
|
||||
|
||||
void GrCCPRTriangleEdgeShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
||||
}
|
||||
|
||||
void GrCCPRTriangleCornerShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
|
||||
const char* cornerId, const char* bloat,
|
||||
const char* wind, const char* rtAdjust,
|
||||
GeometryVars* vars) const {
|
||||
s->codeAppendf("float2 corner = %s[sk_InvocationID];", pts);
|
||||
vars->fCornerVars.fPoint = "corner";
|
||||
|
||||
s->codeAppendf("float2x2 vectors = float2x2(corner - %s[(sk_InvocationID + 2) %% 3], "
|
||||
"corner - %s[(sk_InvocationID + 1) %% 3]);",
|
||||
pts, pts);
|
||||
|
||||
// Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this
|
||||
// is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
|
||||
s->codeAppend ("for (int i = 0; i < 2; ++i) {");
|
||||
s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);");
|
||||
s->codeAppend ("}");
|
||||
|
||||
// Find the vector that bisects the region outside the incoming edges. Each edge is
|
||||
// responsible to subtract the outside region on its own the side of the bisector.
|
||||
s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
|
||||
s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
|
||||
s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? "
|
||||
"leftdir + rightdir : "
|
||||
"float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
|
||||
|
||||
// In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge
|
||||
// method actually finds is coverage inside a logical "AA box", one that is rotated inline
|
||||
// with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set
|
||||
// up transformations into normalized logical AA box space for both incoming edges. These
|
||||
// will tell the fragment shader where the corner is located within each edge's AA box.
|
||||
s->declareGlobal(fAABoxMatrices);
|
||||
s->declareGlobal(fAABoxTranslates);
|
||||
s->declareGlobal(fGeoShaderBisects);
|
||||
s->codeAppendf("for (int i = 0; i < 2; ++i) {");
|
||||
// The X component runs parallel to the edge (i.e. distance to the corner).
|
||||
s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
|
||||
s->codeAppend ( "float nwidth = dot(abs(n), bloat) * 2;");
|
||||
s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
|
||||
s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
|
||||
s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
|
||||
|
||||
// The Y component runs perpendicular to the edge (i.e. distance-to-edge).
|
||||
// NOTE: if we are back in device space and bloat.x == bloat.y, we will not need to find and
|
||||
// divide by nwidth a second time.
|
||||
s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
|
||||
s->codeAppend ( "nwidth = dot(abs(n), bloat) * 2;");
|
||||
s->codeAppend ( "n /= nwidth;");
|
||||
s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
|
||||
s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
|
||||
|
||||
// Translate the bisector into logical AA box space.
|
||||
// NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
|
||||
// bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
|
||||
s->codeAppendf( "%s[i] = -bisect * %s[i];",
|
||||
fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
|
||||
s->codeAppend ("}");
|
||||
}
|
||||
|
||||
GrCCPRTriangleCornerShader::WindHandling
|
||||
GrCCPRTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code,
|
||||
const char* position, const char* /*coverage*/,
|
||||
const char* /*wind*/) {
|
||||
varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
|
||||
varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes);
|
||||
code->appendf("for (int i = 0; i < 2; ++i) {");
|
||||
code->appendf( "%s[i] = %s * %s[i] + %s[i];",
|
||||
fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(),
|
||||
fAABoxTranslates.c_str());
|
||||
code->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str());
|
||||
code->appendf("}");
|
||||
|
||||
return WindHandling::kNotHandled;
|
||||
}
|
||||
|
||||
void GrCCPRTriangleCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
// By the time we reach this shader, the pixel is in the following state:
|
||||
//
|
||||
// 1. The hull shader has emitted a coverage of 1.
|
||||
// 2. Both edges have subtracted the area on their outside.
|
||||
//
|
||||
// This generally works, but it is a problem for corner pixels. There is a region within
|
||||
// corner pixels that is outside both edges at the same time. This means the region has been
|
||||
// double subtracted (once by each edge). The purpose of this shader is to fix these corner
|
||||
// pixels.
|
||||
//
|
||||
// More specifically, each edge redoes its coverage analysis so that it only subtracts the
|
||||
// outside area that falls on its own side of the bisector line.
|
||||
//
|
||||
// NOTE: unless the edges fall on multiples of 90 deg from one another, they will have
|
||||
// different AA boxes. (For an explanation of AA boxes, see comments in
|
||||
// onEmitGeometryShader.) This means the coverage analysis will only be approximate. It
|
||||
// seems acceptable, but if we want exact coverage we will need to switch to a more
|
||||
// expensive model.
|
||||
f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges.
|
||||
f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
|
||||
f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
|
||||
|
||||
// Find the point at which the bisector exits the logical AA box.
|
||||
// (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
|
||||
f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
|
||||
f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
|
||||
": d.x / bisect.x;");
|
||||
f->codeAppendf( "half2 exit = corner + bisect * T;");
|
||||
|
||||
// These lines combined (and the final multiply by .5) accomplish the following:
|
||||
// 1. Add back the area beyond the corner that was subtracted out previously.
|
||||
// 2. Subtract out the area beyond the corner, but under the bisector.
|
||||
// The other edge will take care of the area on its own side of the bisector.
|
||||
f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
|
||||
f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
|
||||
f->codeAppendf("}");
|
||||
|
||||
f->codeAppendf("%s *= .5;", outputCoverage);
|
||||
}
|
80
src/gpu/ccpr/GrCCPRTriangleShader.h
Normal file
80
src/gpu/ccpr/GrCCPRTriangleShader.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCPRTriangleShader_DEFINED
|
||||
#define GrCCPRTriangleShader_DEFINED
|
||||
|
||||
#include "ccpr/GrCCPRCoverageProcessor.h"
|
||||
|
||||
/**
|
||||
* This class renders the coverage of triangles. Triangles are rendered in three passes, as
|
||||
* described below.
|
||||
*/
|
||||
class GrCCPRTriangleShader : public GrCCPRCoverageProcessor::Shader {
|
||||
public:
|
||||
int getNumInputPoints() const final { return 3; }
|
||||
int getNumSegments() const final { return 3; } // 3 wedges, 3 edges, 3 corners.
|
||||
|
||||
void appendInputPointFetch(const GrCCPRCoverageProcessor&, GrGLSLShaderBuilder*,
|
||||
const TexelBufferHandle& pointsBuffer,
|
||||
const char* pointId) const final;
|
||||
|
||||
void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
|
||||
const char* outputWind) const final;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pass 1: Draw the triangle's conservative raster hull with a coverage of 1. (Conservative raster
|
||||
* is drawn by considering 3 pixel size boxes, one centered at each vertex, and drawing the
|
||||
* convex hull of those boxes.)
|
||||
*/
|
||||
class GrCCPRTriangleHullShader : public GrCCPRTriangleShader {
|
||||
GeometryType getGeometryType() const override { return GeometryType::kHull; }
|
||||
|
||||
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
|
||||
const char* coverage, const char* wind) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, const char* outputCoverage) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pass 2: Smooth the edges that were over-rendered during Pass 1. Draw the conservative raster of
|
||||
* each edge (i.e. convex hull of two pixel-size boxes at the endpoints), interpolating from
|
||||
* coverage=-1 on the outside edge to coverage=0 on the inside edge.
|
||||
*/
|
||||
class GrCCPRTriangleEdgeShader : public GrCCPRTriangleShader {
|
||||
GeometryType getGeometryType() const override { return GeometryType::kEdges; }
|
||||
|
||||
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
|
||||
const char* coverage, const char* wind) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
|
||||
|
||||
GrGLSLGeoToFrag fCoverageTimesWind{kHalf_GrSLType};
|
||||
};
|
||||
|
||||
/**
|
||||
* Pass 3: Touch up the corner pixels. Here we fix the simple distance-to-edge coverage analysis
|
||||
* done previously so that it takes into account the region that is outside both edges at
|
||||
* the same time.
|
||||
*/
|
||||
class GrCCPRTriangleCornerShader : public GrCCPRTriangleShader {
|
||||
GeometryType getGeometryType() const override { return GeometryType::kCorners; }
|
||||
|
||||
void emitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
|
||||
const char* bloat, const char* wind, const char* rtAdjust,
|
||||
GeometryVars*) const override;
|
||||
WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
|
||||
const char* coverage, const char* wind) override;
|
||||
void onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, const char* outputCoverage) const override;
|
||||
|
||||
GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2};
|
||||
GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};
|
||||
GrShaderVar fGeoShaderBisects{"bisects", kFloat2_GrSLType, 2};
|
||||
GrGLSLGeoToFrag fCornerLocationInAABoxes{kFloat2x2_GrSLType};
|
||||
GrGLSLGeoToFrag fBisectInAABoxes{kFloat2x2_GrSLType};
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user