Revert "Revert "Change how vertex/instance attributes are handled in geometry processors.""

This reverts commit 5045e501d2aec23e5f1e4b46346033ac3202c6b0.

TBR=csmartdalton@google.com

Change-Id: Ifbf5f1d8f8ef340fdc69653e931b6d68d4bf0854
Reviewed-on: https://skia-review.googlesource.com/135862
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 3237afe..566eebd 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -576,21 +576,21 @@
 
             GrGLSLVarying v(kHalf4_GrSLType);
             varyingHandler->addVarying("QuadEdge", &v);
-            vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.fInQuadEdge->name());
+            vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.kInQuadEdge.name());
 
             // Setup pass through color
-            varyingHandler->addPassThroughAttribute(qe.fInColor, args.fOutputColor);
+            varyingHandler->addPassThroughAttribute(qe.kInColor, args.fOutputColor);
 
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
             // Setup position
-            this->writeOutputPosition(vertBuilder, gpArgs, qe.fInPosition->name());
+            this->writeOutputPosition(vertBuilder, gpArgs, qe.kInPosition.name());
 
             // emit transforms
             this->emitTransforms(vertBuilder,
                                  varyingHandler,
                                  uniformHandler,
-                                 qe.fInPosition->asShaderVar(),
+                                 qe.kInPosition.asShaderVar(),
                                  qe.fLocalMatrix,
                                  args.fFPCoordTransformHandler);
 
@@ -646,21 +646,25 @@
             : INHERITED(kQuadEdgeEffect_ClassID)
             , fLocalMatrix(localMatrix)
             , fUsesLocalCoords(usesLocalCoords) {
-        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
-        fInQuadEdge = &this->addVertexAttrib("inQuadEdge", kHalf4_GrVertexAttribType);
+        this->setVertexAttributeCnt(3);
     }
 
-    const Attribute* fInPosition;
-    const Attribute* fInQuadEdge;
-    const Attribute* fInColor;
-    SkMatrix         fLocalMatrix;
-    bool             fUsesLocalCoords;
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kInPosition, kInColor, kInQuadEdge);
+    }
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
+    static constexpr Attribute kInQuadEdge = {"inQuadEdge", kHalf4_GrVertexAttribType};
+    SkMatrix fLocalMatrix;
+    bool fUsesLocalCoords;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute QuadEdgeEffect::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute QuadEdgeEffect::kInColor;
+constexpr GrPrimitiveProcessor::Attribute QuadEdgeEffect::kInQuadEdge;
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
 
@@ -795,12 +799,10 @@
             return;
         }
 
-        size_t vertexStride = gp->getVertexStride();
-
-        SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
-                         ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
-                         : vertexStride ==
-                                   sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
+        size_t vertexStride = fHelper.compatibleWithAlphaAsCoverage()
+                                      ? sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
+                                      : sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr);
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
         GrAAConvexTessellator tess;
 
@@ -901,9 +903,9 @@
             const GrBuffer* vertexBuffer;
             int firstVertex;
 
-            size_t vertexStride = quadProcessor->getVertexStride();
+            SkASSERT(sizeof(QuadVertex) == quadProcessor->debugOnly_vertexStride());
             QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
-                vertexStride, vertexCount, &vertexBuffer, &firstVertex));
+                    sizeof(QuadVertex), vertexCount, &vertexBuffer, &firstVertex));
 
             if (!verts) {
                 SkDebugf("Could not allocate vertices\n");
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index 5472f11..76bc812 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -242,12 +242,19 @@
     void onPrepareDraws(Target* target) override {
         using namespace GrDefaultGeoProcFactory;
 
+        size_t vertexStride = sizeof(SkPoint) + sizeof(GrColor);
         Color color(Color::kPremulGrColorAttribute_Type);
-        Coverage::Type coverageType = fHelper.compatibleWithAlphaAsCoverage()
-                                              ? Coverage::kSolid_Type
-                                              : Coverage::kAttribute_Type;
-        LocalCoords lc = fHelper.usesLocalCoords() ? LocalCoords::kHasExplicit_Type
-                                                   : LocalCoords::kUnused_Type;
+        Coverage::Type coverageType = Coverage::kSolid_Type;
+        if (!fHelper.compatibleWithAlphaAsCoverage()) {
+            coverageType = Coverage::kAttribute_Type;
+            vertexStride += sizeof(float);
+        }
+        LocalCoords lc = LocalCoords::kUnused_Type;
+        if (fHelper.usesLocalCoords()) {
+            lc = LocalCoords::kHasExplicit_Type;
+            vertexStride += sizeof(SkPoint);
+        }
+
         sk_sp<GrGeometryProcessor> gp =
                 GrDefaultGeoProcFactory::Make(color, coverageType, lc, SkMatrix::I());
         if (!gp) {
@@ -255,7 +262,7 @@
             return;
         }
 
-        size_t vertexStride = gp->getVertexStride();
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
         sk_sp<const GrBuffer> indexBuffer = get_index_buffer(target->resourceProvider());
         PatternHelper helper(GrPrimitiveType::kTriangles);
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index b62c14b..89dedc4 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -959,18 +959,16 @@
         const GrBuffer* vertexBuffer;
         int firstVertex;
 
-        size_t vertexStride = lineGP->getVertexStride();
+        SkASSERT(sizeof(LineVertex) == lineGP->debugOnly_vertexStride());
         int vertexCount = kLineSegNumVertices * lineCount;
-        LineVertex* verts = reinterpret_cast<LineVertex*>(
-            target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex));
+        LineVertex* verts = reinterpret_cast<LineVertex*>(target->makeVertexSpace(
+                sizeof(LineVertex), vertexCount, &vertexBuffer, &firstVertex));
 
         if (!verts|| !linesIndexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        SkASSERT(lineGP->getVertexStride() == sizeof(LineVertex));
-
         for (int i = 0; i < lineCount; ++i) {
             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
         }
@@ -1004,10 +1002,11 @@
 
         sk_sp<const GrBuffer> quadsIndexBuffer = get_quads_index_buffer(target->resourceProvider());
 
-        size_t vertexStride = sizeof(BezierVertex);
+        SkASSERT(sizeof(BezierVertex) == quadGP->debugOnly_vertexStride());
+        SkASSERT(sizeof(BezierVertex) == conicGP->debugOnly_vertexStride());
         int vertexCount = kQuadNumVertices * quadAndConicCount;
-        void *vertices = target->makeVertexSpace(vertexStride, vertexCount,
-                                                 &vertexBuffer, &firstVertex);
+        void* vertices = target->makeVertexSpace(sizeof(BezierVertex), vertexCount, &vertexBuffer,
+                                                 &firstVertex);
 
         if (!vertices || !quadsIndexBuffer) {
             SkDebugf("Could not allocate vertices\n");
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index 5659099..b6b7077 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -249,12 +249,10 @@
             return;
         }
 
-        size_t vertexStride = gp->getVertexStride();
-
-        SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
-                         ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
-                         : vertexStride ==
-                                   sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
+        size_t vertexStride = fHelper.compatibleWithAlphaAsCoverage()
+                                      ? sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
+                                      : sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr);
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
         int instanceCount = fPaths.count();
 
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index b9a6d88..3347227 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -268,11 +268,11 @@
         return;
     }
 
-    size_t vertexStride = gp->getVertexStride();
+    size_t vertexStride = fHelper.compatibleWithAlphaAsCoverage()
+                                  ? sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
+                                  : sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr);
 
