Convert geometry shaders to operate in Skia device space

Defers the transformation to normalized window coordinates until after
the geometry shader. Merges vertex and a geometry shader builders into
a single compilation unit with a common base class.  Updates CCPR
geometry shaders accordingly.

Bug: skia:
Change-Id: If93c90e978b1fdc7120febd05cfb05810fd496b5
Reviewed-on: https://skia-review.googlesource.com/62980
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 8f82e71..c585dd2 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -466,8 +466,6 @@
   "$_src/gpu/glsl/GrGLSLFragmentShaderBuilder.h",
   "$_src/gpu/glsl/GrGLSLGeometryProcessor.cpp",
   "$_src/gpu/glsl/GrGLSLGeometryProcessor.h",
-  "$_src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp",
-  "$_src/gpu/glsl/GrGLSLGeometryShaderBuilder.h",
   "$_src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp",
   "$_src/gpu/glsl/GrGLSLPrimitiveProcessor.h",
   "$_src/gpu/glsl/GrGLSLProgramBuilder.cpp",
@@ -481,8 +479,8 @@
   "$_src/gpu/glsl/GrGLSLUtil.h",
   "$_src/gpu/glsl/GrGLSLVarying.cpp",
   "$_src/gpu/glsl/GrGLSLVarying.h",
-  "$_src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp",
-  "$_src/gpu/glsl/GrGLSLVertexShaderBuilder.h",
+  "$_src/gpu/glsl/GrGLSLVertexGeoBuilder.cpp",
+  "$_src/gpu/glsl/GrGLSLVertexGeoBuilder.h",
   "$_src/gpu/glsl/GrGLSLXferProcessor.cpp",
   "$_src/gpu/glsl/GrGLSLXferProcessor.h",
 
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index aa1fb8b..8633398 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -70,9 +70,9 @@
 
     SkPoint fPoints[4] = {
         {100.05f, 100.05f},
-        {100.05f, 300.95f},
+        {400.75f, 100.05f},
         {400.75f, 300.95f},
-        {400.75f, 100.05f}
+        {100.05f, 300.95f}
     };
 
     SkTArray<SkPoint>   fGpuPoints;
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index a0c1f2a..70f64ab 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -11,7 +11,7 @@
 #include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "glsl/GrGLSLVarying.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLUtil.h"
diff --git a/src/gpu/ccpr/GrCCPRAtlas.cpp b/src/gpu/ccpr/GrCCPRAtlas.cpp
index 1cd7fa8..8978e31 100644
--- a/src/gpu/ccpr/GrCCPRAtlas.cpp
+++ b/src/gpu/ccpr/GrCCPRAtlas.cpp
@@ -105,7 +105,6 @@
     SkASSERT(!fTextureProxy);
 
     GrSurfaceDesc desc;
-    desc.fOrigin = GrCCPRCoverageProcessor::kAtlasOrigin;
     desc.fWidth = fWidth;
     desc.fHeight = fHeight;
     desc.fConfig = kAlpha_half_GrPixelConfig;
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
index 34e0410..5e0538f 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
@@ -387,7 +387,6 @@
 void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
     using RenderPass = GrCCPRCoverageProcessor::RenderPass;
 
-    SkDEBUGCODE(GrCCPRCoverageProcessor::Validate(flushState->drawOpArgs().fProxy));
     SkASSERT(fPointsBuffer);
     SkASSERT(fInstanceBuffer);
 
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
index 3c0b149..e4cabf7 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
@@ -174,15 +174,3 @@
     SK_ABORT("Unexpected GrCCPRCoverageProcessor::RenderPass.");
     return nullptr;
 }
-
-#ifdef SK_DEBUG
-
-#include "GrRenderTargetProxy.h"
-
-void GrCCPRCoverageProcessor::Validate(GrRenderTargetProxy* atlasProxy) {
-    SkASSERT(kAtlasOrigin == atlasProxy->origin());
-    SkASSERT(GrPixelConfigIsAlphaOnly(atlasProxy->config()));
-    SkASSERT(GrPixelConfigIsFloatingPoint(atlasProxy->config()));
-}
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.h b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
index f2c5f43..17aaba5 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -34,9 +34,6 @@
  */
 class GrCCPRCoverageProcessor : public GrGeometryProcessor {
 public:
-    // Use top-left to avoid a uniform access in the fragment shader.
-    static constexpr GrSurfaceOrigin kAtlasOrigin = kTopLeft_GrSurfaceOrigin;
-
     static constexpr GrPrimitiveType kTrianglesGrPrimitiveType = GrPrimitiveType::kTriangles;
     static constexpr GrPrimitiveType kQuadraticsGrPrimitiveType = GrPrimitiveType::kTriangles;
     static constexpr GrPrimitiveType kCubicsGrPrimitiveType = GrPrimitiveType::kLinesAdjacency;
@@ -127,7 +124,7 @@
 
         // 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,
+        virtual void emitWind(GrGLSLShaderBuilder*, const char* pts,
                               const char* outputWind) const = 0;
 
         union GeometryVars {
@@ -147,8 +144,7 @@
         // 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 {}
+                                   const char* wind, GeometryVars*) const {}
 
         void emitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
                           const char* coverage, const char* wind);
