CCPR: Don't use instanced draw calls with geometry shaders

It isn't necessary for the vertex shader to know all the points
because everything happens in the geometry shader. It's simpler to use
regular vertex arrays instead.

Bug: skia:
Change-Id: I7bf83180476fbe0ab01492611cd72e72b3f7d4f2
Reviewed-on: https://skia-review.googlesource.com/82881
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index 424485d..92892c2 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -251,51 +251,45 @@
     GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend() ?
                      static_cast<GrGLGpu*>(state->gpu()) : nullptr;
 
-    bool isCubic = GrCCPRCoverageProcessor::RenderPassIsCubic(fView->fRenderPass);
-    GrMesh mesh(isCubic ?  GrPrimitiveType::kLinesAdjacency : GrPrimitiveType::kTriangles);
-    if (isCubic) {
-        if (fView->fCubicInstances.empty()) {
-            return;
-        }
+    GrCCPRCoverageProcessor proc(fView->fRenderPass);
+    SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat);)
+
+    SkSTArray<1, GrMesh, true> mesh;
+    if (GrCCPRCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) {
         sk_sp<GrBuffer> instBuff(rp->createBuffer(fView->fCubicInstances.count() *
                                                   sizeof(CubicInstance), kVertex_GrBufferType,
                                                   kDynamic_GrAccessPattern,
                                                   GrResourceProvider::kNoPendingIO_Flag |
                                                   GrResourceProvider::kRequireGpuMemory_Flag,
                                                   fView->fCubicInstances.begin()));
-        if (!instBuff) {
-            return;
+        if (!fView->fCubicInstances.empty() && instBuff) {
+            proc.appendMesh(instBuff.get(), fView->fCubicInstances.count(), 0, &mesh);
         }
-        mesh.setInstanced(instBuff.get(), fView->fCubicInstances.count(), 0, 4);
     } else {
-        if (fView->fTriangleInstances.empty()) {
-            return;
-        }
         sk_sp<GrBuffer> instBuff(rp->createBuffer(fView->fTriangleInstances.count() *
                                                   sizeof(TriangleInstance), kVertex_GrBufferType,
                                                   kDynamic_GrAccessPattern,
                                                   GrResourceProvider::kNoPendingIO_Flag |
                                                   GrResourceProvider::kRequireGpuMemory_Flag,
                                                   fView->fTriangleInstances.begin()));
-        if (!instBuff) {
-            return;
+        if (!fView->fTriangleInstances.empty() && instBuff) {
+            proc.appendMesh(instBuff.get(), fView->fTriangleInstances.count(), 0, &mesh);
         }
-        mesh.setInstanced(instBuff.get(), fView->fTriangleInstances.count(), 0, 3);
     }
 
     GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
                         SkBlendMode::kSrcOver);
 
-    GrCCPRCoverageProcessor ccprProc(fView->fRenderPass);
-    SkDEBUGCODE(ccprProc.enableDebugVisualizations(kDebugBloat);)
-
     if (glGpu) {
         glGpu->handleDirtyContext();
         GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
         GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
     }
 
-    state->rtCommandBuffer()->draw(pipeline, ccprProc, &mesh, nullptr, 1, this->bounds());
+    if (!mesh.empty()) {
+        SkASSERT(1 == mesh.count());
+        state->rtCommandBuffer()->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds());
+    }
 
     if (glGpu) {
         context->resetContext(kMisc_GrGLBackendState);
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
index 5775c5a..57b73bf 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
@@ -393,32 +393,28 @@
     fDynamicStatesScratchBuffer.reserve(1 + fScissorBatches.count());
 
     // Triangles.
-    auto constexpr kTrianglesGrPrimitiveType = GrCCPRCoverageProcessor::kTrianglesGrPrimitiveType;
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleHulls,
-                             kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
+                             &PrimitiveTallies::fTriangles);
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleEdges,
-                             kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
+                             &PrimitiveTallies::fTriangles);
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleCorners,
-                             kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
+                             &PrimitiveTallies::fTriangles);
 
     // Quadratics.
-    auto constexpr kQuadraticsGrPrimitiveType = GrCCPRCoverageProcessor::kQuadraticsGrPrimitiveType;
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticHulls,
-                             kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
+                             &PrimitiveTallies::fQuadratics);
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticCorners,
-                             kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
+                             &PrimitiveTallies::fQuadratics);
 
     // Cubics.