-    SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
-                     ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
-                     : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
+    SkASSERT(vertexStride == gp->debugOnly_vertexStride());
     int innerVertexNum = 4;
     int outerVertexNum = this->miterStroke() ? 4 : 8;
     int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 1ea3ddf..fc5e580 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -312,8 +312,8 @@
     }
 
     flushInfo.fGlyphsToFlush = 0;
-    size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
-    SkASSERT(vertexStride == GrTextBlob::GetVertexStride(maskFormat, vmPerspective));
+    size_t vertexStride = GrTextBlob::GetVertexStride(maskFormat, vmPerspective);
+    SkASSERT(vertexStride == flushInfo.fGeometryProcessor->debugOnly_vertexStride());
 
     int glyphCount = this->numGlyphs();
     const GrBuffer* vertexBuffer;
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index cafb3e5..968d169 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -626,7 +626,15 @@
         }
 
         QuadHelper helper;
-        void* vertices = helper.init(target, gp->getVertexStride(), totalRectCount);
+        size_t vertexStride;
+        if (fullDash) {
+            vertexStride =
+                    SkPaint::kRound_Cap == fCap ? sizeof(DashCircleVertex) : sizeof(DashLineVertex);
+        } else {
+            vertexStride = sizeof(SkPoint);
+        }
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
+        void* vertices = helper.init(target, vertexStride, totalRectCount);
         if (!vertices) {
             return;
         }
@@ -638,15 +646,13 @@
 
             if (!draws[i].fLineDone) {
                 if (fullDash) {
-                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
-                                      draws[i].fStartOffset, draws[i].fDevBloatX,
-                                      draws[i].fDevBloatY, draws[i].fLineLength,
-                                      draws[i].fHalfDevStroke, draws[i].fIntervals[0],
-                                      draws[i].fIntervals[1], draws[i].fStrokeWidth,
-                                      capType, gp->getVertexStride());
+                    setup_dashed_rect(
+                            rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
+                            draws[i].fStartOffset, draws[i].fDevBloatX, draws[i].fDevBloatY,
+                            draws[i].fLineLength, draws[i].fHalfDevStroke, draws[i].fIntervals[0],
+                            draws[i].fIntervals[1], draws[i].fStrokeWidth, capType, vertexStride);
                 } else {
                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
-                    SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
                 }
                 curVIdx += 4;
@@ -655,15 +661,13 @@
 
             if (draws[i].fHasStartRect) {
                 if (fullDash) {
-                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
-                                      draws[i].fStartOffset, draws[i].fDevBloatX,
-                                      draws[i].fDevBloatY, draws[i].fIntervals[0],
-                                      draws[i].fHalfDevStroke, draws[i].fIntervals[0],
-                                      draws[i].fIntervals[1], draws[i].fStrokeWidth, capType,
-                                      gp->getVertexStride());
+                    setup_dashed_rect(
+                            rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
+                            draws[i].fStartOffset, draws[i].fDevBloatX, draws[i].fDevBloatY,
+                            draws[i].fIntervals[0], draws[i].fHalfDevStroke, draws[i].fIntervals[0],
+                            draws[i].fIntervals[1], draws[i].fStrokeWidth, capType, vertexStride);
                 } else {
                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
-                    SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
                 }
                 curVIdx += 4;
@@ -672,15 +676,13 @@
 
             if (draws[i].fHasEndRect) {
                 if (fullDash) {
-                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
-                                      draws[i].fStartOffset, draws[i].fDevBloatX,
-                                      draws[i].fDevBloatY, draws[i].fIntervals[0],
-                                      draws[i].fHalfDevStroke, draws[i].fIntervals[0],
-                                      draws[i].fIntervals[1], draws[i].fStrokeWidth, capType,
-                                      gp->getVertexStride());
+                    setup_dashed_rect(
+                            rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
+                            draws[i].fStartOffset, draws[i].fDevBloatX, draws[i].fDevBloatY,
+                            draws[i].fIntervals[0], draws[i].fHalfDevStroke, draws[i].fIntervals[0],
+                            draws[i].fIntervals[1], draws[i].fStrokeWidth, capType, vertexStride);
                 } else {
                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
-                    SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
                 }
                 curVIdx += 4;
@@ -840,12 +842,6 @@
 
     const char* name() const override { return "DashingCircleEffect"; }
 
-    const Attribute* inPosition() const { return fInPosition; }
-
-    const Attribute* inDashParams() const { return fInDashParams; }
-
-    const Attribute* inCircleParams() const { return fInCircleParams; }
-
     AAMode aaMode() const { return fAAMode; }
 
     GrColor color() const { return fColor; }
@@ -862,18 +858,27 @@
     DashingCircleEffect(GrColor, AAMode aaMode, const SkMatrix& localMatrix,
                         bool usesLocalCoords);
 
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kInPosition, kInDashParams, kInCircleParams);
+    }
+
     GrColor             fColor;
     SkMatrix            fLocalMatrix;
     bool                fUsesLocalCoords;
     AAMode              fAAMode;
-    const Attribute*    fInPosition;
-    const Attribute*    fInDashParams;
-    const Attribute*    fInCircleParams;
+
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInDashParams = {"inDashParams", kHalf3_GrVertexAttribType};
+    static constexpr Attribute kInCircleParams = {"inCircleParams", kHalf2_GrVertexAttribType};
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
+    friend class GLDashingCircleEffect;
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute DashingCircleEffect::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute DashingCircleEffect::kInDashParams;
+constexpr GrPrimitiveProcessor::Attribute DashingCircleEffect::kInCircleParams;
 
 //////////////////////////////////////////////////////////////////////////////
 
@@ -918,25 +923,25 @@
     // XY are dashPos, Z is dashInterval
     GrGLSLVarying dashParams(kHalf3_GrSLType);
     varyingHandler->addVarying("DashParam", &dashParams);
-    vertBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.inDashParams()->name());
+    vertBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.kInDashParams.name());
 
     // x refers to circle radius - 0.5, y refers to cicle's center x coord
     GrGLSLVarying circleParams(kHalf2_GrSLType);
     varyingHandler->addVarying("CircleParams", &circleParams);
-    vertBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.inCircleParams()->name());
+    vertBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.kInCircleParams.name());
 
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     // Setup pass through color
     this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
 
     // Setup position
-    this->writeOutputPosition(vertBuilder, gpArgs, dce.inPosition()->name());
+    this->writeOutputPosition(vertBuilder, gpArgs, dce.kInPosition.name());
 
     // emit transforms
     this->emitTransforms(vertBuilder,
                          varyingHandler,
                          uniformHandler,
-                         dce.inPosition()->asShaderVar(),
+                         dce.kInPosition.asShaderVar(),
                          dce.localMatrix(),
                          args.fFPCoordTransformHandler);
 
@@ -1009,9 +1014,7 @@
     , fLocalMatrix(localMatrix)
     , fUsesLocalCoords(usesLocalCoords)
     , fAAMode(aaMode) {
-    fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-    fInDashParams = &this->addVertexAttrib("inDashParams", kHalf3_GrVertexAttribType);
-    fInCircleParams = &this->addVertexAttrib("inCircleParams", kHalf2_GrVertexAttribType);
+    this->setVertexAttributeCnt(3);
 }
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
@@ -1049,12 +1052,6 @@
 
     const char* name() const override { return "DashingEffect"; }
 