@@ -213,8 +209,6 @@
     void enableDebugVisualizations(float debugBloat) { fDebugBloat = debugBloat; }
     bool debugVisualizationsEnabled() const { return fDebugBloat > 0; }
     float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; }
-
-    static void Validate(GrRenderTargetProxy* atlasProxy);
 #endif
 
     class GSImpl;
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
index b9a38e7..f501c3d 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
@@ -7,8 +7,7 @@
 
 #include "GrCCPRCoverageProcessor.h"
 
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 using Shader = GrCCPRCoverageProcessor::Shader;
 
@@ -70,7 +69,7 @@
 
         GrShaderVar wind("wind", kHalf_GrSLType);
         g->declareGlobal(wind);
-        fShader->emitWind(g, "pts", rtAdjust, wind.c_str());
+        fShader->emitWind(g, "pts", wind.c_str());
 
         SkString emitVertexFn;
         SkSTArray<2, GrShaderVar> emitArgs;
@@ -79,21 +78,21 @@
         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();");
+            g->emitVertex(&fnBody, position, rtAdjust);
             return fnBody;
         }().c_str(), &emitVertexFn);
 
-        g->codeAppendf("float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
+        float bloat = kAABloatRadius;
 #ifdef SK_DEBUG
         if (proc.debugVisualizationsEnabled()) {
-            g->codeAppendf("bloat *= %f;", proc.debugBloat());
+            bloat *= proc.debugBloat();
         }
 #endif
+        g->defineConstant("bloat", bloat);
 
         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);
+        fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
+        int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), vars);
 
         int numInputPoints = fShader->getNumInputPoints();
         SkASSERT(3 == numInputPoints || 4 == numInputPoints);
@@ -104,7 +103,7 @@
     }
 
     virtual int onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
-                                     const char* emitVertexFn, const char* rtAdjust,
+                                     const char* emitVertexFn,
                                      const Shader::GeometryVars&) const = 0;
 
     virtual ~GSImpl() {}
@@ -119,7 +118,7 @@
     GSHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
 
     int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
-                             const char* emitVertexFn, const char* rtAdjust,
+                             const char* emitVertexFn,
                              const Shader::GeometryVars& vars) const override {
         int numSides = fShader->getNumSegments();
         SkASSERT(numSides >= 3);
@@ -190,7 +189,7 @@
         g->codeAppend ("if (all(dnotequal)) {");
         g->codeAppendf(    "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
         g->codeAppend ("}");
-        g->codeAppend ("EndPrimitive();");
+        g->endPrimitive();
 
         return 5;
     }
@@ -201,7 +200,7 @@
     GSEdgeImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
 
     int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
-                             const char* emitVertexFn, const char* rtAdjust,
+                             const char* emitVertexFn,
                              const Shader::GeometryVars&) const override {
         int numSides = fShader->getNumSegments();
 
@@ -238,7 +237,7 @@
         g->codeAppend ("if (!aligned) {");
         g->codeAppendf(    "%s(outer_pts[1], outer_coverage[1]);", emitVertexFn);
         g->codeAppend ("}");
-        g->codeAppend ("EndPrimitive();");
+        g->endPrimitive();
 
         return 6;
     }
@@ -249,16 +248,16 @@
     GSCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
 
     int onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
-                             const char* emitVertexFn, const char* rtAdjust,
+                             const char* emitVertexFn,
                              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();");
+        g->codeAppendf("%s(%s + float2(-bloat, -bloat), 1);", emitVertexFn, corner);
+        g->codeAppendf("%s(%s + float2(-bloat, +bloat), 1);", emitVertexFn, corner);
+        g->codeAppendf("%s(%s + float2(+bloat, -bloat), 1);", emitVertexFn, corner);
+        g->codeAppendf("%s(%s + float2(+bloat, +bloat), 1);", emitVertexFn, corner);
+        g->endPrimitive();
 
         return 4;
     }
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.cpp b/src/gpu/ccpr/GrCCPRCubicShader.cpp
index ea1f734..3a820d7 100644
--- a/src/gpu/ccpr/GrCCPRCubicShader.cpp
+++ b/src/gpu/ccpr/GrCCPRCubicShader.cpp
@@ -18,15 +18,14 @@
 }
 
 void GrCCPRCubicShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
-                                           const char* rtAdjust, const char* outputWind) const {
+                                 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);
+    s->codeAppendf("if (2 * abs(area_times_2) < length(%s[3] - %s[0])) {", pts, pts);
 #ifndef SK_BUILD_FOR_MAC
     s->codeAppend (    "return;");
 #else
@@ -38,8 +37,8 @@
 }
 
 void GrCCPRCubicShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
-                                      const char* segmentId, const char* bloat, const char* wind,
-                                      const char* rtAdjust, GeometryVars* vars) const {
+                                      const char* segmentId, const char* wind,
+                                      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);
 
@@ -93,13 +92,14 @@
                                   "0, orientation[0], 0, "
                                   "0, 0, orientation[1]);", fKLMMatrix.c_str());
 
+    // TODO: remove in followup CL.
     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);