-    auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kCubicHulls,
-                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fCubics);
+                             &PrimitiveTallies::fCubics);
     this->drawMaskPrimitives(flushState, pipeline, RenderPass::kCubicCorners,
-                             kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fCubics);
+                             &PrimitiveTallies::fCubics);
 }
 
 void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
                                           GrCCPRCoverageProcessor::RenderPass renderPass,
-                                          GrPrimitiveType primType, int vertexCount,
                                           int PrimitiveTallies::* instanceType) const {
     using ScissorMode = GrCCPRCoverageOpsBuilder::ScissorMode;
     SkASSERT(pipeline.getScissorState().enabled());
@@ -426,11 +422,12 @@
     fMeshesScratchBuffer.reset();
     fDynamicStatesScratchBuffer.reset();
 
+    GrCCPRCoverageProcessor proc(renderPass);
+
     if (const int instanceCount = fInstanceCounts[(int)ScissorMode::kNonScissored].*instanceType) {
         SkASSERT(instanceCount > 0);
         const int baseInstance = fBaseInstances[(int)ScissorMode::kNonScissored].*instanceType;
-        GrMesh& mesh = fMeshesScratchBuffer.emplace_back(primType);
-        mesh.setInstanced(fInstanceBuffer.get(), instanceCount, baseInstance, vertexCount);
+        proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance, &fMeshesScratchBuffer);
         fDynamicStatesScratchBuffer.push_back().fScissorRect.setXYWH(0, 0, fDrawBounds.width(),
                                                                      fDrawBounds.height());
     }
@@ -444,8 +441,8 @@
                 continue;
             }
             SkASSERT(instanceCount > 0);
-            GrMesh& mesh = fMeshesScratchBuffer.emplace_back(primType);
-            mesh.setInstanced(fInstanceBuffer.get(), instanceCount, baseInstance, vertexCount);
+            proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance,
+                            &fMeshesScratchBuffer);
             fDynamicStatesScratchBuffer.push_back().fScissorRect = batch.fScissor;
             baseInstance += instanceCount;
         }
@@ -454,7 +451,6 @@
     SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
 
     if (!fMeshesScratchBuffer.empty()) {
-        GrCCPRCoverageProcessor proc(renderPass);
         SkASSERT(flushState->rtCommandBuffer());
         flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
                                             fDynamicStatesScratchBuffer.begin(),
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.h b/src/gpu/ccpr/GrCCPRCoverageOp.h
index 571e29a..7957586 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.h
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.h
@@ -158,9 +158,8 @@
                            const PrimitiveTallies baseInstances[kNumScissorModes],
                            const PrimitiveTallies endInstances[kNumScissorModes]);
 
-    void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&,
-                            const GrCCPRCoverageProcessor::RenderPass, GrPrimitiveType,
-                            int vertexCount, int PrimitiveTallies::* instanceType) const;
+    void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, GrCCPRCoverageProcessor::RenderPass,
+                            int PrimitiveTallies::* instanceType) const;
 
     sk_sp<GrBuffer> fInstanceBuffer;
     PrimitiveTallies fBaseInstances[kNumScissorModes];
@@ -168,8 +167,8 @@
     const SkTArray<ScissorBatch, true> fScissorBatches;
     const SkISize fDrawBounds;
 
-    mutable SkTArray<GrMesh> fMeshesScratchBuffer;
-    mutable SkTArray<GrPipeline::DynamicState> fDynamicStatesScratchBuffer;
+    mutable SkTArray<GrMesh, true> fMeshesScratchBuffer;
+    mutable SkTArray<GrPipeline::DynamicState, true> fDynamicStatesScratchBuffer;
 
     friend class GrCCPRCoverageOpsBuilder;
 
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
index 61e431a..c7b8039 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
@@ -13,32 +13,6 @@
 #include "ccpr/GrCCPRTriangleShader.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 
-GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(RenderPass renderPass)
-        : INHERITED(kGrCCPRCoverageProcessor_ClassID)
-        , fRenderPass(renderPass) {
-    if (RenderPassIsCubic(fRenderPass)) {
-        this->addInstanceAttrib("X", kFloat4_GrVertexAttribType);
-        this->addInstanceAttrib("Y", kFloat4_GrVertexAttribType);
-
-        SkASSERT(offsetof(CubicInstance, fX) ==
-                 this->getInstanceAttrib(InstanceAttribs::kX).fOffsetInRecord);
-        SkASSERT(offsetof(CubicInstance, fY) ==
-                 this->getInstanceAttrib(InstanceAttribs::kY).fOffsetInRecord);
-        SkASSERT(sizeof(CubicInstance) == this->getInstanceStride());
-    } else {
-        this->addInstanceAttrib("X", kFloat3_GrVertexAttribType);
-        this->addInstanceAttrib("Y", kFloat3_GrVertexAttribType);
-
-        SkASSERT(offsetof(TriangleInstance, fX) ==
-                 this->getInstanceAttrib(InstanceAttribs::kX).fOffsetInRecord);
-        SkASSERT(offsetof(TriangleInstance, fY) ==
-                 this->getInstanceAttrib(InstanceAttribs::kY).fOffsetInRecord);
-        SkASSERT(sizeof(TriangleInstance) == this->getInstanceStride());
-    }
-
-    this->setWillUseGeoShader();
-}
-
 void GrCCPRCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler,
                                                    SkString* code, const char* position,
                                                    const char* coverage, const char* wind) {
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.h b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
index 7ecb888..dfd2e24 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -15,6 +15,7 @@
 
 class GrGLSLPPFragmentBuilder;
 class GrGLSLShaderBuilder;
+class GrMesh;
 
 /**
  * This is the geometry processor for the simple convex primitive shapes (triangles and closed curve
@@ -26,22 +27,12 @@
  * 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).
  *
- * Draw calls are instanced. They use use the corresponding GrPrimitiveTypes as defined below.
- * Caller fills out the primitives' atlas-space vertices and control points in instance arrays
- * using the provided structs below. There are no vertex attribs.
+ * To draw a renderer pass, see appendMesh below.
  */
 class GrCCPRCoverageProcessor : public GrGeometryProcessor {
 public:
-    static constexpr GrPrimitiveType kTrianglesGrPrimitiveType = GrPrimitiveType::kTriangles;
-    static constexpr GrPrimitiveType kQuadraticsGrPrimitiveType = GrPrimitiveType::kTriangles;
-    static constexpr GrPrimitiveType kCubicsGrPrimitiveType = GrPrimitiveType::kLinesAdjacency;
-
-    enum class InstanceAttribs : int {
-        kX,
-        kY
-    };
-
-    struct TriangleInstance { // Also used by quadratics.
+    // Defines a single triangle or closed quadratic bezier, with transposed x,y point values.
+    struct TriangleInstance {
         float fX[3];
         float fY[3];
 
@@ -49,6 +40,7 @@
         void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans);
     };
 
+    // Defines a single closed cubic bezier, with transposed x,y point values.
     struct CubicInstance {
         float fX[4];
         float fY[4];
@@ -56,11 +48,9 @@
         void set(const SkPoint[4], float dx, float dy);
     };
 
-    /**
-     * 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.
-     */
+    // 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,
@@ -75,42 +65,44 @@
         kCubicHulls,
         kCubicCorners
     };
+    static bool RenderPassIsCubic(RenderPass);
+    static const char* RenderPassName(RenderPass);
 
-    static inline bool RenderPassIsCubic(RenderPass pass) {
-        switch (pass) {
-            case RenderPass::kTriangleHulls:
-            case RenderPass::kTriangleEdges:
-            case RenderPass::kTriangleCorners:
-            case RenderPass::kQuadraticHulls:
-            case RenderPass::kQuadraticCorners:
-                return false;
-            case RenderPass::kCubicHulls:
-            case RenderPass::kCubicCorners:
-                return true;
-        }
-        SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
-        return false;
+    GrCCPRCoverageProcessor(RenderPass pass)
+            : INHERITED(kGrCCPRCoverageProcessor_ClassID)
+            , fRenderPass(pass) {
+        this->initGS();
     }
 
-    static inline const char* RenderPassName(RenderPass pass) {
-        switch (pass) {
-            case RenderPass::kTriangleHulls: return "kTriangleHulls";
-            case RenderPass::kTriangleEdges: return "kTriangleEdges";
-            case RenderPass::kTriangleCorners: return "kTriangleCorners";
-            case RenderPass::kQuadraticHulls: return "kQuadraticHulls";
-            case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
-            case RenderPass::kCubicHulls: return "kCubicHulls";
-            case RenderPass::kCubicCorners: return "kCubicCorners";
-        }
-        SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
-        return "";
+    // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array
+    // of either TriangleInstance or CubicInstance, depending on this processor's RendererPass, with
+    // coordinates in the desired shape's final atlas-space position.
+    //
+    // NOTE: Quadratics use TriangleInstance since both have 3 points.
+    void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                    SkTArray<GrMesh, true>* out) {
+        this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out);
     }
 
-    /**
-     * 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.
-     */
+    // GrPrimitiveProcessor overrides.
+    const char* name() const override { return RenderPassName(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;
+
+#ifdef SK_DEBUG
+    // Increases the 1/2 pixel AA bloat by a factor of debugBloat and outputs color instead of
+    // coverage (coverage=+1 -> green, coverage=0 -> black, coverage=-1 -> red).
+    void enableDebugVisualizations(float debugBloat) { fDebugBloat = debugBloat; }
+    bool debugVisualizationsEnabled() const { return fDebugBloat > 0; }
+    float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; }
+#endif
+
+    // 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;
@@ -211,27 +203,6 @@
         GrGLSLVarying fWind{kHalf_GrSLType, GrGLSLVarying::Scope::kGeoToFrag};
     };
 
-    GrCCPRCoverageProcessor(RenderPass);
-
-    const char* name() const override { return RenderPassName(fRenderPass); }
-    SkString dumpInfo() const override {
-        return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
-    }
-    const Attribute& getInstanceAttrib(InstanceAttribs attribID) const {
-        return this->getAttrib((int)attribID);
-    }
-
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
-
-#ifdef SK_DEBUG
-    // Increases the 1/2 pixel AA bloat by a factor of debugBloat and outputs color instead of
-    // coverage (coverage=+1 -> green, coverage=0 -> black, coverage=-1 -> red).
-    void enableDebugVisualizations(float debugBloat) { fDebugBloat = debugBloat; }
-    bool debugVisualizationsEnabled() const { return fDebugBloat > 0; }
-    float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; }
-#endif
-
     class GSImpl;
 
 private:
@@ -239,6 +210,10 @@
     // accidentally bleed into neighbor pixels.
     static constexpr float kAABloatRadius = 0.491111f;
 
+    void initGS();
+    void appendGSMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                      SkTArray<GrMesh, true>* out);
+
     int numInputPoints() const {
         return RenderPassIsCubic(fRenderPass) ? 4 : 3;
     }
@@ -270,4 +245,34 @@
     (Y + dy).store(&fY);
 }
 
+inline bool GrCCPRCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
+    switch (pass) {
+        case RenderPass::kTriangleHulls:
+        case RenderPass::kTriangleEdges:
+        case RenderPass::kTriangleCorners:
+        case RenderPass::kQuadraticHulls:
+        case RenderPass::kQuadraticCorners:
+            return false;
+        case RenderPass::kCubicHulls:
+        case RenderPass::kCubicCorners:
+            return true;
+    }
+    SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
+    return false;
+}
+
+inline const char* GrCCPRCoverageProcessor::RenderPassName(RenderPass pass) {
+    switch (pass) {
+        case RenderPass::kTriangleHulls: return "kTriangleHulls";
+        case RenderPass::kTriangleEdges: return "kTriangleEdges";
+        case RenderPass::kTriangleCorners: return "kTriangleCorners";
+        case RenderPass::kQuadraticHulls: return "kQuadraticHulls";
+        case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
+        case RenderPass::kCubicHulls: return "kCubicHulls";
+        case RenderPass::kCubicCorners: return "kCubicCorners";
+    }
+    SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
+    return "";
+}
+
 #endif
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
index 5c9ba3b..60928b6 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
@@ -7,6 +7,7 @@
 
 #include "GrCCPRCoverageProcessor.h"
 
+#include "GrMesh.h"
 #include "glsl/GrGLSLVertexGeoBuilder.h"
 
 using Shader = GrCCPRCoverageProcessor::Shader;
@@ -26,17 +27,16 @@
     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
         const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
 
-        // Vertex shader.
-        GrGLSLVertexBuilder* v = args.fVertBuilder;
-        v->codeAppendf("float2 self = float2(%s[sk_VertexID], %s[sk_VertexID]);",
-                       proc.getInstanceAttrib(InstanceAttribs::kX).fName,
-                       proc.getInstanceAttrib(InstanceAttribs::kY).fName);
-        gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
+        // The vertex shader simply forwards transposed x or y values to the geometry shader.
+        SkASSERT(1 == proc.numAttribs());
+        gpArgs->fPositionVar.set(4 == proc.numInputPoints() ? kFloat4_GrSLType : kFloat3_GrSLType,
+                                 proc.getAttrib(0).fName);
 
         // Geometry shader.
         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
         this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName);
         varyingHandler->emitAttributes(proc);