-    const Attribute* inPosition() const { return fInPosition; }
-
-    const Attribute* inDashParams() const { return fInDashParams; }
-
-    const Attribute* inRectParams() const { return fInRectParams; }
-
     AAMode aaMode() const { return fAAMode; }
 
     GrColor color() const { return fColor; }
@@ -1071,18 +1068,28 @@
     DashingLineEffect(GrColor, AAMode aaMode, const SkMatrix& localMatrix,
                       bool usesLocalCoords);
 
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kInPosition, kInDashParams, kInRectParams);
+    }
+
     GrColor             fColor;
     SkMatrix            fLocalMatrix;
     bool                fUsesLocalCoords;
     AAMode              fAAMode;
-    const Attribute*    fInPosition;
-    const Attribute*    fInDashParams;
-    const Attribute*    fInRectParams;
+
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInDashParams = {"inDashParams", kHalf3_GrVertexAttribType};
+    static constexpr Attribute kInRectParams = {"inRect", kHalf4_GrVertexAttribType};
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
+    friend class GLDashingLineEffect;
+
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute DashingLineEffect::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute DashingLineEffect::kInDashParams;
+constexpr GrPrimitiveProcessor::Attribute DashingLineEffect::kInRectParams;
 
 //////////////////////////////////////////////////////////////////////////////
 
@@ -1120,26 +1127,26 @@
     // XY refers to dashPos, Z is the dash interval length
     GrGLSLVarying inDashParams(kFloat3_GrSLType);
     varyingHandler->addVarying("DashParams", &inDashParams);
-    vertBuilder->codeAppendf("%s = %s;", inDashParams.vsOut(), de.inDashParams()->name());
+    vertBuilder->codeAppendf("%s = %s;", inDashParams.vsOut(), de.kInDashParams.name());
 
     // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
     // respectively.
     GrGLSLVarying inRectParams(kFloat4_GrSLType);
     varyingHandler->addVarying("RectParams", &inRectParams);
-    vertBuilder->codeAppendf("%s = %s;", inRectParams.vsOut(), de.inRectParams()->name());
+    vertBuilder->codeAppendf("%s = %s;", inRectParams.vsOut(), de.kInRectParams.name());
 
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     // Setup pass through color
     this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
 
     // Setup position
-    this->writeOutputPosition(vertBuilder, gpArgs, de.inPosition()->name());
+    this->writeOutputPosition(vertBuilder, gpArgs, de.kInPosition.name());
 
     // emit transforms
     this->emitTransforms(vertBuilder,
                          varyingHandler,
                          uniformHandler,
-                         de.inPosition()->asShaderVar(),
+                         de.kInPosition.asShaderVar(),
                          de.localMatrix(),
                          args.fFPCoordTransformHandler);
 
@@ -1230,9 +1237,7 @@
     , fLocalMatrix(localMatrix)
     , fUsesLocalCoords(usesLocalCoords)
     , fAAMode(aaMode) {
-    fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-    fInDashParams = &this->addVertexAttrib("inDashParams", kHalf3_GrVertexAttribType);
-    fInRectParams = &this->addVertexAttrib("inRect", kHalf4_GrVertexAttribType);
+    this->setVertexAttributeCnt(3);
 }
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
@@ -1244,8 +1249,8 @@
                                    aaMode, GrTest::TestMatrix(d->fRandom),
                                    d->fRandom->nextBool());
 }
-#endif
 
+#endif
 //////////////////////////////////////////////////////////////////////////////
 
 static sk_sp<GrGeometryProcessor> make_dash_gp(GrColor color,
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index df6f81f..aa0963b 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -404,7 +404,7 @@
             gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
         }
 
-        SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
+        SkASSERT(gp->debugOnly_vertexStride() == sizeof(SkPoint));
 
         int instanceCount = fPaths.count();
 
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index fa08e55..47bad8b 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -122,9 +122,9 @@
     sk_sp<GrGeometryProcessor> gp(make_gp(this->hasColors(), this->color(), this->viewMatrix()));
 
     int instanceCount = fGeoData.count();
-    size_t vertexStride = gp->getVertexStride();
-    SkASSERT(vertexStride ==
-             sizeof(SkPoint) + sizeof(SkPoint) + (this->hasColors() ? sizeof(GrColor) : 0));
+    size_t vertexStride =
+            sizeof(SkPoint) + sizeof(SkPoint) + (this->hasColors() ? sizeof(GrColor) : 0);
+    SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
     QuadHelper helper;
     int numQuads = this->quadCount();
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index c968611..8f6ad97 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -140,10 +140,10 @@
     bool hasColorAttribute;
     bool hasLocalCoordsAttribute;
     sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute, &hasLocalCoordsAttribute);
-    size_t vertexStride = gp->getVertexStride();
 
-    SkASSERT(vertexStride == sizeof(SkPoint) + (hasColorAttribute ? sizeof(uint32_t) : 0) +
-                                     (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0));
+    size_t vertexStride = sizeof(SkPoint) + (hasColorAttribute ? sizeof(uint32_t) : 0) +
+                          (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0);
+    SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
     int instanceCount = fMeshes.count();
 
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 5903231..7bfa72e 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -62,19 +62,19 @@
                                                 latticeGP.fColorSpaceXform.get());
 
                 args.fVaryingHandler->emitAttributes(latticeGP);
-                this->writeOutputPosition(args.fVertBuilder, gpArgs, latticeGP.fPositions.name());
+                this->writeOutputPosition(args.fVertBuilder, gpArgs, latticeGP.kPositions.name());
                 this->emitTransforms(args.fVertBuilder,
                                      args.fVaryingHandler,
                                      args.fUniformHandler,
-                                     latticeGP.fTextureCoords.asShaderVar(),
+                                     latticeGP.kTextureCoords.asShaderVar(),
                                      args.fFPCoordTransformHandler);
                 args.fFragBuilder->codeAppend("float2 textureCoords;");
-                args.fVaryingHandler->addPassThroughAttribute(&latticeGP.fTextureCoords,
+                args.fVaryingHandler->addPassThroughAttribute(latticeGP.kTextureCoords,
                                                               "textureCoords");
                 args.fFragBuilder->codeAppend("float4 textureDomain;");
                 args.fVaryingHandler->addPassThroughAttribute(
-                        &latticeGP.fTextureDomain, "textureDomain", Interpolation::kCanBeFlat);
-                args.fVaryingHandler->addPassThroughAttribute(&latticeGP.fColors, args.fOutputColor,
+                        latticeGP.kTextureDomain, "textureDomain", Interpolation::kCanBeFlat);
+                args.fVaryingHandler->addPassThroughAttribute(latticeGP.kColors, args.fOutputColor,
                                                               Interpolation::kCanBeFlat);
                 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
                 args.fFragBuilder->appendTextureLookupAndModulate(
@@ -95,24 +95,31 @@
     LatticeGP(sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> csxf,
               GrSamplerState::Filter filter)
             : INHERITED(kLatticeGP_ClassID), fColorSpaceXform(std::move(csxf)) {
-        fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
         fSampler.reset(std::move(proxy), filter);
         this->addTextureSampler(&fSampler);
-        fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
-        fTextureDomain = this->addVertexAttrib("textureDomain", kFloat4_GrVertexAttribType);
-        fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
+        this->setVertexAttributeCnt(4);
     }
 
-    Attribute fPositions;
-    Attribute fTextureCoords;
-    Attribute fTextureDomain;
-    Attribute fColors;
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kPositions, kTextureCoords, kTextureDomain, kColors);
+    }
+
+    static constexpr Attribute kPositions = {"position", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kTextureDomain = {"textureDomain", kFloat4_GrVertexAttribType};
+    static constexpr Attribute kColors = {"color", kUByte4_norm_GrVertexAttribType};
+
     sk_sp<GrColorSpaceXform> fColorSpaceXform;
     TextureSampler fSampler;
 
     typedef GrGeometryProcessor INHERITED;
 };
 
