diff options
author | 2017-07-14 15:17:41 -0600 | |
---|---|---|
committer | 2017-07-14 21:45:35 +0000 | |
commit | 1a325d25b941ef801b3e9b2c0342da43cf35cdba (patch) | |
tree | 3f65d36541399e1ae6a529a534119815a2c5ba36 /src/gpu/ccpr/GrCCPRTriangleProcessor.cpp | |
parent | 588fb040b3ad410cdb10c87f9a7884b6eb825e90 (diff) |
Coverage counting path renderer
Initial implementation of a GPU path renderer that draws antialiased
paths by counting coverage in an offscreen buffer.
Initially disabled until it has had time to soak.
Bug: skia:
Change-Id: I003d8cfdf8dc62641581b5ea2dc4f0aa00108df6
Reviewed-on: https://skia-review.googlesource.com/21541
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Diffstat (limited to 'src/gpu/ccpr/GrCCPRTriangleProcessor.cpp')
-rw-r--r-- | src/gpu/ccpr/GrCCPRTriangleProcessor.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp b/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp new file mode 100644 index 0000000000..23f7b143b1 --- /dev/null +++ b/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp @@ -0,0 +1,201 @@ +/* + * 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 { + v->codeAppend ("highp vec2 self = "); + v->appendTexelFetch(pointsBuffer, + SkStringPrintf("%s[sk_VertexID]", proc.instanceAttrib()).c_str()); + v->codeAppendf(".xy + %s;", atlasOffset); + gpArgs->fPositionVar.set(kVec2f_GrSLType, "self"); +} + +void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const { + // Prepend in_vertices at the start of the shader. + g->codePrependf("highp mat3x2 in_vertices = mat3x2(sk_in[0].gl_Position.xy, " + "sk_in[1].gl_Position.xy, " + "sk_in[2].gl_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(mat2(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("highp vec2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind); + g->codeAppendf("highp vec2 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::onEmitVertexShader(const GrCCPRCoverageProcessor& proc, + GrGLSLVertexBuilder* v, + const TexelBufferHandle& pointsBuffer, + const char* atlasOffset, + const char* rtAdjust, + GrGPArgs* gpArgs) const { + this->INHERITED::onEmitVertexShader(proc, v, pointsBuffer, atlasOffset, rtAdjust, gpArgs); + + // Fetch and transform the next point in the triangle. + v->codeAppend ("highp vec2 next = "); + v->appendTexelFetch(pointsBuffer, + SkStringPrintf("%s[(sk_VertexID+1) %% 3]", proc.instanceAttrib()).c_str()); + v->codeAppendf(".xy + %s;", atlasOffset); + + // Find the plane that gives distance from the [self -> next] edge, normalized to its AA + // bloat width. + v->codeAppend ("highp vec2 n = vec2(next.y - self.y, self.x - next.x);"); + v->codeAppendf("highp vec2 d = n * mat2(self + %f * sign(n), " + "self - %f * sign(n));", kAABloatRadius, kAABloatRadius); + + // Clamp for when n=0. (wind=0 when n=0, so as long as we don't get Inf or NaN we are fine.) + v->codeAppendf("%s.xy = n / max(d[0] - d[1], 1e-30);", fEdgeDistance.vsOut()); + v->codeAppendf("%s.z = -dot(%s.xy, self);", fEdgeDistance.vsOut(), fEdgeDistance.vsOut()); + + // Emit device coords to geo shader. + v->codeAppendf("%s = self;", fDevCoord.vsOut()); +} + +void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, + const char* emitVertexFn, const char* wind, + const char* rtAdjust) const { + this->defineInputVertices(g); + + g->codeAppend ("highp vec2 self = in_vertices[sk_InvocationID];"); + g->codeAppendf("%s(self + vec2(-bloat.x, -bloat.y), 1);", emitVertexFn); + g->codeAppendf("%s(self + vec2(-bloat.x, +bloat.y), 1);", emitVertexFn); + g->codeAppendf("%s(self + vec2(+bloat.x, -bloat.y), 1);", emitVertexFn); + g->codeAppendf("%s(self + vec2(+bloat.x, +bloat.y), 1);", emitVertexFn); + g->codeAppend ("EndPrimitive();"); + + g->configure(GrGLSLGeometryBuilder::InputType::kTriangles, + GrGLSLGeometryBuilder::OutputType::kTriangleStrip, + 4, 3); +} + +void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody, + const char* position, + const char* /*coverage*/, + const char* wind) const { + fnBody->appendf("%s.xy = %s[(sk_InvocationID + 1) %% 3];", + fNeighbors.gsOut(), fDevCoord.gsIn()); + fnBody->appendf("%s.zw = %s[(sk_InvocationID + 2) %% 3];", + fNeighbors.gsOut(), fDevCoord.gsIn()); + fnBody->appendf("%s = mat3(%s[(sk_InvocationID + 2) %% 3], " + "%s[sk_InvocationID], " + "%s[(sk_InvocationID + 1) %% 3]) * %s;", + fEdgeDistances.gsOut(), fEdgeDistance.gsIn(), fEdgeDistance.gsIn(), + fEdgeDistance.gsIn(), wind); + + // Otherwise, fEdgeDistances = mat3(...) * sign(wind * rtAdjust.x * rdAdjust.z). + GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin); + + fnBody->appendf("%s = sk_InvocationID;", fCornerIdx.gsOut()); +} + +void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f, + const char* outputCoverage) const { + // FIXME: Adreno breaks if we don't put the frag coord in an intermediate highp variable. + f->codeAppendf("highp vec2 fragcoord = sk_FragCoord.xy;"); + + // Approximate coverage by tracking where 4 horizontal lines enter and leave the triangle. + GrShaderVar samples("samples", kVec4f_GrSLType, GrShaderVar::kNonArray, + kHigh_GrSLPrecision); + f->declareGlobal(samples); + f->codeAppendf("%s = fragcoord.y + vec4(-0.375, -0.125, 0.125, 0.375);", samples.c_str()); + + GrShaderVar leftedge("leftedge", kVec4f_GrSLType, GrShaderVar::kNonArray, + kHigh_GrSLPrecision); + f->declareGlobal(leftedge); + f->codeAppendf("%s = vec4(fragcoord.x - 0.5);", leftedge.c_str()); + + GrShaderVar rightedge("rightedge", kVec4f_GrSLType, GrShaderVar::kNonArray, + kHigh_GrSLPrecision); + f->declareGlobal(rightedge); + f->codeAppendf("%s = vec4(fragcoord.x + 0.5);", rightedge.c_str()); + + SkString sampleEdgeFn; + GrShaderVar edgeArg("edge_distance", kVec3f_GrSLType, GrShaderVar::kNonArray, + kHigh_GrSLPrecision); + f->emitFunction(kVoid_GrSLType, "sampleEdge", 1, &edgeArg, [&]() { + SkString b; + b.appendf("highp float m = abs(%s.x) < 1e-3 ? 1e18 : -1 / %s.x;", + edgeArg.c_str(), edgeArg.c_str()); + b.appendf("highp vec4 edge = m * (%s.y * samples + %s.z);", + edgeArg.c_str(), edgeArg.c_str()); + b.appendf("if (%s.x <= 1e-3 || (abs(%s.x) < 1e-3 && %s.y > 0)) {", + edgeArg.c_str(), edgeArg.c_str(), edgeArg.c_str()); + b.appendf( "%s = max(%s, edge);", leftedge.c_str(), leftedge.c_str()); + b.append ("} else {"); + b.appendf( "%s = min(%s, edge);", rightedge.c_str(), rightedge.c_str()); + b.append ("}"); + return b; + }().c_str(), &sampleEdgeFn); + + // See if the previous neighbor already handled this pixel. + f->codeAppendf("if (all(lessThan(abs(fragcoord - %s.zw), vec2(%f)))) {", + fNeighbors.fsIn(), kAABloatRadius); + // Handle the case where all 3 corners defer to the previous neighbor. + f->codeAppendf( "if (%s != 0 || !all(lessThan(abs(fragcoord - %s.xy), vec2(%f)))) {", + fCornerIdx.fsIn(), fNeighbors.fsIn(), kAABloatRadius); + f->codeAppend ( "discard;"); + f->codeAppend ( "}"); + f->codeAppend ("}"); + + // Erase what the hull and two edges wrote at this corner in previous shaders (the two .5's + // for the edges and the -1 for the hull cancel each other out). + f->codeAppendf("%s = dot(vec3(fragcoord, 1) * mat2x3(%s), vec2(1));", + outputCoverage, fEdgeDistances.fsIn()); + + // Sample the two edges at this corner. + f->codeAppendf("%s(%s[0]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn()); + f->codeAppendf("%s(%s[1]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn()); + + // Handle the opposite edge if the next neighbor will defer to us. + f->codeAppendf("if (all(lessThan(abs(fragcoord - %s.xy), vec2(%f)))) {", + fNeighbors.fsIn(), kAABloatRadius); + // Erase the coverage the opposite edge wrote to this corner. + f->codeAppendf( "%s += dot(%s[2], vec3(fragcoord, 1)) + 0.5;", + outputCoverage, fEdgeDistances.fsIn()); + // Sample the opposite edge. + f->codeAppendf( "%s(%s[2]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn()); + f->codeAppend ("}"); + + f->codeAppendf("highp vec4 widths = max(%s - %s, 0);", rightedge.c_str(), leftedge.c_str()); + f->codeAppendf("%s += dot(widths, vec4(0.25));", outputCoverage); +} |