+    s->codeAppendf("%s[0] = %s[0].xy;",
+                   fKLMDerivatives.c_str(), fKLMMatrix.c_str());
+    s->codeAppendf("%s[1] = %s[1].xy;",
+                   fKLMDerivatives.c_str(), fKLMMatrix.c_str());
+    s->codeAppendf("%s[2] = %s[2].xy;",
+                   fKLMDerivatives.c_str(), fKLMMatrix.c_str());
 
     // Determine the amount of additional coverage to subtract out for the flat edge (P3 -> P0).
     s->declareGlobal(fEdgeDistanceEquation);
@@ -108,7 +108,7 @@
     s->codeAppendf("float2 edgept1 = %s[3 - edgeidx0];", pts);
     Shader::EmitEdgeDistanceEquation(s, "edgept0", "edgept1", fEdgeDistanceEquation.c_str());
 
-    this->onEmitSetupCode(s, pts, segmentId, rtAdjust, vars);
+    this->onEmitSetupCode(s, pts, segmentId, vars);
 }
 
 GrCCPRCubicShader::WindHandling
@@ -125,8 +125,7 @@
 }
 
 void GrCCPRCubicHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* /*pts*/,
-                                            const char* /*wedgeId*/, const char* /*rtAdjust*/,
-                                            GeometryVars* vars) const {
+                                            const char* /*wedgeId*/, GeometryVars* vars) const {
     // "midpoint" was just defined by the base class.
     vars->fHullVars.fAlternateMidpoint = "midpoint";
 }
@@ -150,11 +149,11 @@
 }
 
 void GrCCPRCubicCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
-                                              const char* cornerId, const char* rtAdjust,
-                                              GeometryVars* vars) const {
+                                              const char* cornerId, GeometryVars* vars) const {
+    // TODO: remove in followup CL.
     s->declareGlobal(fEdgeDistanceDerivatives);
-    s->codeAppendf("%s = %s.xy * %s.xz;",
-                   fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
+    s->codeAppendf("%s = %s.xy;",
+                   fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str());
 
     s->codeAppendf("float2 corner = %s[%s * 3];", pts, cornerId);
     vars->fCornerVars.fPoint = "corner";
@@ -170,9 +169,6 @@
     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,
diff --git a/src/gpu/ccpr/GrCCPRCubicShader.h b/src/gpu/ccpr/GrCCPRCubicShader.h
index f9655be..eae2a20 100644
--- a/src/gpu/ccpr/GrCCPRCubicShader.h
+++ b/src/gpu/ccpr/GrCCPRCubicShader.h
@@ -37,15 +37,13 @@
                                const TexelBufferHandle& pointsBuffer,
                                const char* pointId) const final;
 
-    void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
-                  const char* outputWind) const final;
+    void emitWind(GrGLSLShaderBuilder*, const char* pts, 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;
+                       const char* wind, GeometryVars*) const final;
 
     virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
-                                 const char* rtAdjust, GeometryVars*) const = 0;
+                                 GeometryVars*) const = 0;
 
     WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
                                 const char* coverage, const char* wind) final;
@@ -67,7 +65,7 @@
     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;
+                         GeometryVars*) const override;
     void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
     void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
 
@@ -82,7 +80,7 @@
     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;
+                         GeometryVars*) const override;
     void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
     void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
 
diff --git a/src/gpu/ccpr/GrCCPRQuadraticShader.cpp b/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
index 44e9b7e..126dae1 100644
--- a/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
+++ b/src/gpu/ccpr/GrCCPRQuadraticShader.cpp
@@ -17,13 +17,12 @@
                         SkStringPrintf("%s.x + %s", proc.instanceAttrib(), pointId).c_str());
 }
 
-void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
+void GrCCPRQuadraticShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
                                      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);
+    s->codeAppendf("if (2 * abs(area_times_2) < length(%s[2] - %s[0])) {", pts, pts);
 #ifndef SK_BUILD_FOR_MAC
     s->codeAppend (    "return;");
 #else
@@ -35,8 +34,7 @@
 }
 
 void GrCCPRQuadraticShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
-                                          const char* segmentId, const char* bloat,
-                                          const char* wind, const char* rtAdjust,
+                                          const char* segmentId, const char* wind,
                                           GeometryVars* vars) const {
     s->declareGlobal(fCanonicalMatrix);
     s->codeAppendf("%s = float3x3(0.0, 0, 1, "
@@ -47,16 +45,17 @@
                                          "%s[2], 1));",
                    fCanonicalMatrix.c_str(), pts, pts, pts);
 
+    // TODO: remove in followup CL.
     s->declareGlobal(fCanonicalDerivatives);
-    s->codeAppendf("%s = float2x2(%s) * float2x2(%s.x, 0, 0, %s.z);",
-                   fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str(), rtAdjust, rtAdjust);
+    s->codeAppendf("%s = float2x2(%s);",
+                   fCanonicalDerivatives.c_str(), fCanonicalMatrix.c_str());
 
     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);
+    this->onEmitSetupCode(s, pts, segmentId, vars);
 }
 
 GrCCPRQuadraticShader::WindHandling
@@ -75,8 +74,7 @@
 }
 
 void GrCCPRQuadraticHullShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
-                                                const char* /*wedgeId*/, const char* /*rtAdjust*/,
-                                                GeometryVars* vars) const {
+                                                const char* /*wedgeId*/, 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);
@@ -109,11 +107,11 @@
 }
 
 void GrCCPRQuadraticCornerShader::onEmitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