+constexpr GrPrimitiveProcessor::Attribute LatticeGP::kPositions;
+constexpr GrPrimitiveProcessor::Attribute LatticeGP::kTextureCoords;
+constexpr GrPrimitiveProcessor::Attribute LatticeGP::kTextureDomain;
+constexpr GrPrimitiveProcessor::Attribute LatticeGP::kColors;
+
 class NonAALatticeOp final : public GrMeshDrawOp {
 private:
     using Helper = GrSimpleMeshDrawOpHelper;
@@ -200,7 +207,10 @@
             return;
         }
 
-        size_t vertexStride = gp->getVertexStride();
+        static constexpr size_t kVertexStide =
+                sizeof(SkPoint) + sizeof(SkPoint) + sizeof(SkRect) + sizeof(uint32_t);
+        SkASSERT(kVertexStide == gp->debugOnly_vertexStride());
+
         int patchCnt = fPatches.count();
         int numRects = 0;
         for (int i = 0; i < patchCnt; i++) {
@@ -213,7 +223,7 @@
 
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
         PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
+        void* vertices = helper.init(target, kVertexStide, indexBuffer.get(), kVertsPerRect,
                                      kIndicesPerRect, numRects);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
@@ -241,7 +251,7 @@
             static const Sk4f kFlipMuls(1.f, -1.f, 1.f, -1.f);
             while (patch.fIter->next(&srcR, &dstR)) {
                 auto vertices = reinterpret_cast<LatticeGP::Vertex*>(verts);
-                SkPointPriv::SetRectTriStrip(&vertices->fPosition, dstR, vertexStride);
+                SkPointPriv::SetRectTriStrip(&vertices->fPosition, dstR, kVertexStide);
                 Sk4f coords(SkIntToScalar(srcR.fLeft), SkIntToScalar(srcR.fTop),
                             SkIntToScalar(srcR.fRight), SkIntToScalar(srcR.fBottom));
                 Sk4f domain = coords + kDomainOffsets;
@@ -252,7 +262,7 @@
                     domain = SkNx_shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets);
                 }
                 SkPointPriv::SetRectTriStrip(&vertices->fTextureCoords, coords[0], coords[1],
-                                             coords[2], coords[3], vertexStride);
+                                             coords[2], coords[3], kVertexStide);
                 for (int j = 0; j < kVertsPerRect; ++j) {
                     vertices[j].fTextureDomain = {domain[0], domain[1], domain[2], domain[3]};
                 }
@@ -260,13 +270,13 @@
                 for (int j = 0; j < kVertsPerRect; ++j) {
                     vertices[j].fColor = patch.fColor;
                 }
-                verts += kVertsPerRect * vertexStride;
+                verts += kVertsPerRect * kVertexStide;
             }
 
             // If we didn't handle it above, apply the matrix here.
             if (!isScaleTranslate) {
                 SkPoint* positions = reinterpret_cast<SkPoint*>(patchVerts);
-                SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, positions, vertexStride,
+                SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, positions, kVertexStide,
                                                   kVertsPerRect * patch.fIter->numRectsToDraw());
             }
         }
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index 20bd599..d4ebedc 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -181,15 +181,16 @@
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
         }
-        SkASSERT(gp->getVertexStride() ==
-                 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
 
-        size_t vertexStride = gp->getVertexStride();
+        static constexpr size_t kVertexStride =
+                sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr);
+        SkASSERT(kVertexStride == gp->debugOnly_vertexStride());
+
         int rectCount = fRects.count();
 
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
         PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
+        void* vertices = helper.init(target, kVertexStride, indexBuffer.get(), kVertsPerRect,
                                      kIndicesPerRect, rectCount);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
@@ -198,8 +199,8 @@
 
         for (int i = 0; i < rectCount; i++) {
             intptr_t verts =
-                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
-            tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
+                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * kVertexStride;
+            tesselate(verts, kVertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
                       fRects[i].fRect, &fRects[i].fLocalQuad);
         }
         helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
@@ -311,13 +312,11 @@
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
         }
-        SkASSERT(fHasLocalRect
-                         ? gp->getVertexStride() ==
-                                   sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
-                         : gp->getVertexStride() ==
-                                   sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
+        size_t vertexStride = fHasLocalRect
+                                      ? sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
+                                      : sizeof(GrDefaultGeoProcFactory::PositionColorAttr);
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
-        size_t vertexStride = gp->getVertexStride();
         int rectCount = fRects.count();
 
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index 5e00e55..11c2f8c 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -152,9 +152,9 @@
                                                fViewMatrix);
         }
 
-        size_t vertexStride = gp->getVertexStride();
+        static constexpr size_t kVertexStride = sizeof(GrDefaultGeoProcFactory::PositionAttr);
 
-        SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
+        SkASSERT(kVertexStride == gp->debugOnly_vertexStride());
 
         int vertexCount = kVertsPerHairlineRect;
         if (fStrokeWidth > 0) {
@@ -165,7 +165,7 @@
         int firstVertex;
 
         void* verts =
-                target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
+                target->makeVertexSpace(kVertexStride, vertexCount, &vertexBuffer, &firstVertex);
 
         if (!verts) {
             SkDebugf("Could not allocate vertices\n");
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 7b0e462..1655b76 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -75,32 +75,26 @@
             : INHERITED(kCircleGeometryProcessor_ClassID)
             , fLocalMatrix(localMatrix)
             , fStroke(stroke) {
-        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
-        fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType);
+        int cnt = 3;
         if (clipPlane) {
-            fInClipPlane = &this->addVertexAttrib("inClipPlane", kHalf3_GrVertexAttribType);
-        } else {
-            fInClipPlane = nullptr;
+            fInClipPlane = {"inClipPlane", kHalf3_GrVertexAttribType};
+            ++cnt;
         }
         if (isectPlane) {
-            fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kHalf3_GrVertexAttribType);
-        } else {
-            fInIsectPlane = nullptr;
+            fInIsectPlane = {"inIsectPlane", kHalf3_GrVertexAttribType};
+            ++cnt;
         }
         if (unionPlane) {
-            fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kHalf3_GrVertexAttribType);
-        } else {
-            fInUnionPlane = nullptr;
+            fInUnionPlane = {"inUnionPlane", kHalf3_GrVertexAttribType};
+            ++cnt;
         }
         if (roundCaps) {
             SkASSERT(stroke);
             SkASSERT(clipPlane);
-            fInRoundCapCenters =
-                    &this->addVertexAttrib("inRoundCapCenters", kFloat4_GrVertexAttribType);
-        } else {
-            fInRoundCapCenters = nullptr;
+            fInRoundCapCenters = {"inRoundCapCenters", kFloat4_GrVertexAttribType};
+            ++cnt;
         }