+        varyingHandler->setNoPerspective();
         SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
 
         // Fragment shader.
@@ -52,12 +52,9 @@
         int numInputPoints = proc.numInputPoints();
         SkASSERT(3 == numInputPoints || 4 == numInputPoints);
 
-        g->codeAppendf("float%ix2 pts = float%ix2(", numInputPoints, numInputPoints);
-        for (int i = 0; i < numInputPoints; ++i) {
-            g->codeAppend (i ? ", " : "");
-            g->codeAppendf("sk_in[%i].sk_Position.xy", i);
-        }
-        g->codeAppend (");");
+        const char* posValues = (4 == numInputPoints) ? "sk_Position" : "sk_Position.xyz";
+        g->codeAppendf("float%ix2 pts = transpose(float2x%i(sk_in[0].%s, sk_in[1].%s));",
+                       numInputPoints, numInputPoints, posValues, posValues);
 
         GrShaderVar wind("wind", kHalf_GrSLType);
         g->declareGlobal(wind);
@@ -89,9 +86,8 @@
         Shader::GeometryVars vars;
         fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
         int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), vars);
-        InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
-                                                    : InputType::kLinesAdjacency;
-        g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
+        g->configure(InputType::kLines, OutputType::kTriangleStrip, maxPoints,
+                     fShader->getNumSegments());
     }
 
     virtual int onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
@@ -255,6 +251,17 @@
     }
 };
 
+void GrCCPRCoverageProcessor::initGS() {
+    if (RenderPassIsCubic(fRenderPass)) {
+        this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); // (See appendMesh.)
+        SkASSERT(sizeof(CubicInstance) == this->getVertexStride() * 2);
+    } else {
+        this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); // (See appendMesh.)
+        SkASSERT(sizeof(TriangleInstance) == this->getVertexStride() * 2);
+    }
+    this->setWillUseGeoShader();
+}
+
 GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::CreateGSImpl(std::unique_ptr<Shader> shader) {
     switch (shader->getGeometryType()) {
         case Shader::GeometryType::kHull:
@@ -267,3 +274,14 @@
     SK_ABORT("Unexpected Shader::GeometryType.");
     return nullptr;
 }
+
+void GrCCPRCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceCount,
+                                           int baseInstance, SkTArray<GrMesh, true>* out) {
+    // GSImpl doesn't actually make instanced draw calls. Instead, we feed transposed x,y point
+    // values to the GPU in a regular vertex array and draw kLines (see initGS). Then, each vertex
+    // invocation receives either the shape's x or y values as inputs, which it forwards to the
+    // geometry shader.
+    GrMesh& mesh = out->emplace_back(GrPrimitiveType::kLines);
+    mesh.setNonIndexedNonInstanced(instanceCount * 2);
+    mesh.setVertexData(instanceBuffer, baseInstance * 2);
+}
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
index ab27ffa..9c71042 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -16,27 +16,37 @@
 void GrGLSLGeometryProcessor::emitCode(EmitArgs& args) {
     GrGPArgs gpArgs;
     this->onEmitCode(args, &gpArgs);
-    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.
+        SkASSERT(kFloat2_GrSLType == gpArgs.fPositionVar.getType() ||
+                 kFloat3_GrSLType == gpArgs.fPositionVar.getType());
         vBuilder->emitNormalizedSkPosition(gpArgs.fPositionVar.c_str(), args.fRTAdjustName,
                                            gpArgs.fPositionVar.getType());
+        if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
+            args.fVaryingHandler->setNoPerspective();
+        }
     } 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.
+        // The subclass must call setNoPerspective on the varying handler, if applicable.
         vBuilder->codeAppendf("sk_Position = float4(%s", gpArgs.fPositionVar.c_str());
-        if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
-            vBuilder->codeAppend(", 0");
+        switch (gpArgs.fPositionVar.getType()) {
+            case kFloat_GrSLType:
+                vBuilder->codeAppend(", 0"); // fallthru.
+            case kFloat2_GrSLType:
+                vBuilder->codeAppend(", 0"); // fallthru.
+            case kFloat3_GrSLType:
+                vBuilder->codeAppend(", 1"); // fallthru.
+            case kFloat4_GrSLType:
+                vBuilder->codeAppend(");");
+                break;
+            default:
+                SK_ABORT("Invalid position var type");
+                break;
         }
-        vBuilder->codeAppend(", 1);");
-    }
-
-    if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
-        args.fVaryingHandler->setNoPerspective();
     }
 }