-                                                  const char* cornerId, const char* rtAdjust,
-                                                  GeometryVars* vars) const {
+                                                  const char* cornerId, GeometryVars* vars) const {
+    // TODO: remove in followup CL.
     s->declareGlobal(fEdgeDistanceDerivatives);
-    s->codeAppendf("%s = %s.xy * %s.xz;",
-                   fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust);
+    s->codeAppendf("%s = %s.xy;",
+                   fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str());
 
     s->codeAppendf("float2 corner = %s[%s * 2];", pts, cornerId);
     vars->fCornerVars.fPoint = "corner";
diff --git a/src/gpu/ccpr/GrCCPRQuadraticShader.h b/src/gpu/ccpr/GrCCPRQuadraticShader.h
index 7441266..8511c51 100644
--- a/src/gpu/ccpr/GrCCPRQuadraticShader.h
+++ b/src/gpu/ccpr/GrCCPRQuadraticShader.h
@@ -28,15 +28,13 @@
                                const TexelBufferHandle& pointsBuffer,
                                const char* pointId) const override;
 
-    void emitWind(GrGLSLShaderBuilder*, const char* pts, const char* rtAdjust,
-                  const char* outputWind) const final;
+    void emitWind(GrGLSLShaderBuilder*, const char* pts, 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;
+                       const char* wind, GeometryVars*) const final;
 
     virtual void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* segmentId,
-                                 const char* rtAdjust, GeometryVars*) const = 0;
+                                 GeometryVars*) const = 0;
 
     WindHandling onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, const char* position,
                                 const char* coverage, const char* wind) final;
@@ -60,7 +58,7 @@
 
     GeometryType getGeometryType() const override { return GeometryType::kHull; }
     void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* wedgeId,
-                         const char* rtAdjust, GeometryVars*) const override;
+                         GeometryVars*) const override;
     void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
     void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
 
@@ -75,7 +73,7 @@
 
     GeometryType getGeometryType() const override { return GeometryType::kCorners; }
     void onEmitSetupCode(GrGLSLShaderBuilder*, const char* pts, const char* cornerId,
-                         const char* rtAdjust, GeometryVars*) const override;
+                         GeometryVars*) const override;
     void onEmitVaryings(GrGLSLVaryingHandler*, SkString* code) override;
     void onEmitFragmentCode(GrGLSLPPFragmentBuilder*, const char* outputCoverage) const override;
 
diff --git a/src/gpu/ccpr/GrCCPRTriangleShader.cpp b/src/gpu/ccpr/GrCCPRTriangleShader.cpp
index 5c50265..8db3b63 100644
--- a/src/gpu/ccpr/GrCCPRTriangleShader.cpp
+++ b/src/gpu/ccpr/GrCCPRTriangleShader.cpp
@@ -8,8 +8,7 @@
 #include "GrCCPRTriangleShader.h"
 
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 void GrCCPRTriangleShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc,
                                                  GrGLSLShaderBuilder* s,
@@ -19,8 +18,8 @@
                         SkStringPrintf("%s[%s]", proc.instanceAttrib(), pointId).c_str());
 }
 
-void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
-              const char* outputWind) const {
+void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts,
+                                    const char* outputWind) const {
     s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));",
                    outputWind, pts, pts, pts, pts);
 }
@@ -52,8 +51,7 @@
 }
 
 void GrCCPRTriangleCornerShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts,