+        this->setVertexAttributeCnt(cnt);
     }
 
     ~CircleGeometryProcessor() override {}
@@ -130,23 +124,22 @@
             // emit attributes
             varyingHandler->emitAttributes(cgp);
             fragBuilder->codeAppend("float4 circleEdge;");
-            varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
-            if (cgp.fInClipPlane) {
+            varyingHandler->addPassThroughAttribute(cgp.kInCircleEdge, "circleEdge");
+            if (cgp.fInClipPlane.isInitialized()) {
                 fragBuilder->codeAppend("half3 clipPlane;");
                 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
             }
-            if (cgp.fInIsectPlane) {
-                SkASSERT(cgp.fInClipPlane);
+            if (cgp.fInIsectPlane.isInitialized()) {
                 fragBuilder->codeAppend("half3 isectPlane;");
                 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
             }
-            if (cgp.fInUnionPlane) {
-                SkASSERT(cgp.fInClipPlane);
+            if (cgp.fInUnionPlane.isInitialized()) {
+                SkASSERT(cgp.fInClipPlane.isInitialized());
                 fragBuilder->codeAppend("half3 unionPlane;");
                 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
             }
             GrGLSLVarying capRadius(kFloat_GrSLType);
-            if (cgp.fInRoundCapCenters) {
+            if (cgp.fInRoundCapCenters.isInitialized()) {
                 fragBuilder->codeAppend("float4 roundCapCenters;");
                 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
                 varyingHandler->addVarying("capRadius", &capRadius,
@@ -154,20 +147,20 @@
                 // This is the cap radius in normalized space where the outer radius is 1 and
                 // circledEdge.w is the normalized inner radius.
                 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
-                                         cgp.fInCircleEdge->name());
+                                         cgp.kInCircleEdge.name());
             }
 
             // setup pass through color
-            varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
+            varyingHandler->addPassThroughAttribute(cgp.kInColor, args.fOutputColor);
 
             // Setup position
-            this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->name());
+            this->writeOutputPosition(vertBuilder, gpArgs, cgp.kInPosition.name());
 
             // emit transforms
             this->emitTransforms(vertBuilder,
                                  varyingHandler,
                                  uniformHandler,
-                                 cgp.fInPosition->asShaderVar(),
+                                 cgp.kInPosition.asShaderVar(),
                                  cgp.fLocalMatrix,
                                  args.fFPCoordTransformHandler);
 
@@ -181,22 +174,22 @@
                 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
             }
 
-            if (cgp.fInClipPlane) {
+            if (cgp.fInClipPlane.isInitialized()) {
                 fragBuilder->codeAppend(
                         "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
                         "clipPlane.z, 0.0, 1.0);");
-                if (cgp.fInIsectPlane) {
+                if (cgp.fInIsectPlane.isInitialized()) {
                     fragBuilder->codeAppend(
                             "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
                             "isectPlane.z, 0.0, 1.0);");
                 }
-                if (cgp.fInUnionPlane) {
+                if (cgp.fInUnionPlane.isInitialized()) {
                     fragBuilder->codeAppend(
                             "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
                             "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
                 }
                 fragBuilder->codeAppend("edgeAlpha *= clip;");
-                if (cgp.fInRoundCapCenters) {
+                if (cgp.fInRoundCapCenters.isInitialized()) {
                     // We compute coverage of the round caps as circles at the butt caps produced
                     // by the clip planes. The inverse of the clip planes is applied so that there
                     // is no double counting.
@@ -220,10 +213,10 @@
             uint16_t key;
             key = cgp.fStroke ? 0x01 : 0x0;
             key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
-            key |= cgp.fInClipPlane ? 0x04 : 0x0;
-            key |= cgp.fInIsectPlane ? 0x08 : 0x0;
-            key |= cgp.fInUnionPlane ? 0x10 : 0x0;
-            key |= cgp.fInRoundCapCenters ? 0x20 : 0x0;
+            key |= cgp.fInClipPlane.isInitialized() ? 0x04 : 0x0;
+            key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
+            key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
+            key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
             b->add32(key);
         }
 
@@ -237,19 +230,31 @@
         typedef GrGLSLGeometryProcessor INHERITED;
     };
 
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthInitializedAttribute(i, kInPosition, kInColor, kInCircleEdge, fInClipPlane,
+                                       fInIsectPlane, fInUnionPlane, fInRoundCapCenters);
+    }
+
     SkMatrix fLocalMatrix;
-    const Attribute* fInPosition;
-    const Attribute* fInColor;
-    const Attribute* fInCircleEdge;
-    const Attribute* fInClipPlane;
-    const Attribute* fInIsectPlane;
-    const Attribute* fInUnionPlane;
-    const Attribute* fInRoundCapCenters;
+
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
+    static constexpr Attribute kInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType};
+
+    // Optional attributes.
+    Attribute fInClipPlane;
+    Attribute fInIsectPlane;
+    Attribute fInUnionPlane;
+    Attribute fInRoundCapCenters;
+
     bool fStroke;
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute CircleGeometryProcessor::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute CircleGeometryProcessor::kInColor;
+constexpr GrPrimitiveProcessor::Attribute CircleGeometryProcessor::kInCircleEdge;
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
 
@@ -270,10 +275,7 @@
 public:
     ButtCapDashedCircleGeometryProcessor(const SkMatrix& localMatrix)
             : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
-        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
-        fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType);
-        fInDashParams = &this->addVertexAttrib("inDashParams", kFloat4_GrVertexAttribType);
+        this->setVertexAttributeCnt(4);
     }
 
     ~ButtCapDashedCircleGeometryProcessor() override {}
@@ -304,11 +306,11 @@
             // emit attributes
             varyingHandler->emitAttributes(bcscgp);
             fragBuilder->codeAppend("float4 circleEdge;");
-            varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
+            varyingHandler->addPassThroughAttribute(bcscgp.kInCircleEdge, "circleEdge");
 
             fragBuilder->codeAppend("float4 dashParams;");
             varyingHandler->addPassThroughAttribute(
-                    bcscgp.fInDashParams, "dashParams",
+                    bcscgp.kInDashParams, "dashParams",
                     GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
             GrGLSLVarying wrapDashes(kHalf4_GrSLType);
             varyingHandler->addVarying("wrapDashes", &wrapDashes,
@@ -316,7 +318,7 @@
             GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
             varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
                                        GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
-            vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams->name());
+            vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.kInDashParams.name());
             // Our fragment shader works in on/off intervals as specified by dashParams.xy:
             //     x = length of on interval, y = length of on + off.
             // There are two other parameters in dashParams.zw:
@@ -378,17 +380,17 @@
 
             // setup pass through color
             varyingHandler->addPassThroughAttribute(
-                    bcscgp.fInColor, args.fOutputColor,
+                    bcscgp.kInColor, args.fOutputColor,
                     GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
 
             // Setup position
-            this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition->name());
+            this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.kInPosition.name());
 
             // emit transforms
             this->emitTransforms(vertBuilder,
                                  varyingHandler,
                                  uniformHandler,
-                                 bcscgp.fInPosition->asShaderVar(),
+                                 bcscgp.kInPosition.asShaderVar(),
                                  bcscgp.fLocalMatrix,
                                  args.fFPCoordTransformHandler);
             GrShaderVar fnArgs[] = {
@@ -482,16 +484,24 @@
         typedef GrGLSLGeometryProcessor INHERITED;
     };
 
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kInPosition, kInColor, kInCircleEdge, kInDashParams);
+    }
+
     SkMatrix fLocalMatrix;
