diff options
author | 2017-10-14 21:48:10 -0600 | |
---|---|---|
committer | 2017-10-15 04:13:06 +0000 | |
commit | e501033bbdc9143848edde3c3e3b7282169bc11e (patch) | |
tree | e518ba3cc2c5b7f7eb5aaf579f23060966ae1e37 /src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp | |
parent | ac094702f425e07c0d479ef5caf793527d48bebe (diff) |
Refactor CCPR coverage shaders for a vertex impl
Decouples geometry generation and analytic coverage. This paves the
way for a vertex shader implementation.
Bug: skia:
Change-Id: I23b79d4397db22bd8fc063b8dfca58ab00037292
Reviewed-on: https://skia-review.googlesource.com/59200
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Diffstat (limited to 'src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp')
-rw-r--r-- | src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp new file mode 100644 index 0000000000..b9a38e732a --- /dev/null +++ b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp @@ -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; +} |