-                                               const char* cornerId, const char* bloat,
-                                               const char* wind, const char* rtAdjust,
+                                               const char* cornerId, const char* wind,
                                                GeometryVars* vars) const {
     s->codeAppendf("float2 corner = %s[sk_InvocationID];", pts);
     vars->fCornerVars.fPoint = "corner";
@@ -87,7 +85,7 @@
     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 (    "float nwidth = (abs(n.x) + abs(n.y)) * (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());
@@ -96,7 +94,7 @@
     // 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 (    "nwidth = (abs(n.x) + abs(n.y)) * (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());
diff --git a/src/gpu/ccpr/GrCCPRTriangleShader.h b/src/gpu/ccpr/GrCCPRTriangleShader.h
index efc2521..64e5495 100644
--- a/src/gpu/ccpr/GrCCPRTriangleShader.h
+++ b/src/gpu/ccpr/GrCCPRTriangleShader.h
@@ -23,8 +23,7 @@
                                const TexelBufferHandle& pointsBuffer,
                                const char* pointId) const final;
 
-    void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* rtAdjust,
-                  const char* outputWind) const final;
+    void emitWind(GrGLSLShaderBuilder* s, const char* pts, const char* outputWind) const final;
 };
 
 /**
@@ -64,8 +63,7 @@
     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;
+                       const char* wind, 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;
diff --git a/src/gpu/effects/GrAtlasedShaderHelpers.h b/src/gpu/effects/GrAtlasedShaderHelpers.h
index 5277d31..ad90141 100644
--- a/src/gpu/effects/GrAtlasedShaderHelpers.h
+++ b/src/gpu/effects/GrAtlasedShaderHelpers.h
@@ -11,7 +11,7 @@
 #include "glsl/GrGLSLPrimitiveProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 static void append_index_uv_varyings(GrGLSLPrimitiveProcessor::EmitArgs& args,
                                      const char* inTexCoordsName,
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index 0eb417a..5cffaf4 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -13,7 +13,7 @@
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLUtil.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 class GrGLConicEffect : public GrGLSLGeometryProcessor {
 public:
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index ddf882e..d5f930f 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -14,7 +14,7 @@
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 class GrGLBitmapTextGeoProc : public GrGLSLGeometryProcessor {
 public:
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
index e1c733d..aafabde 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -16,7 +16,7 @@
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLUtil.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 // Assuming a radius of a little less than the diagonal of the fragment
 #define SK_DistanceFieldAAFactor     "0.65"
diff --git a/src/gpu/effects/GrShadowGeoProc.cpp b/src/gpu/effects/GrShadowGeoProc.cpp
index 682486b..ba337d7 100644
--- a/src/gpu/effects/GrShadowGeoProc.cpp
+++ b/src/gpu/effects/GrShadowGeoProc.cpp
@@ -11,7 +11,7 @@
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 class GrGLSLRRectShadowGeoProc : public GrGLSLGeometryProcessor {
 public:
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
index cb421ad..8ee9bbd 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -11,15 +11,31 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 void GrGLSLGeometryProcessor::emitCode(EmitArgs& args) {
-    GrGLSLVertexBuilder* vBuilder = args.fVertBuilder;
     GrGPArgs gpArgs;
     this->onEmitCode(args, &gpArgs);
-    vBuilder->transformToNormalizedDeviceSpace(gpArgs.fPositionVar, args.fRTAdjustName);
-    if (kFloat2_GrSLType == gpArgs.fPositionVar.getType() ||
-        kHalf2_GrSLType == gpArgs.fPositionVar.getType()) {
+    SkASSERT(kFloat2_GrSLType == gpArgs.fPositionVar.getType() ||
+             kFloat3_GrSLType == gpArgs.fPositionVar.getType());
+
+    GrGLSLVertexBuilder* vBuilder = args.fVertBuilder;
+    if (!args.fGP.willUseGeoShader()) {
+        // Emit the vertex position to the hardware in the normalized window coordinates it expects.
+        vBuilder->emitNormalizedSkPosition(gpArgs.fPositionVar.c_str(), args.fRTAdjustName,
+                                           gpArgs.fPositionVar.getType());
+    } else {
+        // Since we have a geometry shader, leave the vertex position in Skia device space for now.
+        // The geometry Shader will operate in device space, and then convert the final positions to
+        // normalized hardware window coordinates under the hood, once everything else has finished.
+        vBuilder->codeAppendf("sk_Position = float4(%s", gpArgs.fPositionVar.c_str());
+        if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
+            vBuilder->codeAppend(", 0");
+        }
+        vBuilder->codeAppend(", 1);");
+    }
+
+    if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
         args.fVaryingHandler->setNoPerspective();
     }
 }
diff --git a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp b/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp
deleted file mode 100644
index d9f34c2..0000000
--- a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrGLSLGeometryShaderBuilder.h"
-#include "GrGLSLProgramBuilder.h"
-#include "GrGLSLVarying.h"
-
-static const char* input_type_name(GrGLSLGeometryBuilder::InputType in) {
-    using InputType = GrGLSLGeometryBuilder::InputType;
-    switch (in) {
-        case InputType::kPoints: return "points";
-        case InputType::kLines: return "lines";
-        case InputType::kLinesAdjacency: return "lines_adjacency";
-        case InputType::kTriangles: return "triangles";
-        case InputType::kTrianglesAdjacency: return "triangles_adjacency";
-    }
-    SK_ABORT("invalid input type");
-    return "unknown_input";
-}
-
-static const char* output_type_name(GrGLSLGeometryBuilder::OutputType out) {
-    using OutputType = GrGLSLGeometryBuilder::OutputType;
-    switch (out) {
-        case OutputType::kPoints: return "points";
-        case OutputType::kLineStrip: return "line_strip";
-        case OutputType::kTriangleStrip: return "triangle_strip";
-    }
-    SK_ABORT("invalid output type");
-    return "unknown_output";
-}
-
-GrGLSLGeometryBuilder::GrGLSLGeometryBuilder(GrGLSLProgramBuilder* program)
-    : INHERITED(program)
-    , fNumInvocations(0) {
-}
-
-void GrGLSLGeometryBuilder::configure(InputType inputType, OutputType outputType, int maxVertices,
-                                      int numInvocations) {
-    SkASSERT(!this->isConfigured());
-    fNumInvocations = numInvocations;
-    this->addLayoutQualifier(input_type_name(inputType), kIn_InterfaceQualifier);
-    this->addLayoutQualifier(SkStringPrintf("invocations = %i", numInvocations).c_str(),
-                             kIn_InterfaceQualifier);
-    this->addLayoutQualifier(output_type_name(outputType), kOut_InterfaceQualifier);
-    this->addLayoutQualifier(SkStringPrintf("max_vertices = %i", maxVertices).c_str(),
-                             kOut_InterfaceQualifier);
-}
-
-void GrGLSLGeometryBuilder::onFinalize() {
-    SkASSERT(this->isConfigured());
-    fProgramBuilder->varyingHandler()->getGeomDecls(&this->inputs(), &this->outputs());
-}
diff --git a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h b/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h
deleted file mode 100644
index 04be530..0000000
--- a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGLSLGeometryShaderBuilder_DEFINED
-#define GrGLSLGeometryShaderBuilder_DEFINED
-
-#include "GrGLSLShaderBuilder.h"
-
-class GrGLSLVarying;
-
-class GrGLSLGeometryBuilder : public GrGLSLShaderBuilder {
-public:
-    GrGLSLGeometryBuilder(GrGLSLProgramBuilder* program);
-
-    enum class InputType {
-        kPoints,
-        kLines,
-        kLinesAdjacency,
-        kTriangles,
-        kTrianglesAdjacency
-    };
-
-    enum class OutputType {
-        kPoints,
-        kLineStrip,
-        kTriangleStrip
-    };
-
-    void configure(InputType, OutputType, int maxVertices, int numInvocations = 1);
-    bool isConfigured() const { return fNumInvocations; }
-
-private:
-    void onFinalize() override;
-
-    int fNumInvocations;
-
-    friend class GrGLProgramBuilder;
-
-    typedef GrGLSLShaderBuilder INHERITED;
-};
-
-#endif
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
index 0cb7e4d..02fa2e7 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
@@ -12,7 +12,7 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
 #include "glsl/GrGLSLUniformHandler.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 SkMatrix GrGLSLPrimitiveProcessor::GetTransformMatrix(const SkMatrix& localMatrix,
                                                       const GrCoordTransform& coordTransform) {
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 0457e71..83a437f 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -13,11 +13,10 @@
 #include "GrProgramDesc.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLGeometryShaderBuilder.h"
 #include "glsl/GrGLSLPrimitiveProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "glsl/GrGLSLXferProcessor.h"
 
 class GrShaderVar;
diff --git a/src/gpu/glsl/GrGLSLVertexGeoBuilder.cpp b/src/gpu/glsl/GrGLSLVertexGeoBuilder.cpp
new file mode 100644
index 0000000..1cf85f7
--- /dev/null
+++ b/src/gpu/glsl/GrGLSLVertexGeoBuilder.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 "GrGLSLVertexGeoBuilder.h"
+
+#include "GrGLSLProgramBuilder.h"
+#include "GrGLSLVarying.h"
+#include "GrTypes.h"
+
+void GrGLSLVertexGeoBuilder::emitNormalizedSkPosition(SkString* out, const char* devPos,
+                                                      const char* rtAdjustName,
+                                                      GrSLType devPosType) {
+    if (this->getProgramBuilder()->desc()->header().fSnapVerticesToPixelCenters) {
+        if (kFloat3_GrSLType == devPosType) {
+            const char* p = devPos;
+            out->appendf("{float2 _posTmp = float2(%s.x/%s.z, %s.y/%s.z);", p, p, p, p);
+        } else {
+            SkASSERT(kFloat2_GrSLType == devPosType);
+            out->appendf("{float2 _posTmp = %s;", devPos);
+        }
+        out->appendf("_posTmp = floor(_posTmp) + half2(0.5, 0.5);"
+                     "sk_Position = float4(_posTmp.x * %s.x + %s.y,"
+                                          "_posTmp.y * %s.z + %s.w, 0, 1);}",
+                     rtAdjustName, rtAdjustName, rtAdjustName, rtAdjustName);
+    } else if (kFloat3_GrSLType == devPosType) {
+        out->appendf("sk_Position = float4(dot(%s.xz, %s.xy), dot(%s.yz, %s.zw), 0, %s.z);",
+                     devPos, rtAdjustName, devPos, rtAdjustName, devPos);
+    } else {
+        SkASSERT(kFloat2_GrSLType == devPosType);
+        out->appendf("sk_Position = float4(%s.x * %s.x + %s.y, %s.y * %s.z + %s.w, 0, 1);",
+                     devPos, rtAdjustName, rtAdjustName, devPos, rtAdjustName, rtAdjustName);
+    }
+}
+
+void GrGLSLVertexBuilder::onFinalize() {
+    // We could have the GrGeometryProcessor do this, but its just easier to have it performed
+    // here. If we ever need to set variable pointsize, then we can reinvestigate.
+    if (this->getProgramBuilder()->desc()->header().fHasPointSize) {
+        this->codeAppend("sk_PointSize = 1.0;");
+    }
+    fProgramBuilder->varyingHandler()->getVertexDecls(&this->inputs(), &this->outputs());
+}
+
+static const char* input_type_name(GrGLSLGeometryBuilder::InputType in) {
+    using InputType = GrGLSLGeometryBuilder::InputType;
+    switch (in) {
+        case InputType::kPoints: return "points";
+        case InputType::kLines: return "lines";
+        case InputType::kLinesAdjacency: return "lines_adjacency";
+        case InputType::kTriangles: return "triangles";
+        case InputType::kTrianglesAdjacency: return "triangles_adjacency";
+    }
+    SK_ABORT("invalid input type");
+    return "unknown_input";
+}
+
+static const char* output_type_name(GrGLSLGeometryBuilder::OutputType out) {
+    using OutputType = GrGLSLGeometryBuilder::OutputType;
+    switch (out) {
+        case OutputType::kPoints: return "points";
+        case OutputType::kLineStrip: return "line_strip";
+        case OutputType::kTriangleStrip: return "triangle_strip";
+    }
+    SK_ABORT("invalid output type");
+    return "unknown_output";
+}
+
+void GrGLSLGeometryBuilder::configure(InputType inputType, OutputType outputType, int maxVertices,
+                                      int numInvocations) {
+    SkASSERT(!this->isConfigured());
+    fNumInvocations = numInvocations;
+    this->addLayoutQualifier(input_type_name(inputType), kIn_InterfaceQualifier);
+    this->addLayoutQualifier(SkStringPrintf("invocations = %i", numInvocations).c_str(),
+                             kIn_InterfaceQualifier);
+    this->addLayoutQualifier(output_type_name(outputType), kOut_InterfaceQualifier);
+    this->addLayoutQualifier(SkStringPrintf("max_vertices = %i", maxVertices).c_str(),
+                             kOut_InterfaceQualifier);
+}
+
+void GrGLSLGeometryBuilder::emitVertex(SkString* out, const char* devPos, const char* rtAdjustName,
+                                       GrSLType devPosType) {
+    this->emitNormalizedSkPosition(out, devPos, rtAdjustName, devPosType);
+    out->append("EmitVertex();");
+}
+
+void GrGLSLGeometryBuilder::endPrimitive() {
+    this->codeAppend("EndPrimitive();");
+}
+
+void GrGLSLGeometryBuilder::onFinalize() {
+    SkASSERT(this->isConfigured());
+    fProgramBuilder->varyingHandler()->getGeomDecls(&this->inputs(), &this->outputs());
+}
diff --git a/src/gpu/glsl/GrGLSLVertexGeoBuilder.h b/src/gpu/glsl/GrGLSLVertexGeoBuilder.h
new file mode 100644
index 0000000..c0a55a0
--- /dev/null
+++ b/src/gpu/glsl/GrGLSLVertexGeoBuilder.h
@@ -0,0 +1,88 @@
+/*
+ * 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 GrGLSLVertexGeoBuilder_DEFINED
+#define GrGLSLVertexGeoBuilder_DEFINED
+
+#include "GrGLSLShaderBuilder.h"
+
+/**
+ * Base class for vertex and geometry shader builders. This is the stage that computes input
+ * geometry for the rasterizer.
+ */
+class GrGLSLVertexGeoBuilder : public GrGLSLShaderBuilder {
+protected:
+    GrGLSLVertexGeoBuilder(GrGLSLProgramBuilder* program) : INHERITED(program) {}
+
+    void emitNormalizedSkPosition(const char* devPos, const char* rtAdjustName,
+                                  GrSLType devPosType = GrSLType::kFloat2_GrSLType) {
+        this->emitNormalizedSkPosition(&this->code(), devPos, rtAdjustName, devPosType);
+    }
+
+    void emitNormalizedSkPosition(SkString* out, const char* devPos, const char* rtAdjustName,
+                                  GrSLType devPosType = GrSLType::kFloat2_GrSLType);
+
+    friend class GrGLSLGeometryProcessor;
+
+    typedef GrGLSLShaderBuilder INHERITED;
+};
+
+
+class GrGLSLVertexBuilder : public GrGLSLVertexGeoBuilder {
+public:
+    GrGLSLVertexBuilder(GrGLSLProgramBuilder* program) : INHERITED(program) {}
+
+private:
+    void onFinalize() override;
+
+    friend class GrGLProgramBuilder;
+
+    typedef GrGLSLVertexGeoBuilder INHERITED;
+};
+
+
+class GrGLSLGeometryBuilder : public GrGLSLVertexGeoBuilder {
+public:
+    GrGLSLGeometryBuilder(GrGLSLProgramBuilder* program) : INHERITED(program) {}
+
+    enum class InputType {
+        kPoints,
+        kLines,
+        kLinesAdjacency,
+        kTriangles,
+        kTrianglesAdjacency
+    };
+
+    enum class OutputType {
+        kPoints,
+        kLineStrip,
+        kTriangleStrip
+    };
+
+    void configure(InputType, OutputType, int maxVertices, int numInvocations = 1);
+    bool isConfigured() const { return fNumInvocations; }
+
+    void emitVertex(const char* devPos, const char* rtAdjustName,
+                    GrSLType devPosType = GrSLType::kFloat2_GrSLType) {
+        this->emitVertex(&this->code(), devPos, rtAdjustName, devPosType);
+    }
+    void emitVertex(SkString* out, const char* devPos, const char* rtAdjustName,
+                    GrSLType devPosType = GrSLType::kFloat2_GrSLType);
+
+    void endPrimitive();
+
+private:
+    void onFinalize() override;
+
+    int fNumInvocations = 0;
+
+    friend class GrGLProgramBuilder;
+
+    typedef GrGLSLVertexGeoBuilder INHERITED;
+};
+
+#endif
diff --git a/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp b/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp
deleted file mode 100644
index 192417c..0000000
--- a/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrGLSLVertexShaderBuilder.h"
-#include "glsl/GrGLSLProgramBuilder.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#include "glsl/GrGLSLVarying.h"
-
-GrGLSLVertexBuilder::GrGLSLVertexBuilder(GrGLSLProgramBuilder* program)
-    : INHERITED(program) {
-}
-
-void GrGLSLVertexBuilder::transformToNormalizedDeviceSpace(const GrShaderVar& posVar,
-                                                           const char* rtAdjustName) {
-    // setup RT Uniform
-    if (this->getProgramBuilder()->desc()->header().fSnapVerticesToPixelCenters) {
-        if (kFloat3_GrSLType == posVar.getType()) {
-            const char* p = posVar.c_str();
-            this->codeAppendf("{float2 _posTmp = float2(%s.x/%s.z, %s.y/%s.z);", p, p, p, p);
-        } else {
-            SkASSERT(kFloat2_GrSLType == posVar.getType());
-            this->codeAppendf("{float2 _posTmp = %s;", posVar.c_str());
-        }
-        this->codeAppendf("_posTmp = floor(_posTmp) + half2(0.5, 0.5);"
-                          "sk_Position = float4(_posTmp.x * %s.x + %s.y,"
-                                               "_posTmp.y * %s.z + %s.w, 0, 1);}",
-                          rtAdjustName, rtAdjustName, rtAdjustName, rtAdjustName);
-    } else if (kFloat3_GrSLType == posVar.getType()) {
-        this->codeAppendf("sk_Position = float4(dot(%s.xz, %s.xy), dot(%s.yz, %s.zw), 0, %s.z);",
-                          posVar.c_str(), rtAdjustName,
-                          posVar.c_str(), rtAdjustName,
-                          posVar.c_str());
-    } else {
-        SkASSERT(kFloat2_GrSLType == posVar.getType());
-        this->codeAppendf("sk_Position = float4(%s.x * %s.x + %s.y, %s.y * %s.z + %s.w, 0, 1);",
-                          posVar.c_str(), rtAdjustName, rtAdjustName,
-                          posVar.c_str(), rtAdjustName, rtAdjustName);
-    }
-    // We could have the GrGeometryProcessor do this, but its just easier to have it performed
-    // here. If we ever need to set variable pointsize, then we can reinvestigate.
-    if (this->getProgramBuilder()->desc()->header().fHasPointSize) {
-        this->codeAppend("sk_PointSize = 1.0;");
-    }
-}
-
-void GrGLSLVertexBuilder::onFinalize() {
-    fProgramBuilder->varyingHandler()->getVertexDecls(&this->inputs(), &this->outputs());
-}
diff --git a/src/gpu/glsl/GrGLSLVertexShaderBuilder.h b/src/gpu/glsl/GrGLSLVertexShaderBuilder.h
deleted file mode 100644
index 5d71565..0000000
--- a/src/gpu/glsl/GrGLSLVertexShaderBuilder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGLSLVertexShader_DEFINED
-#define GrGLSLVertexShader_DEFINED
-
-#include "GrGLSLShaderBuilder.h"
-#include "GrGeometryProcessor.h"
-
-class GrGLSLVarying;
-
-// Enough precision to represent 1 / 2048 accurately in printf
-#define GR_SIGNIFICANT_POW2_DECIMAL_DIG 11
-
-class GrGLSLVertexBuilder : public GrGLSLShaderBuilder {
-public:
-    GrGLSLVertexBuilder(GrGLSLProgramBuilder* program);
-
-    void transformToNormalizedDeviceSpace(const GrShaderVar& posVar, const char* rtAdjustName);
-private:
-    void onFinalize() override;
-
-    friend class GrGLProgramBuilder;
-
-    typedef GrGLSLShaderBuilder INHERITED;
-};
-
-#endif
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 47c4045..0e37238 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -26,7 +26,7 @@
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "ops/GrMeshDrawOp.h"
 
 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index 070d979..6b014ff 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -22,7 +22,7 @@
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "ops/GrMeshDrawOp.h"
 
 using AAMode = GrDashOp::AAMode;