-    const Attribute* fInPosition;
-    const Attribute* fInColor;
-    const Attribute* fInCircleEdge;
-    const Attribute* fInDashParams;
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
+    static constexpr Attribute kInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType};
+    static constexpr Attribute kInDashParams = {"inDashParams", kFloat4_GrVertexAttribType};
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInColor;
+constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInCircleEdge;
+constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInDashParams;
 
 #if GR_TEST_UTILS
 sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
@@ -515,10 +525,7 @@
     EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
     : INHERITED(kEllipseGeometryProcessor_ClassID)
     , fLocalMatrix(localMatrix) {
-        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
-        fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kHalf2_GrVertexAttribType);
-        fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kHalf4_GrVertexAttribType);
+        this->setVertexAttributeCnt(4);
         fStroke = stroke;
     }
 
@@ -551,24 +558,24 @@
             GrGLSLVarying ellipseOffsets(kHalf2_GrSLType);
             varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
             vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
-                                     egp.fInEllipseOffset->name());
+                                     egp.kInEllipseOffset.name());
 
             GrGLSLVarying ellipseRadii(kHalf4_GrSLType);
             varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
-            vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->name());
+            vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.kInEllipseRadii.name());
 
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
             // setup pass through color
-            varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
+            varyingHandler->addPassThroughAttribute(egp.kInColor, args.fOutputColor);
 
             // Setup position
-            this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->name());
+            this->writeOutputPosition(vertBuilder, gpArgs, egp.kInPosition.name());
 
             // emit transforms
             this->emitTransforms(vertBuilder,
                                  varyingHandler,
                                  uniformHandler,
-                                 egp.fInPosition->asShaderVar(),
+                                 egp.kInPosition.asShaderVar(),
                                  egp.fLocalMatrix,
                                  args.fFPCoordTransformHandler);
 
@@ -616,10 +623,15 @@
         typedef GrGLSLGeometryProcessor INHERITED;
     };
 
-    const Attribute* fInPosition;
-    const Attribute* fInColor;
-    const Attribute* fInEllipseOffset;
-    const Attribute* fInEllipseRadii;
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kInPosition, kInColor, kInEllipseOffset, kInEllipseRadii);
+    }
+
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
+    static constexpr Attribute kInEllipseOffset = {"inEllipseOffset", kHalf2_GrVertexAttribType};
+    static constexpr Attribute kInEllipseRadii = {"inEllipseRadii", kHalf4_GrVertexAttribType};
+
     SkMatrix fLocalMatrix;
     bool fStroke;
 
@@ -627,6 +639,10 @@
 
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInColor;
+constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInEllipseOffset;
+constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInEllipseRadii;
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
 
@@ -655,11 +671,8 @@
     DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
             : INHERITED(kDIEllipseGeometryProcessor_ClassID)
             , fViewMatrix(viewMatrix) {
-        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
-        fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
-        fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kHalf2_GrVertexAttribType);
-        fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kHalf2_GrVertexAttribType);
         fStyle = style;
+        this->setVertexAttributeCnt(4);
     }
 
     ~DIEllipseGeometryProcessor() override {}
@@ -690,22 +703,20 @@
 
             GrGLSLVarying offsets0(kHalf2_GrSLType);
             varyingHandler->addVarying("EllipseOffsets0", &offsets0);
-            vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
-                                     diegp.fInEllipseOffsets0->name());
+            vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.kInEllipseOffsets0.name());
 
             GrGLSLVarying offsets1(kHalf2_GrSLType);
             varyingHandler->addVarying("EllipseOffsets1", &offsets1);
-            vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
-                                     diegp.fInEllipseOffsets1->name());
+            vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.kInEllipseOffsets1.name());
 
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-            varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
+            varyingHandler->addPassThroughAttribute(diegp.kInColor, args.fOutputColor);
 
             // Setup position
             this->writeOutputPosition(vertBuilder,
                                       uniformHandler,
                                       gpArgs,
-                                      diegp.fInPosition->name(),
+                                      diegp.kInPosition.name(),
                                       diegp.fViewMatrix,
                                       &fViewMatrixUniform);
 
@@ -713,7 +724,7 @@
             this->emitTransforms(vertBuilder,
                                  varyingHandler,
                                  uniformHandler,
-                                 diegp.fInPosition->asShaderVar(),
+                                 diegp.kInPosition.asShaderVar(),
                                  args.fFPCoordTransformHandler);
 
             // for outer curve
@@ -784,10 +795,17 @@
         typedef GrGLSLGeometryProcessor INHERITED;
     };
 
-    const Attribute* fInPosition;
-    const Attribute* fInColor;
-    const Attribute* fInEllipseOffsets0;
-    const Attribute* fInEllipseOffsets1;
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthAttribute(i, kInPosition, kInColor, kInEllipseOffsets0, kInEllipseOffsets1);
+    }
+
+    static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
+    static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
+    static constexpr Attribute kInEllipseOffsets0 = {"inEllipseOffsets0",
+                                                     kHalf2_GrVertexAttribType};
+    static constexpr Attribute kInEllipseOffsets1 = {"inEllipseOffsets1",
+                                                     kHalf2_GrVertexAttribType};
+
     SkMatrix fViewMatrix;
     DIEllipseStyle fStyle;
 
@@ -795,6 +813,10 @@
 
     typedef GrGeometryProcessor INHERITED;
 };
+constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInPosition;
+constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInColor;
+constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInEllipseOffsets0;
+constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInEllipseOffsets1;
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
 
@@ -1144,11 +1166,11 @@
         auto vertexCapCenters = [numPlanes](CircleVertex* v) {
             return (void*)(v->fHalfPlanes + numPlanes);
         };
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
-                                         (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
-                                         (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) +
-                                         (fRoundCaps ? 2 * sizeof(SkPoint) : 0));
+        size_t vertexStride = sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
+                              (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
+                              (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) +
+                              (fRoundCaps ? 2 * sizeof(SkPoint) : 0);
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
@@ -1632,12 +1654,12 @@
             SkScalar fPhaseAngle;
         };
 
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(CircleVertex));
+        static constexpr size_t kVertexStride = sizeof(CircleVertex);
+        SkASSERT(kVertexStride == gp->debugOnly_vertexStride());
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
-        char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
+        char* vertices = (char*)target->makeVertexSpace(kVertexStride, fVertCount, &vertexBuffer,
                                                         &firstVertex);
         if (!vertices) {
             SkDebugf("Could not allocate vertices\n");
@@ -1688,7 +1710,7 @@
             SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
             SkScalar halfWidth = 0.5f * bounds.width();
             auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) {
-                CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * vertexStride);
+                CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * kVertexStride);
                 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth};
                 v->fOffset = {x, y};
                 init_const_attrs_and_reflect(v);
@@ -1706,7 +1728,7 @@
             // Compute the vertices of the inner octagon.
             auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) {
                 CircleVertex* v =
-                        reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * vertexStride);
+                        reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * kVertexStride);
                 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius};
                 v->fOffset = {x * normInnerRadius, y * normInnerRadius};
                 init_const_attrs_and_reflect(v);
@@ -1732,7 +1754,7 @@
             }
 
             currStartVertex += circle_type_to_vert_count(true);
-            vertices += circle_type_to_vert_count(true) * vertexStride;
+            vertices += circle_type_to_vert_count(true) * kVertexStride;
         }
 
         GrMesh mesh(GrPrimitiveType::kTriangles);
@@ -1934,10 +1956,9 @@
         sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
 
         QuadHelper helper;
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(EllipseVertex));
+        SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
-                helper.init(target, vertexStride, fEllipses.count()));
+                helper.init(target, sizeof(EllipseVertex), fEllipses.count()));
         if (!verts) {
             return;
         }
@@ -2163,11 +2184,10 @@
         sk_sp<GrGeometryProcessor> gp(
                 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
 
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(DIEllipseVertex));
+        SkASSERT(sizeof(DIEllipseVertex) == gp->debugOnly_vertexStride());
         QuadHelper helper;
         DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
-                helper.init(target, vertexStride, fEllipses.count()));
+                helper.init(target, sizeof(DIEllipseVertex), fEllipses.count()));
         if (!verts) {
             return;
         }
@@ -2578,14 +2598,13 @@
         sk_sp<GrGeometryProcessor> gp(
                 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix));
 
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(sizeof(CircleVertex) == vertexStride);
+        SkASSERT(sizeof(CircleVertex) == gp->debugOnly_vertexStride());
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
 
-        CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
-                                                                     &vertexBuffer, &firstVertex);
+        CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
+                sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
         if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -2867,8 +2886,7 @@
         // Setup geometry processor
         sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
 
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(EllipseVertex));
+        SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
 
         // drop out the middle quad if we're stroked
         int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
@@ -2877,8 +2895,8 @@
 
         PatternHelper helper(GrPrimitiveType::kTriangles);
         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
-                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
-                            indicesPerInstance, fRRects.count()));
+                helper.init(target, sizeof(EllipseVertex), indexBuffer.get(),
+                            kVertsPerStandardRRect, indicesPerInstance, fRRects.count()));
         if (!verts || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 9e81d66..9a9814e 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -115,7 +115,8 @@
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
         }
-        SkASSERT(gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
+        static constexpr size_t kVertexStride = sizeof(GrDefaultGeoProcFactory::PositionColorAttr);
+        SkASSERT(kVertexStride == gp->debugOnly_vertexStride());
 
         int numRegions = fRegions.count();
         int numRects = 0;
@@ -126,12 +127,10 @@
         if (!numRects) {
             return;
         }
-        size_t vertexStride = gp->getVertexStride();
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
         PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices =
-                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerInstance,
-                            kIndicesPerInstance, numRects);
+        void* vertices = helper.init(target, kVertexStride, indexBuffer.get(), kVertsPerInstance,
+                                     kIndicesPerInstance, numRects);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -139,9 +138,9 @@
 
         intptr_t verts = reinterpret_cast<intptr_t>(vertices);
         for (int i = 0; i < numRegions; i++) {
-            tesselate_region(verts, vertexStride, fRegions[i].fColor, fRegions[i].fRegion);
+            tesselate_region(verts, kVertexStride, fRegions[i].fColor, fRegions[i].fRegion);
             int numRectsInRegion = fRegions[i].fRegion.computeRegionComplexity();
-            verts += numRectsInRegion * kVertsPerInstance * vertexStride;
+            verts += numRectsInRegion * kVertsPerInstance * kVertexStride;
         }
         helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index f61d11d..afb084d 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -576,13 +576,12 @@
         sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
 
         int instanceCount = fGeoData.count();
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(sizeof(CircleVertex) == vertexStride);
+        SkASSERT(sizeof(CircleVertex) == gp->debugOnly_vertexStride());
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
-        CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
-                                                                     &vertexBuffer, &firstVertex);
+        CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
+                sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
         if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index 4ffe478..a0c9f9c 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -351,8 +351,9 @@
         }
 
         // allocate vertices
-        size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
-        SkASSERT(vertexStride == sizeof(SkPoint) + sizeof(GrColor) + 2*sizeof(uint16_t));
+        static constexpr size_t kVertexStride =
+                sizeof(SkPoint) + sizeof(GrColor) + 2 * sizeof(uint16_t);
+        SkASSERT(kVertexStride == flushInfo.fGeometryProcessor->debugOnly_vertexStride());
 
         const GrBuffer* vertexBuffer;
 
@@ -361,7 +362,7 @@
         if (instanceCount > SK_MaxS32 / kVerticesPerQuad) {
             return;
         }
-        void* vertices = target->makeVertexSpace(vertexStride,
+        void* vertices = target->makeVertexSpace(kVertexStride,
                                                  kVerticesPerQuad * instanceCount,
                                                  &vertexBuffer,
                                                  &flushInfo.fVertexOffset);
@@ -476,13 +477,9 @@
             auto uploadTarget = target->deferredUploadTarget();
             fAtlas->setLastUseToken(shapeData->fID, uploadTarget->tokenTracker()->nextDrawToken());
 
-            this->writePathVertices(fAtlas,
-                                    offset,
-                                    args.fColor,
-                                    vertexStride,
-                                    args.fViewMatrix,
-                                    shapeData);
-            offset += kVerticesPerQuad * vertexStride;
+            this->writePathVertices(
+                    fAtlas, offset, args.fColor, kVertexStride, args.fViewMatrix, shapeData);
+            offset += kVerticesPerQuad * kVertexStride;
             flushInfo.fInstancesToFlush++;
         }
 
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 51dac5a..4c436c2 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -237,7 +237,7 @@
         return path;
     }
 