diff --git a/src/gpu/ops/GrMSAAPathRenderer.cpp b/src/gpu/ops/GrMSAAPathRenderer.cpp
index 2a3235d..27d78a7 100644
--- a/src/gpu/ops/GrMSAAPathRenderer.cpp
+++ b/src/gpu/ops/GrMSAAPathRenderer.cpp
@@ -23,7 +23,7 @@
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUtil.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "ops/GrMeshDrawOp.h"
 #include "ops/GrRectOpFactory.h"
 
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 99aadbc..a325660 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -21,7 +21,7 @@
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLUtil.h"
 #include "glsl/GrGLSLVarying.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "ops/GrMeshDrawOp.h"
 #include "ops/GrSimpleMeshDrawOpHelper.h"
 
diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp
index ed6f6f5..b89e198 100644
--- a/tests/GrMeshTest.cpp
+++ b/tests/GrMeshTest.cpp
@@ -19,7 +19,7 @@
 #include "GrResourceProvider.h"
 #include "GrResourceKey.h"
 #include "SkMakeUnique.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLVarying.h"
diff --git a/tests/GrPipelineDynamicStateTest.cpp b/tests/GrPipelineDynamicStateTest.cpp
index 6b518ab..d84afb1 100644
--- a/tests/GrPipelineDynamicStateTest.cpp
+++ b/tests/GrPipelineDynamicStateTest.cpp
@@ -19,7 +19,7 @@
 #include "GrRenderTargetContextPriv.h"
 #include "GrResourceProvider.h"
 #include "SkMakeUnique.h"
-#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLVarying.h"