-    void draw(Target* target, const GrGeometryProcessor* gp) {
+    void draw(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
         SkASSERT(!fAntiAlias);
         GrResourceProvider* rp = target->resourceProvider();
         bool inverseFill = fShape.inverseFilled();
@@ -274,7 +274,7 @@
         vmi.mapRect(&clipBounds);
         bool isLinear;
         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
-        StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
+        StaticVertexAllocator allocator(vertexStride, rp, canMapVB);
         int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
                                                    false, GrColor(), false, &isLinear);
         if (count == 0) {
@@ -289,7 +289,7 @@
         fShape.addGenIDChangeListener(sk_make_sp<PathInvalidator>(key));
     }
 
-    void drawAA(Target* target, const GrGeometryProcessor* gp) {
+    void drawAA(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
         SkASSERT(fAntiAlias);
         SkPath path = getPath();
         if (path.isEmpty()) {
@@ -299,7 +299,7 @@
         path.transform(fViewMatrix);
         SkScalar tol = GrPathUtils::kDefaultTolerance;
         bool isLinear;
-        DynamicVertexAllocator allocator(gp->getVertexStride(), target);
+        DynamicVertexAllocator allocator(vertexStride, target);
         int count =
                 GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true, fColor,
                                                fHelper.compatibleWithAlphaAsCoverage(), &isLinear);
@@ -311,9 +311,12 @@
 
     void onPrepareDraws(Target* target) override {
         sk_sp<GrGeometryProcessor> gp;
+        size_t vertexStride;
         {
             using namespace GrDefaultGeoProcFactory;
 
+            vertexStride = sizeof(SkPoint);  // position
+
             Color color(fColor);
             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
                                                         ? LocalCoords::kUsePosition_Type
@@ -321,10 +324,12 @@
             Coverage::Type coverageType;
             if (fAntiAlias) {
                 color = Color(Color::kPremulGrColorAttribute_Type);
+                vertexStride += sizeof(uint32_t);
                 if (fHelper.compatibleWithAlphaAsCoverage()) {
                     coverageType = Coverage::kSolid_Type;
                 } else {
                     coverageType = Coverage::kAttribute_Type;
+                    vertexStride += 4;
                 }
             } else {
                 coverageType = Coverage::kSolid_Type;
@@ -340,10 +345,11 @@
         if (!gp.get()) {
             return;
         }
+        SkASSERT(vertexStride == gp->debugOnly_vertexStride());
         if (fAntiAlias) {
-            this->drawAA(target, gp.get());
+            this->drawAA(target, gp.get(), vertexStride);
         } else {
-            this->draw(target, gp.get());
+            this->draw(target, gp.get(), vertexStride);
         }
     }
 
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index e06d94f..a895230 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -163,16 +163,14 @@
                                      args.fUniformHandler,
                                      textureGP.fTextureCoords.asShaderVar(),
                                      args.fFPCoordTransformHandler);
-                args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors,
-                                                              args.fOutputColor,
-                                                              Interpolation::kCanBeFlat);
+                args.fVaryingHandler->addPassThroughAttribute(
+                        textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
                 args.fFragBuilder->codeAppend("float2 texCoord;");
-                args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords,
-                                                              "texCoord");
+                args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureCoords, "texCoord");
                 if (textureGP.fDomain.isInitialized()) {
                     args.fFragBuilder->codeAppend("float4 domain;");
                     args.fVaryingHandler->addPassThroughAttribute(
-                            &textureGP.fDomain, "domain",
+                            textureGP.fDomain, "domain",
                             GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
                     args.fFragBuilder->codeAppend(
                             "texCoord = clamp(texCoord, domain.xy, domain.zw);");
@@ -182,7 +180,7 @@
                     SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type());
                     SkASSERT(args.fShaderCaps->integerSupport());
                     args.fFragBuilder->codeAppend("int texIdx;");
-                    args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx, "texIdx",
+                    args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureIdx, "texIdx",
                                                                   Interpolation::kMustBeFlat);
                     args.fFragBuilder->codeAppend("switch (texIdx) {");
                     for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
@@ -316,12 +314,13 @@
         }
 
         if (perspective) {
-            fPositions = this->addVertexAttrib("position", kFloat3_GrVertexAttribType);
+            fPositions = {"position", kFloat3_GrVertexAttribType};
         } else {
-            fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
+            fPositions = {"position", kFloat2_GrVertexAttribType};
         }
-        fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
-        fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
+        fColors = {"color", kUByte4_norm_GrVertexAttribType};
+        fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType};
+        int vertexAttributeCnt = 3;
 
         if (samplerCnt > 1) {
             // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt
@@ -332,17 +331,26 @@
                 this->addTextureSampler(&fSamplers[i]);
             }
             SkASSERT(caps.integerSupport());
-            fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType);
+            fTextureIdx = {"textureIdx", kInt_GrVertexAttribType};
+            ++vertexAttributeCnt;
         }
         if (domain == Domain::kYes) {
-            fDomain = this->addVertexAttrib("domain", kFloat4_GrVertexAttribType);
+            fDomain = {"domain", kFloat4_GrVertexAttribType};
+            ++vertexAttributeCnt;
         }
         if (coverageAA) {
-            fAAEdges[0] = this->addVertexAttrib("aaEdge0", kFloat3_GrVertexAttribType);
-            fAAEdges[1] = this->addVertexAttrib("aaEdge1", kFloat3_GrVertexAttribType);
-            fAAEdges[2] = this->addVertexAttrib("aaEdge2", kFloat3_GrVertexAttribType);
-            fAAEdges[3] = this->addVertexAttrib("aaEdge3", kFloat3_GrVertexAttribType);
+            fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType};
+            fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType};
+            fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType};
+            fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType};
+            vertexAttributeCnt += 4;
         }
+        this->setVertexAttributeCnt(vertexAttributeCnt);
+    }
+
+    const Attribute& onVertexAttribute(int i) const override {
+        return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fTextureIdx, fDomain,
+                                       fAAEdges[0], fAAEdges[1], fAAEdges[2], fAAEdges[3]);
     }
 
     Attribute fPositions;
@@ -746,7 +754,7 @@
     template <typename Pos, MultiTexture MT, Domain D, GrAA AA>
     void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) {
         using Vertex = TextureGeometryProcessor::Vertex<Pos, MT, D, AA>;
-        SkASSERT(gp->getVertexStride() == sizeof(Vertex));
+        SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
         auto vertices = static_cast<Vertex*>(v);
         auto proxies = this->proxies();
         auto filters = this->filters();
@@ -787,10 +795,47 @@
 
         const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(),
                                                            target->detachAppliedClip());
+        using TessFn =
+                decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
+#define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA)                          \
+    {                                                                           \
+        &TextureOp::tess<Point, MT, Domain, AA>,                                \
+                sizeof(TextureGeometryProcessor::Vertex<Point, MT, Domain, AA>) \
+    }
+        static constexpr struct {
+            TessFn fTessFn;
+            size_t fVertexSize;
+        } kTessFnsAndVertexSizes[] = {
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo),
+                TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes),
+        };
+#undef TESS_FN_AND_VERTEX_SIZE
+        int tessFnIdx = 0;
+        tessFnIdx |= coverageAA      ? 0x1 : 0x0;
+        tessFnIdx |= fDomain         ? 0x2 : 0x0;
+        tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
+        tessFnIdx |= fPerspective    ? 0x8 : 0x0;
+
+        SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
+
         int vstart;
         const GrBuffer* vbuffer;
-        void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer,
-                                              &vstart);
+        void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
+                                              4 * fDraws.count(), &vbuffer, &vstart);
         if (!vdata) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -804,32 +849,7 @@
             ih[t] = 1.f / texture->height();
         }
 
-        using TessFn =
-                decltype(&TextureOp::tess<SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo>);
-        static constexpr TessFn kTessFns[] = {
-                &TextureOp::tess<SkPoint,  MultiTexture::kNo,  Domain::kNo,  GrAA::kNo>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kNo,  Domain::kNo,  GrAA::kYes>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kNo,  Domain::kYes, GrAA::kNo>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kNo,  Domain::kYes, GrAA::kYes>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kYes, Domain::kNo,  GrAA::kNo>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kYes, Domain::kNo,  GrAA::kYes>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kYes, Domain::kYes, GrAA::kNo>,
-                &TextureOp::tess<SkPoint,  MultiTexture::kYes, Domain::kYes, GrAA::kYes>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kNo,  Domain::kNo,  GrAA::kNo>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kNo,  Domain::kNo,  GrAA::kYes>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kNo,  Domain::kYes, GrAA::kNo>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kNo,  Domain::kYes, GrAA::kYes>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kNo,  GrAA::kNo>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kNo,  GrAA::kYes>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo>,
-                &TextureOp::tess<SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes>,
-        };
-        int tessFnIdx = 0;
-        tessFnIdx |= coverageAA      ? 0x1 : 0x0;
-        tessFnIdx |= fDomain         ? 0x2 : 0x0;
-        tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0;
-        tessFnIdx |= fPerspective    ? 0x8 : 0x0;
-        (this->*(kTessFns[tessFnIdx]))(vdata, iw, ih, gp.get());
+        (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, iw, ih, gp.get());
 
         GrPrimitiveType primitiveType =
                 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;