Convert GrMesh to a struct

Converts GrMesh to a struct and changes the names/semantics of its
fields to be more inline with their GL counterparts. Also renames the
"instancing" feature to "pattern", to avoid ambiguity with hardware
instancing.

Bug: skia:
Change-Id: Ia0999d4f9c83b5dd31f81b9bf4f36ed9abd26286
Reviewed-on: https://skia-review.googlesource.com/15157
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/GrGpuResourceRef.h b/include/gpu/GrGpuResourceRef.h
index d6ef6c1..3a170f4 100644
--- a/include/gpu/GrGpuResourceRef.h
+++ b/include/gpu/GrGpuResourceRef.h
@@ -163,6 +163,10 @@
         this->reset(resource);
     }
 
+    GrPendingIOResource(const GrPendingIOResource& that)
+        : GrPendingIOResource(that.get()) {
+    }
+
     void reset(T* resource) {
         if (resource) {
             switch (IO_TYPE) {
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 5ce8274..f8ac850 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -25,24 +25,6 @@
 #include "GrTexturePriv.h"
 #include "SkMathPriv.h"
 
-GrMesh& GrMesh::operator =(const GrMesh& di) {
-    fPrimitiveType  = di.fPrimitiveType;
-    fStartVertex    = di.fStartVertex;
-    fStartIndex     = di.fStartIndex;
-    fVertexCount    = di.fVertexCount;
-    fIndexCount     = di.fIndexCount;
-
-    fInstanceCount          = di.fInstanceCount;
-    fVerticesPerInstance    = di.fVerticesPerInstance;
-    fIndicesPerInstance     = di.fIndicesPerInstance;
-    fMaxInstancesPerDraw    = di.fMaxInstancesPerDraw;
-
-    fVertexBuffer.reset(di.vertexBuffer());
-    fIndexBuffer.reset(di.indexBuffer());
-
-    return *this;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 GrGpu::GrGpu(GrContext* context)
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 68d2619..b7fe048 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -25,8 +25,7 @@
 class GrContext;
 struct GrContextOptions;
 class GrGLContext;
-class GrMesh;
-class GrNonInstancedVertices;
+struct GrMesh;
 class GrPath;
 class GrPathRange;
 class GrPathRenderer;
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index 23a300c..fb54b2e 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -14,7 +14,7 @@
 class GrOpFlushState;
 class GrFixedClip;
 class GrGpu;
-class GrMesh;
+struct GrMesh;
 class GrPipeline;
 class GrPrimitiveProcessor;
 class GrRenderTarget;
diff --git a/src/gpu/GrMesh.h b/src/gpu/GrMesh.h
index 5d1ce6f..e2fd2df 100644
--- a/src/gpu/GrMesh.h
+++ b/src/gpu/GrMesh.h
@@ -11,169 +11,90 @@
 #include "GrBuffer.h"
 #include "GrGpuResourceRef.h"
 
-class GrNonInstancedMesh {
-public:
-    GrPrimitiveType primitiveType() const { return fPrimitiveType; }
-    int startVertex() const { return fStartVertex; }
-    int startIndex() const { return fStartIndex; }
-    int vertexCount() const { return fVertexCount; }
-    int indexCount() const { return fIndexCount; }
-    bool isIndexed() const { return fIndexCount > 0; }
-
-    const GrBuffer* vertexBuffer() const { return fVertexBuffer.get(); }
-    const GrBuffer* indexBuffer() const { return fIndexBuffer.get(); }
-
-protected:
-    GrPrimitiveType         fPrimitiveType;
-    int                     fStartVertex;
-    int                     fStartIndex;
-    int                     fVertexCount;
-    int                     fIndexCount;
-    GrPendingIOResource<const GrBuffer, kRead_GrIOType> fVertexBuffer;
-    GrPendingIOResource<const GrBuffer, kRead_GrIOType> fIndexBuffer;
-    friend class GrMesh;
-};
-
 /**
  * Used to communicate index and vertex buffers, counts, and offsets for a draw from GrOp to
  * GrGpu. It also holds the primitive type for the draw. TODO: Consider moving ownership of this
  * and draw-issuing responsibility to GrPrimitiveProcessor. The rest of the vertex info lives there
  * already (stride, attribute mappings).
  */
-class GrMesh : public GrNonInstancedMesh {
+struct GrMesh {
+    using PendingBuffer = GrPendingIOResource<const GrBuffer, kRead_GrIOType>;
+
+    GrPrimitiveType   fPrimitiveType;
+
+    PendingBuffer     fIndexBuffer;
+    int               fIndexCount = 0;
+    int               fBaseIndex = 0;
+
+    PendingBuffer     fVertexBuffer;
+    int               fVertexCount = 0;
+    int               fBaseVertex = 0;
+
+    int               fPatternRepeatCount = 1;
+    int               fMaxPatternRepetitionsInIndexBuffer = 1;
+
+    struct PatternBatch;
+    class PatternIterator;
+
+    SkDEBUGCODE(void validate() const;)
+};
+
+struct GrMesh::PatternBatch {
+    int   fBaseVertex;
+    int   fRepeatCount;
+};
+
+class GrMesh::PatternIterator {
 public:
-    GrMesh() {}
-    GrMesh(const GrMesh& di) { (*this) = di; }
-    GrMesh& operator =(const GrMesh& di);
-
-    void init(GrPrimitiveType primType, const GrBuffer* vertexBuffer, int startVertex,
-                int vertexCount) {
-        SkASSERT(vertexBuffer);
-        SkASSERT(vertexCount);
-        SkASSERT(startVertex >= 0);
-        fPrimitiveType = primType;
-        fVertexBuffer.reset(vertexBuffer);
-        fIndexBuffer.reset(nullptr);
-        fStartVertex = startVertex;
-        fStartIndex = 0;
-        fVertexCount = vertexCount;
-        fIndexCount = 0;
-        fInstanceCount = 0;
-        fVerticesPerInstance = 0;
-        fIndicesPerInstance = 0;
-        fMaxInstancesPerDraw = 0;
+    PatternIterator(const GrMesh& mesh, int repetitionIdx)
+        : fMesh(mesh)
+        , fRepetitionIdx(repetitionIdx) {
+        SkDEBUGCODE(mesh.validate());
     }
 
-    void initIndexed(GrPrimitiveType primType,
-                        const GrBuffer* vertexBuffer,
-                        const GrBuffer* indexBuffer,
-                        int startVertex,
-                        int startIndex,
-                        int vertexCount,
-                        int indexCount) {
-        SkASSERT(indexBuffer);
-        SkASSERT(vertexBuffer);
-        SkASSERT(indexCount);
-        SkASSERT(vertexCount);
-        SkASSERT(startIndex >= 0);
-        SkASSERT(startVertex >= 0);
-        fPrimitiveType = primType;
-        fVertexBuffer.reset(vertexBuffer);
-        fIndexBuffer.reset(indexBuffer);
-        fStartVertex = startVertex;
-        fStartIndex = startIndex;
-        fVertexCount = vertexCount;
-        fIndexCount = indexCount;
-        fInstanceCount = 0;
-        fVerticesPerInstance = 0;
-        fIndicesPerInstance = 0;
-        fMaxInstancesPerDraw = 0;
+    bool operator!=(const PatternIterator& that) {
+        SkASSERT(&fMesh == &that.fMesh);
+        return fRepetitionIdx != that.fRepetitionIdx;
     }
 
-
-    /** Variation of the above that may be used when the total number of instances may exceed
-        the number of instances supported by the index buffer. To be used with
-        nextInstances() to draw in max-sized batches.*/
-    void initInstanced(GrPrimitiveType primType,
-                        const GrBuffer* vertexBuffer,
-                        const GrBuffer* indexBuffer,
-                        int startVertex,
-                        int verticesPerInstance,
-                        int indicesPerInstance,
-                        int instanceCount,
-                        int maxInstancesPerDraw) {
-        SkASSERT(vertexBuffer);
-        SkASSERT(indexBuffer);
-        SkASSERT(instanceCount);
-        SkASSERT(verticesPerInstance);
-        SkASSERT(indicesPerInstance);
-        SkASSERT(startVertex >= 0);
-        fPrimitiveType = primType;
-        fVertexBuffer.reset(vertexBuffer);
-        fIndexBuffer.reset(indexBuffer);
-        fStartVertex = startVertex;
-        fStartIndex = 0;
-        fVerticesPerInstance = verticesPerInstance;
-        fIndicesPerInstance = indicesPerInstance;
-        fInstanceCount = instanceCount;
-        fVertexCount = instanceCount * fVerticesPerInstance;
-        fIndexCount = instanceCount * fIndicesPerInstance;
-        fMaxInstancesPerDraw = maxInstancesPerDraw;
+    const PatternBatch operator*() {
+        PatternBatch batch;
+        batch.fBaseVertex = fMesh.fBaseVertex + fRepetitionIdx * fMesh.fVertexCount;
+        batch.fRepeatCount = SkTMin(fMesh.fPatternRepeatCount - fRepetitionIdx,
+                                    fMesh.fMaxPatternRepetitionsInIndexBuffer);
+        return batch;
     }
 
-
-    /** These return 0 if initInstanced was not used to initialize the GrVertices. */
-    int verticesPerInstance() const { return fVerticesPerInstance; }
-    int indicesPerInstance() const { return fIndicesPerInstance; }
-    int instanceCount() const { return fInstanceCount; }
-
-    bool isInstanced() const { return fInstanceCount > 0; }
-
-    class Iterator {
-    public:
-        const GrNonInstancedMesh* init(const GrMesh& mesh) {
-            fMesh = &mesh;
-            if (mesh.fInstanceCount <= mesh.fMaxInstancesPerDraw) {
-                fInstancesRemaining = 0;
-                // Note, this also covers the non-instanced case!
-                return &mesh;
-            }
-            SkASSERT(mesh.isInstanced());
-            fInstanceBatch.fIndexBuffer.reset(mesh.fIndexBuffer.get());
-            fInstanceBatch.fVertexBuffer.reset(mesh.fVertexBuffer.get());
-            fInstanceBatch.fIndexCount = mesh.fMaxInstancesPerDraw *
-                                         mesh.fIndicesPerInstance;
-            fInstanceBatch.fVertexCount = mesh.fMaxInstancesPerDraw *
-                                          mesh.fVerticesPerInstance;
-            fInstanceBatch.fPrimitiveType = mesh.fPrimitiveType;
-            fInstanceBatch.fStartIndex = mesh.fStartIndex;
-            fInstanceBatch.fStartVertex = mesh.fStartVertex;
-            fInstancesRemaining = mesh.fInstanceCount - mesh.fMaxInstancesPerDraw;
-            return &fInstanceBatch;
-        }
-
-        const GrNonInstancedMesh* next() {
-            if (!fInstancesRemaining) {
-                return nullptr;
-            }
-            fInstanceBatch.fStartVertex += fInstanceBatch.fVertexCount;
-            int instances = SkTMin(fInstancesRemaining, fMesh->fMaxInstancesPerDraw);
-            fInstanceBatch.fIndexCount = instances * fMesh->fIndicesPerInstance;
-            fInstanceBatch.fVertexCount = instances * fMesh->fVerticesPerInstance;
-            fInstancesRemaining -= instances;
-            return &fInstanceBatch;
-        }
-    private:
-        GrNonInstancedMesh fInstanceBatch;
-        const GrMesh* fMesh;
-        int fInstancesRemaining;
-    };
+    void operator++() {
+        fRepetitionIdx = SkTMin(fRepetitionIdx + fMesh.fMaxPatternRepetitionsInIndexBuffer,
+                                fMesh.fPatternRepeatCount);
+    }
 
 private:
-    int                     fInstanceCount;
-    int                     fVerticesPerInstance;
-    int                     fIndicesPerInstance;
-    int                     fMaxInstancesPerDraw;
+    const GrMesh&    fMesh;
+    int              fRepetitionIdx;
 };
 
+inline GrMesh::PatternIterator begin(const GrMesh& mesh) {
+    return GrMesh::PatternIterator(mesh, 0);
+}
+
+inline GrMesh::PatternIterator end(const GrMesh& mesh) {
+    return GrMesh::PatternIterator(mesh, mesh.fPatternRepeatCount);
+}
+
+#ifdef SK_DEBUG
+inline void GrMesh::validate() const {
+    SkASSERT(!fIndexBuffer || fIndexCount > 0);
+    SkASSERT(fBaseIndex >= 0);
+
+    SkASSERT(fVertexBuffer);
+    SkASSERT(fVertexCount);
+    SkASSERT(fBaseVertex >= 0);
+
+    SkASSERT(fPatternRepeatCount > 0);
+    SkASSERT(fMaxPatternRepetitionsInIndexBuffer > 0);
+}
+#endif
+
 #endif
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index d7b9087..ad92abf 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -313,7 +313,7 @@
     return GrSurfaceProxy::MakeWrapped(std::move(texture));
 }
 
-const GrBuffer* GrResourceProvider::createInstancedIndexBuffer(const uint16_t* pattern,
+const GrBuffer* GrResourceProvider::createPatternedIndexBuffer(const uint16_t* pattern,
                                                                int patternSize,
                                                                int reps,
                                                                int vertCount,
@@ -356,7 +356,7 @@
     GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
     static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 };
 
-    return this->createInstancedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
+    return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
 }
 
 GrPath* GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index e22a0d8..14db3e6 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -106,9 +106,9 @@
     static const int kMinScratchTextureSize;
 
     /**
-     * Either finds and refs, or creates an index buffer for instanced drawing with a specific
-     * pattern if the index buffer is not found. If the return is non-null, the caller owns
-     * a ref on the returned GrBuffer.
+     * Either finds and refs, or creates an index buffer with a repeating pattern for drawing
+     * contiguous vertices of a repeated mesh. If the return is non-null, the caller owns a ref on
+     * the returned GrBuffer.
      *
      * @param pattern     the pattern of indices to repeat
      * @param patternSize size in bytes of the pattern
@@ -118,7 +118,7 @@
      *
      * @return The index buffer if successful, otherwise nullptr.
      */
-    const GrBuffer* findOrCreateInstancedIndexBuffer(const uint16_t* pattern,
+    const GrBuffer* findOrCreatePatternedIndexBuffer(const uint16_t* pattern,
                                                      int patternSize,
                                                      int reps,
                                                      int vertCount,
@@ -126,7 +126,7 @@
         if (GrBuffer* buffer = this->findAndRefTByUniqueKey<GrBuffer>(key)) {
             return buffer;
         }
-        return this->createInstancedIndexBuffer(pattern, patternSize, reps, vertCount, key);
+        return this->createPatternedIndexBuffer(pattern, patternSize, reps, vertCount, key);
     }
 
     /**
@@ -268,7 +268,7 @@
         return !SkToBool(fCache);
     }
 
-    const GrBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
+    const GrBuffer* createPatternedIndexBuffer(const uint16_t* pattern,
                                                int patternSize,
                                                int reps,
                                                int vertCount,
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 4aa1920..8d76fae 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1984,34 +1984,29 @@
 }
 
 void GrGLGpu::setupGeometry(const GrPrimitiveProcessor& primProc,
-                            const GrNonInstancedMesh& mesh,
-                            size_t* indexOffsetInBytes) {
-    const GrBuffer* vbuf = mesh.vertexBuffer();
-    SkASSERT(vbuf);
-    SkASSERT(!vbuf->isMapped());
-
+                            const GrBuffer* indexBuffer,
+                            const GrBuffer* vertexBuffer,
+                            int baseVertex) {
     GrGLAttribArrayState* attribState;
-    if (mesh.isIndexed()) {
-        SkASSERT(indexOffsetInBytes);
-
-        *indexOffsetInBytes = 0;
-        const GrBuffer* ibuf = mesh.indexBuffer();
-        SkASSERT(ibuf);
-        SkASSERT(!ibuf->isMapped());
-        *indexOffsetInBytes += ibuf->baseOffset();
-        attribState = fHWVertexArrayState.bindInternalVertexArray(this, ibuf);
+    if (indexBuffer) {
+        SkASSERT(indexBuffer);
+        SkASSERT(!indexBuffer->isMapped());
+        attribState = fHWVertexArrayState.bindInternalVertexArray(this, indexBuffer);
     } else {
         attribState = fHWVertexArrayState.bindInternalVertexArray(this);
     }
 
+    SkASSERT(vertexBuffer);
+    SkASSERT(!vertexBuffer->isMapped());
+
     int vaCount = primProc.numAttribs();
     if (vaCount > 0) {
 
         GrGLsizei stride = static_cast<GrGLsizei>(primProc.getVertexStride());
 
-        size_t vertexOffsetInBytes = stride * mesh.startVertex();
+        size_t vertexOffsetInBytes = stride * baseVertex;
 
-        vertexOffsetInBytes += vbuf->baseOffset();
+        vertexOffsetInBytes += vertexBuffer->baseOffset();
 
         uint32_t usedAttribArraysMask = 0;
         size_t offset = 0;
@@ -2022,7 +2017,7 @@
             GrVertexAttribType attribType = attrib.fType;
             attribState->set(this,
                              attribIndex,
-                             vbuf,
+                             vertexBuffer,
                              attribType,
                              stride,
                              reinterpret_cast<GrGLvoid*>(vertexOffsetInBytes + offset));
@@ -2646,7 +2641,7 @@
 
     bool hasPoints = false;
     for (int i = 0; i < meshCount; ++i) {
-        if (meshes[i].primitiveType() == kPoints_GrPrimitiveType) {
+        if (meshes[i].fPrimitiveType == kPoints_GrPrimitiveType) {
             hasPoints = true;
             break;
         }
@@ -2661,41 +2656,38 @@
         }
 
         const GrMesh& mesh = meshes[i];
-        GrMesh::Iterator iter;
-        const GrNonInstancedMesh* nonInstMesh = iter.init(mesh);
-        do {
-            size_t indexOffsetInBytes = 0;
-            this->setupGeometry(primProc, *nonInstMesh, &indexOffsetInBytes);
-            if (nonInstMesh->isIndexed()) {
-                GrGLvoid* indices =
-                    reinterpret_cast<GrGLvoid*>(indexOffsetInBytes +
-                                                sizeof(uint16_t) * nonInstMesh->startIndex());
-                // info.startVertex() was accounted for by setupGeometry.
+        for (GrMesh::PatternBatch batch : mesh) {
+            this->setupGeometry(primProc, mesh.fIndexBuffer.get(), mesh.fVertexBuffer.get(),
+                                batch.fBaseVertex);
+            if (const GrBuffer* indexBuffer = mesh.fIndexBuffer.get()) {
+                GrGLvoid* indices = reinterpret_cast<void*>(indexBuffer->baseOffset() +
+                                                            sizeof(uint16_t) * mesh.fBaseIndex);
+                // mesh.fBaseVertex was accounted for by setupGeometry.
                 if (this->glCaps().drawRangeElementsSupport()) {
                     // We assume here that the GrMeshDrawOps that generated the mesh used the full
                     // 0..vertexCount()-1 range.
                     int start = 0;
-                    int end = nonInstMesh->vertexCount() - 1;
-                    GL_CALL(DrawRangeElements(gPrimitiveType2GLMode[nonInstMesh->primitiveType()],
+                    int end = mesh.fVertexCount * batch.fRepeatCount - 1;
+                    GL_CALL(DrawRangeElements(gPrimitiveType2GLMode[mesh.fPrimitiveType],
                                               start, end,
-                                              nonInstMesh->indexCount(),
+                                              mesh.fIndexCount * batch.fRepeatCount,
                                               GR_GL_UNSIGNED_SHORT,
                                               indices));
                 } else {
-                    GL_CALL(DrawElements(gPrimitiveType2GLMode[nonInstMesh->primitiveType()],
-                                         nonInstMesh->indexCount(),
+                    GL_CALL(DrawElements(gPrimitiveType2GLMode[mesh.fPrimitiveType],
+                                         mesh.fIndexCount * batch.fRepeatCount,
                                          GR_GL_UNSIGNED_SHORT,
                                          indices));
                 }
             } else {
                 // Pass 0 for parameter first. We have to adjust glVertexAttribPointer() to account
-                // for startVertex in the DrawElements case. So we always rely on setupGeometry to
-                // have accounted for startVertex.
-                GL_CALL(DrawArrays(gPrimitiveType2GLMode[nonInstMesh->primitiveType()], 0,
-                                   nonInstMesh->vertexCount()));
+                // for mesh.fBaseVertex in the DrawElements case. So we always rely on setupGeometry
+                // to have accounted for mesh.fBaseVertex.
+                GL_CALL(DrawArrays(gPrimitiveType2GLMode[mesh.fPrimitiveType], 0,
+                                   mesh.fVertexCount * batch.fRepeatCount));
             }
             fStats.incNumDraws();
-        } while ((nonInstMesh = iter.next()));
+        }
     }
 
 #if SWAP_PER_DRAW
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 6108445..f192c2b 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -26,7 +26,6 @@
 
 class GrGLBuffer;
 class GrPipeline;
-class GrNonInstancedMesh;
 class GrSwizzle;
 
 namespace gr_instanced { class GLInstancedRendering; }
@@ -254,8 +253,9 @@
     // an into the index buffer. It does not account for vertices.startIndex() but rather the start
     // index is relative to the returned offset.
     void setupGeometry(const GrPrimitiveProcessor&,
-                       const GrNonInstancedMesh& mesh,
-                       size_t* indexOffsetInBytes);
+                       const GrBuffer* indexBuffer,
+                       const GrBuffer* vertexBuffer,
+                       int baseVertex);
 
     void flushBlend(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle&);
 
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index efc6622..f9e95c7 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -813,10 +813,13 @@
             extract_verts(tess, verts, vertexStride, fColor, idxs, canTweakAlphaForCoverage);
 
             GrMesh mesh;
-            mesh.initIndexed(kTriangles_GrPrimitiveType,
-                             vertexBuffer, indexBuffer,
-                             firstVertex, firstIndex,
-                             tess.numPts(), tess.numIndices());
+            mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+            mesh.fIndexBuffer.reset(indexBuffer);
+            mesh.fIndexCount = tess.numIndices();
+            mesh.fBaseIndex = firstIndex;
+            mesh.fVertexBuffer.reset(vertexBuffer);
+            mesh.fVertexCount = tess.numPts();
+            mesh.fBaseVertex = firstVertex;
             target->draw(gp.get(), this->pipeline(), mesh);
         }
     }
@@ -875,12 +878,14 @@
                 continue;
             }
 
-            const GrBuffer* vertexBuffer;
-            int firstVertex;
+            GrMesh mesh;
+            mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
 
+            const GrBuffer* vertexBuffer;
             size_t vertexStride = quadProcessor->getVertexStride();
             QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
-                vertexStride, vertexCount, &vertexBuffer, &firstVertex));
+                vertexStride, vertexCount, &vertexBuffer, &mesh.fBaseVertex));
+            mesh.fVertexBuffer.reset(vertexBuffer);
 
             if (!verts) {
                 SkDebugf("Could not allocate vertices\n");
@@ -888,26 +893,23 @@
             }
 
             const GrBuffer* indexBuffer;
-            int firstIndex;
-
-            uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
+            uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &mesh.fBaseIndex);
             if (!idxs) {
                 SkDebugf("Could not allocate indices\n");
                 return;
             }
+            mesh.fIndexBuffer.reset(indexBuffer);
 
             SkSTArray<kPreallocDrawCnt, Draw, true> draws;
             create_vertices(segments, fanPt, &draws, verts, idxs);
 
-            GrMesh mesh;
-
             for (int j = 0; j < draws.count(); ++j) {
                 const Draw& draw = draws[j];
-                mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
-                                 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
+                mesh.fIndexCount = draw.fIndexCnt;
+                mesh.fVertexCount = draw.fVertexCnt;
                 target->draw(quadProcessor.get(), this->pipeline(), mesh);
-                firstVertex += draw.fVertexCnt;
-                firstIndex += draw.fIndexCnt;
+                mesh.fBaseIndex += draw.fIndexCnt;
+                mesh.fBaseVertex += draw.fVertexCnt;
             }
         }
     }
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index 7bf62ec..ca91e94 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -41,7 +41,7 @@
     // clang-format on
 
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
-    return resourceProvider->findOrCreateInstancedIndexBuffer(
+    return resourceProvider->findOrCreatePatternedIndexBuffer(
             gFillAARectIdx, kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer,
             kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
 }
@@ -231,7 +231,7 @@
         size_t vertexStride = gp->getVertexStride();
 
         sk_sp<const GrBuffer> indexBuffer(get_index_buffer(target->resourceProvider()));
-        InstancedHelper helper;
+        PatternHelper helper;
         void* vertices =
                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
                             kVertsPerAAFillRect, kIndicesPerAAFillRect, fRectCnt);
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index 274e308..b8ec966 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -64,7 +64,7 @@
 
 static const GrBuffer* ref_quads_index_buffer(GrResourceProvider* resourceProvider) {
     GR_DEFINE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
-    return resourceProvider->findOrCreateInstancedIndexBuffer(
+    return resourceProvider->findOrCreatePatternedIndexBuffer(
         kQuadIdxBufPattern, kIdxsPerQuad, kQuadsNumInIdxBuffer, kQuadNumVertices,
         gQuadsIndexBufferKey);
 }
@@ -98,7 +98,7 @@
 
 static const GrBuffer* ref_lines_index_buffer(GrResourceProvider* resourceProvider) {
     GR_DEFINE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
-    return resourceProvider->findOrCreateInstancedIndexBuffer(
+    return resourceProvider->findOrCreatePatternedIndexBuffer(
         kLineSegIdxBufPattern, kIdxsPerLineSeg,  kLineSegsNumInIdxBuffer, kLineSegNumVertices,
         gLinesIndexBufferKey);
 }
@@ -861,9 +861,14 @@
         }
 
         GrMesh mesh;
-        mesh.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, linesIndexBuffer.get(),
-                           firstVertex, kLineSegNumVertices, kIdxsPerLineSeg, lineCount,
-                           kLineSegsNumInIdxBuffer);
+        mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+        mesh.fIndexBuffer.reset(linesIndexBuffer.get());
+        mesh.fIndexCount = kIdxsPerLineSeg;
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = kLineSegNumVertices;
+        mesh.fBaseVertex = firstVertex;
+        mesh.fPatternRepeatCount = lineCount;
+        mesh.fMaxPatternRepetitionsInIndexBuffer = kLineSegsNumInIdxBuffer;
         target->draw(lineGP.get(), this->pipeline(), mesh);
     }
 
@@ -918,18 +923,28 @@
 
         if (quadCount > 0) {
             GrMesh mesh;
-            mesh.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer.get(),
-                               firstVertex, kQuadNumVertices, kIdxsPerQuad, quadCount,
-                               kQuadsNumInIdxBuffer);
+            mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+            mesh.fIndexBuffer.reset(quadsIndexBuffer.get());
+            mesh.fIndexCount = kIdxsPerQuad;
+            mesh.fVertexBuffer.reset(vertexBuffer);
+            mesh.fVertexCount = kQuadNumVertices;
+            mesh.fBaseVertex = firstVertex;
+            mesh.fPatternRepeatCount = quadCount;
+            mesh.fMaxPatternRepetitionsInIndexBuffer = kQuadsNumInIdxBuffer;
             target->draw(quadGP.get(), this->pipeline(), mesh);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
         if (conicCount > 0) {
             GrMesh mesh;
-            mesh.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer.get(),
-                               firstVertex, kQuadNumVertices, kIdxsPerQuad, conicCount,
-                               kQuadsNumInIdxBuffer);
+            mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+            mesh.fIndexBuffer.reset(quadsIndexBuffer.get());
+            mesh.fIndexCount = kIdxsPerQuad;
+            mesh.fVertexBuffer.reset(vertexBuffer);
+            mesh.fVertexCount = kQuadNumVertices;
+            mesh.fBaseVertex = firstVertex;
+            mesh.fPatternRepeatCount = conicCount;
+            mesh.fMaxPatternRepetitionsInIndexBuffer = kQuadsNumInIdxBuffer;
             target->draw(conicGP.get(), this->pipeline(), mesh);
         }
     }
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index 2a5464c..6c94793 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -189,27 +189,30 @@
         if (vertexCount == 0 || indexCount == 0) {
             return;
         }
-        const GrBuffer* vertexBuffer;
+
         GrMesh mesh;
-        int firstVertex;
+        mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+        const GrBuffer* vertexBuffer;
         void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
-                                              &firstVertex);
+                                              &mesh.fBaseVertex);
         if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = vertexCount;
         memcpy(verts, vertices, vertexCount * vertexStride);
 
         const GrBuffer* indexBuffer;
-        int firstIndex;
-        uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
+        uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &mesh.fBaseIndex);
         if (!idxs) {
             SkDebugf("Could not allocate indices\n");
             return;
         }
+        mesh.fIndexBuffer.reset(indexBuffer);
+        mesh.fIndexCount = indexCount;
         memcpy(idxs, indices, indexCount * sizeof(uint16_t));
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, vertexCount, indexCount);
+
         target->draw(gp, this->pipeline(), mesh);
     }
 
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index 8b47f5e..cf16d1b 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -262,7 +262,7 @@
 
     const sk_sp<const GrBuffer> indexBuffer(
             GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
-    InstancedHelper helper;
+    PatternHelper helper;
     void* vertices =
             helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
                         verticesPerInstance, indicesPerInstance, instanceCount);
@@ -312,7 +312,7 @@
         // clang-format on
         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
         GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
-        return resourceProvider->findOrCreateInstancedIndexBuffer(
+        return resourceProvider->findOrCreatePatternedIndexBuffer(
                 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
                 gMiterIndexBufferKey);
     } else {
@@ -377,7 +377,7 @@
         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
 
         GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
-        return resourceProvider->findOrCreateInstancedIndexBuffer(
+        return resourceProvider->findOrCreatePatternedIndexBuffer(
                 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
                 gBevelIndexBufferKey);
     }
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 64710e7..5271d42 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -149,9 +149,14 @@
     GrMesh mesh;
     int maxGlyphsPerDraw =
             static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
-    mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
-                       flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerGlyph,
-                       kIndicesPerGlyph, flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
+    mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+    mesh.fIndexBuffer.reset(flushInfo->fIndexBuffer.get());
+    mesh.fIndexCount = kIndicesPerGlyph;
+    mesh.fVertexBuffer.reset(flushInfo->fVertexBuffer.get());
+    mesh.fVertexCount = kVerticesPerGlyph;
+    mesh.fBaseVertex = flushInfo->fVertexOffset;
+    mesh.fPatternRepeatCount = flushInfo->fGlyphsToFlush;
+    mesh.fMaxPatternRepetitionsInIndexBuffer = maxGlyphsPerDraw;
     target->draw(flushInfo->fGeometryProcessor.get(), this->pipeline(), mesh);
     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
     flushInfo->fGlyphsToFlush = 0;
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 762084c..b2baf2e 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -251,12 +251,15 @@
         }
 
         GrMesh mesh;
+        mesh.fPrimitiveType = primitiveType;
         if (isIndexed) {
-            mesh.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
-                             vertexOffset, indexOffset);
-        } else {
-            mesh.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
+            mesh.fIndexBuffer.reset(indexBuffer);
+            mesh.fIndexCount = indexOffset;
+            mesh.fBaseIndex = firstIndex;
         }
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = vertexOffset;
+        mesh.fBaseVertex = firstVertex;
         target->draw(gp.get(), this->pipeline(), mesh);
 
         // put back reserves
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index c67233c..aa90db9 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -236,13 +236,15 @@
     }
 
     GrMesh mesh;
+    mesh.fPrimitiveType = this->primitiveType();
     if (indices) {
-        mesh.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex, firstIndex,
-                         fVertexCount, fIndexCount);
-
-    } else {
-        mesh.init(this->primitiveType(), vertexBuffer, firstVertex, fVertexCount);
+        mesh.fIndexBuffer.reset(indexBuffer);
+        mesh.fIndexCount = fIndexCount;
+        mesh.fBaseIndex = firstIndex;
     }
+    mesh.fVertexBuffer.reset(vertexBuffer);
+    mesh.fVertexCount = fVertexCount;
+    mesh.fBaseVertex = firstVertex;
     target->draw(gp.get(), this->pipeline(), mesh);
 }
 
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 4b8f5e3..0ba0164 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -86,7 +86,7 @@
         }
 
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
+        PatternHelper helper;
         void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
                                      indexBuffer.get(), kVertsPerRect, kIndicesPerRect, numRects);
         if (!vertices || !indexBuffer) {
diff --git a/src/gpu/ops/GrMSAAPathRenderer.cpp b/src/gpu/ops/GrMSAAPathRenderer.cpp
index 8e32006..f4d14cd 100644
--- a/src/gpu/ops/GrMSAAPathRenderer.cpp
+++ b/src/gpu/ops/GrMSAAPathRenderer.cpp
@@ -413,14 +413,16 @@
             SkASSERT(lineVertexStride == lineGP->getVertexStride());
 
             GrMesh lineMeshes;
+            lineMeshes.fPrimitiveType = primitiveType;
             if (fIsIndexed) {
-                lineMeshes.initIndexed(primitiveType, lineVertexBuffer, lineIndexBuffer,
-                                         firstLineVertex, firstLineIndex, lineVertexOffset,
-                                         lineIndexOffset);
-            } else {
-                lineMeshes.init(primitiveType, lineVertexBuffer, firstLineVertex,
-                                  lineVertexOffset);
+                lineMeshes.fIndexBuffer.reset(lineIndexBuffer);
+                lineMeshes.fIndexCount = lineIndexOffset;
+                lineMeshes.fBaseIndex = firstLineIndex;
             }
+            lineMeshes.fVertexBuffer.reset(lineVertexBuffer);
+            lineMeshes.fVertexCount = lineVertexOffset;
+            lineMeshes.fBaseVertex = firstLineVertex;
+
             target->draw(lineGP.get(), this->pipeline(), lineMeshes);
         }
 
@@ -435,20 +437,20 @@
                                             &firstQuadVertex);
             memcpy(quadVertices, quads.vertices, quadVertexStride * quadVertexOffset);
             GrMesh quadMeshes;
+            quadMeshes.fPrimitiveType = kTriangles_GrPrimitiveType;
             if (fIsIndexed) {
                 const GrBuffer* quadIndexBuffer;
-                int firstQuadIndex;
                 uint16_t* quadIndices = (uint16_t*) target->makeIndexSpace(quadIndexOffset,
                                                                            &quadIndexBuffer,
-                                                                           &firstQuadIndex);
+                                                                           &quadMeshes.fBaseIndex);
+                quadMeshes.fIndexBuffer.reset(quadIndexBuffer);
+                quadMeshes.fIndexCount = quadIndexOffset;
                 memcpy(quadIndices, quads.indices, sizeof(uint16_t) * quadIndexOffset);
-                quadMeshes.initIndexed(kTriangles_GrPrimitiveType, quadVertexBuffer,
-                                       quadIndexBuffer, firstQuadVertex, firstQuadIndex,
-                                       quadVertexOffset, quadIndexOffset);
-            } else {
-                quadMeshes.init(kTriangles_GrPrimitiveType, quadVertexBuffer, firstQuadVertex,
-                                quadVertexOffset);
             }
+            quadMeshes.fVertexBuffer.reset(quadVertexBuffer);
+            quadMeshes.fVertexCount = quadVertexOffset;
+            quadMeshes.fBaseVertex = firstQuadVertex;
+
             target->draw(quadGP.get(), this->pipeline(), quadMeshes);
         }
     }
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index 3785245..92571d6 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -17,17 +17,17 @@
     this->onPrepareDraws(&target);
 }
 
-void* GrMeshDrawOp::InstancedHelper::init(Target* target, GrPrimitiveType primType,
+void* GrMeshDrawOp::PatternHelper::init(Target* target, GrPrimitiveType primType,
                                           size_t vertexStride, const GrBuffer* indexBuffer,
-                                          int verticesPerInstance, int indicesPerInstance,
-                                          int instancesToDraw) {
+                                          int verticesPerRepetition, int indicesPerRepetition,
+                                          int repeatCount) {
     SkASSERT(target);
     if (!indexBuffer) {
         return nullptr;
     }
     const GrBuffer* vertexBuffer;
     int firstVertex;
-    int vertexCount = verticesPerInstance * instancesToDraw;
+    int vertexCount = verticesPerRepetition * repeatCount;
     void* vertices =
             target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
     if (!vertices) {
@@ -36,16 +36,22 @@
     }
     SkASSERT(vertexBuffer);
     size_t ibSize = indexBuffer->gpuMemorySize();
-    int maxInstancesPerDraw = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerInstance));
+    int maxRepetitions = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerRepetition));
 
-    fMesh.initInstanced(primType, vertexBuffer, indexBuffer, firstVertex, verticesPerInstance,
-                        indicesPerInstance, instancesToDraw, maxInstancesPerDraw);
+    fMesh.fPrimitiveType = primType;
+    fMesh.fIndexBuffer.reset(indexBuffer);
+    fMesh.fIndexCount = indicesPerRepetition;
+    fMesh.fBaseIndex = 0;
+    fMesh.fVertexBuffer.reset(vertexBuffer);
+    fMesh.fVertexCount = verticesPerRepetition;
+    fMesh.fBaseVertex = firstVertex;
+    fMesh.fPatternRepeatCount = repeatCount;
+    fMesh.fMaxPatternRepetitionsInIndexBuffer = maxRepetitions;
     return vertices;
 }
 
-void GrMeshDrawOp::InstancedHelper::recordDraw(Target* target, const GrGeometryProcessor* gp,
+void GrMeshDrawOp::PatternHelper::recordDraw(Target* target, const GrGeometryProcessor* gp,
                                                const GrPipeline* pipeline) {
-    SkASSERT(fMesh.instanceCount());
     target->draw(gp, pipeline, fMesh);
 }
 
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index a4b172d..92a4c1e 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -29,15 +29,15 @@
 protected:
     GrMeshDrawOp(uint32_t classID);
 
-    /** Helper for rendering instances using an instanced index buffer. This class creates the space
-        for the vertices and flushes the draws to the GrMeshDrawOp::Target. */
-    class InstancedHelper {
+    /** Helper for rendering repeating meshes using a patterned index buffer. This class creates the
+        space for the vertices and flushes the draws to the GrMeshDrawOp::Target. */
+    class PatternHelper {
     public:
-        InstancedHelper() {}
+        PatternHelper() {}
         /** Returns the allocated storage for the vertices. The caller should populate the vertices
             before calling recordDraws(). */
         void* init(Target*, GrPrimitiveType, size_t vertexStride, const GrBuffer*,
-                   int verticesPerInstance, int indicesPerInstance, int instancesToDraw);
+                   int verticesPerRepetition, int indicesPerRepetition, int repeatCount);
 
         /** Call after init() to issue draws to the GrMeshDrawOp::Target.*/
         void recordDraw(Target*, const GrGeometryProcessor*, const GrPipeline*);
@@ -50,7 +50,7 @@
     static const int kIndicesPerQuad = 6;
 
     /** A specialization of InstanceHelper for quad rendering. */
-    class QuadHelper : private InstancedHelper {
+    class QuadHelper : private PatternHelper {
     public:
         QuadHelper() : INHERITED() {}
         /** Finds the cached quad index buffer and reserves vertex space. Returns nullptr on failure
@@ -58,10 +58,10 @@
             calling recordDraws(). */
         void* init(Target*, size_t vertexStride, int quadsToDraw);
 
-        using InstancedHelper::recordDraw;
+        using PatternHelper::recordDraw;
 
     private:
-        typedef InstancedHelper INHERITED;
+        typedef PatternHelper INHERITED;
     };
 
 private:
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index 69bfe29..b9c20e1 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -133,7 +133,7 @@
         int instanceCount = fRects.count();
 
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
+        PatternHelper helper;
         void* vertices =
                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
                             kVertsPerInstance, kIndicesPerInstance, instanceCount);
diff --git a/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp b/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp
index fef5154..647f69e 100644
--- a/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp
@@ -158,7 +158,7 @@
         int instanceCount = fRects.count();
 
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
+        PatternHelper helper;
         void* vertices =
                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
                             kVertsPerInstance, kIndicesPerInstance, instanceCount);
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index 69958c0..c40364d 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -157,7 +157,10 @@
         }
 
         GrMesh mesh;
-        mesh.init(primType, vertexBuffer, firstVertex, vertexCount);
+        mesh.fPrimitiveType = primType;
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = vertexCount;
+        mesh.fBaseVertex = firstVertex;
         target->draw(gp.get(), this->pipeline(), mesh);
     }
 
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 71f8bb0..4d44b13 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -1097,8 +1097,13 @@
         }
 
         GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
+        mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+        mesh.fIndexBuffer.reset(indexBuffer);
+        mesh.fIndexCount = fIndexCount;
+        mesh.fBaseIndex = firstIndex;
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = fVertCount;
+        mesh.fBaseVertex = firstVertex;
         target->draw(gp.get(), this->pipeline(), mesh);
     }
 
@@ -1998,8 +2003,13 @@
         }
 
         GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
+        mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+        mesh.fIndexBuffer.reset(indexBuffer);
+        mesh.fIndexCount = fIndexCount;
+        mesh.fBaseIndex = firstIndex;
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = fVertCount;
+        mesh.fBaseVertex = firstVertex;
         target->draw(gp.get(), this->pipeline(), mesh);
     }
 
@@ -2055,11 +2065,11 @@
     GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
     switch (type) {
         case kFill_RRectType:
-            return resourceProvider->findOrCreateInstancedIndexBuffer(
+            return resourceProvider->findOrCreatePatternedIndexBuffer(
                     gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
                     kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
         case kStroke_RRectType:
-            return resourceProvider->findOrCreateInstancedIndexBuffer(
+            return resourceProvider->findOrCreatePatternedIndexBuffer(
                     gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
                     kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
         default:
@@ -2185,7 +2195,7 @@
         sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
                 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
 
-        InstancedHelper helper;
+        PatternHelper helper;
         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
                             kVertsPerStandardRRect, indicesPerInstance, instanceCount));
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 91191f3..7c90ae7 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -103,7 +103,7 @@
 
         size_t vertexStride = gp->getVertexStride();
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
+        PatternHelper helper;
         void* vertices =
                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
                             kVertsPerInstance, kIndicesPerInstance, numRects);
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index 4c2c322..7803449 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -608,8 +608,13 @@
         }
 
         GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
+        mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+        mesh.fIndexBuffer.reset(indexBuffer);
+        mesh.fIndexCount = fIndexCount;
+        mesh.fBaseIndex = firstIndex;
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = fVertCount;
+        mesh.fBaseVertex = firstVertex;
         target->draw(gp.get(), this->pipeline(), mesh);
     }
 
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index 0986a09..53de779 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -682,9 +682,14 @@
             GrMesh mesh;
             int maxInstancesPerDraw =
                 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
-            mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
-                flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerQuad,
-                kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
+            mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+            mesh.fIndexBuffer.reset(flushInfo->fIndexBuffer.get());
+            mesh.fIndexCount = kIndicesPerQuad;
+            mesh.fVertexBuffer.reset(flushInfo->fVertexBuffer.get());
+            mesh.fVertexCount = kVerticesPerQuad;
+            mesh.fBaseVertex = flushInfo->fVertexOffset;
+            mesh.fPatternRepeatCount = flushInfo->fInstancesToFlush;
+            mesh.fMaxPatternRepetitionsInIndexBuffer = maxInstancesPerDraw;
             target->draw(flushInfo->fGeometryProcessor.get(), this->pipeline(), mesh);
             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
             flushInfo->fInstancesToFlush = 0;
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 059d831..96c9124 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -316,7 +316,10 @@
         GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
                                                               : kTriangles_GrPrimitiveType;
         GrMesh mesh;
-        mesh.init(primitiveType, vb, firstVertex, count);
+        mesh.fPrimitiveType = primitiveType;
+        mesh.fVertexBuffer.reset(vb);
+        mesh.fVertexCount = count;
+        mesh.fBaseVertex = firstVertex;
         target->draw(gp, this->pipeline(), mesh);
     }
 
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index f1de386..7c54877 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -40,7 +40,7 @@
                          BarrierType barrierType,
                          void* barrier) const;
 
-    void bindVertexBuffer(GrVkGpu* gpu, GrVkVertexBuffer* vbuffer) {
+    void bindVertexBuffer(GrVkGpu* gpu, const GrVkVertexBuffer* vbuffer) {
         VkBuffer vkBuffer = vbuffer->buffer();
         // TODO: once vbuffer->offset() no longer always returns 0, we will need to track the offset
         // to know if we can skip binding or not.
@@ -57,7 +57,7 @@
         }
     }
 
-    void bindIndexBuffer(GrVkGpu* gpu, GrVkIndexBuffer* ibuffer) {
+    void bindIndexBuffer(GrVkGpu* gpu, const GrVkIndexBuffer* ibuffer) {
         VkBuffer vkBuffer = ibuffer->buffer();
         // TODO: once ibuffer->offset() no longer always returns 0, we will need to track the offset
         // to know if we can skip binding or not.
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index b06d4c3..ac21923 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -22,7 +22,6 @@
 #include "vk/GrVkDefines.h"
 
 class GrPipeline;
-class GrNonInstancedMesh;
 
 class GrVkBufferImpl;
 class GrVkPipeline;
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index e6593ef..475480b 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -425,27 +425,25 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrVkGpuCommandBuffer::bindGeometry(const GrPrimitiveProcessor& primProc,
-                                        const GrNonInstancedMesh& mesh) {
-    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+                                        const GrBuffer* indexBuffer,
+                                        const GrBuffer* vertexBuffer) {
+    GrVkSecondaryCommandBuffer* currCmdBuf = fCommandBufferInfos[fCurrentCmdInfo].currentCmdBuf();
     // There is no need to put any memory barriers to make sure host writes have finished here.
     // When a command buffer is submitted to a queue, there is an implicit memory barrier that
     // occurs for all host writes. Additionally, BufferMemoryBarriers are not allowed inside of
     // an active RenderPass.
-    SkASSERT(!mesh.vertexBuffer()->isCPUBacked());
-    GrVkVertexBuffer* vbuf;
-    vbuf = (GrVkVertexBuffer*)mesh.vertexBuffer();
-    SkASSERT(vbuf);
-    SkASSERT(!vbuf->isMapped());
+    SkASSERT(vertexBuffer);
+    SkASSERT(!vertexBuffer->isCPUBacked());
+    SkASSERT(!vertexBuffer->isMapped());
 
-    cbInfo.currentCmdBuf()->bindVertexBuffer(fGpu, vbuf);
+    currCmdBuf->bindVertexBuffer(fGpu, static_cast<const GrVkVertexBuffer*>(vertexBuffer));
 
-    if (mesh.isIndexed()) {
-        SkASSERT(!mesh.indexBuffer()->isCPUBacked());
-        GrVkIndexBuffer* ibuf = (GrVkIndexBuffer*)mesh.indexBuffer();
-        SkASSERT(ibuf);
-        SkASSERT(!ibuf->isMapped());
+    if (indexBuffer) {
+        SkASSERT(indexBuffer);
+        SkASSERT(!indexBuffer->isMapped());
+        SkASSERT(!indexBuffer->isCPUBacked());
 
-        cbInfo.currentCmdBuf()->bindIndexBuffer(fGpu, ibuf);
+        currCmdBuf->bindIndexBuffer(fGpu, static_cast<const GrVkIndexBuffer*>(indexBuffer));
     }
 }
 
@@ -539,7 +537,7 @@
         set_texture_layout(dstTexture, fGpu);
     }
 
-    GrPrimitiveType primitiveType = meshes[0].primitiveType();
+    GrPrimitiveType primitiveType = meshes[0].fPrimitiveType;
     sk_sp<GrVkPipelineState> pipelineState = this->prepareDrawState(pipeline,
                                                                     primProc,
                                                                     primitiveType);
@@ -551,16 +549,14 @@
 
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
-        GrMesh::Iterator iter;
-        const GrNonInstancedMesh* nonIdxMesh = iter.init(mesh);
-        do {
-            if (nonIdxMesh->primitiveType() != primitiveType) {
+        for (GrMesh::PatternBatch batch : mesh) {
+            if (mesh.fPrimitiveType != primitiveType) {
                 // Technically we don't have to call this here (since there is a safety check in
                 // pipelineState:setData but this will allow for quicker freeing of resources if the
                 // pipelineState sits in a cache for a while.
                 pipelineState->freeTempResources(fGpu);
                 SkDEBUGCODE(pipelineState = nullptr);
-                primitiveType = nonIdxMesh->primitiveType();
+                primitiveType = mesh.fPrimitiveType;
                 pipelineState = this->prepareDrawState(pipeline,
                                                        primProc,
                                                        primitiveType);
@@ -569,26 +565,26 @@
                 }
             }
             SkASSERT(pipelineState);
-            this->bindGeometry(primProc, *nonIdxMesh);
+            this->bindGeometry(primProc, mesh.fIndexBuffer.get(), mesh.fVertexBuffer.get());
 
-            if (nonIdxMesh->isIndexed()) {
+            if (mesh.fIndexBuffer) {
                 cbInfo.currentCmdBuf()->drawIndexed(fGpu,
-                                                   nonIdxMesh->indexCount(),
-                                                   1,
-                                                   nonIdxMesh->startIndex(),
-                                                   nonIdxMesh->startVertex(),
-                                                   0);
+                                                    mesh.fIndexCount * batch.fRepeatCount,
+                                                    1,
+                                                    mesh.fBaseIndex,
+                                                    batch.fBaseVertex,
+                                                    0);
             } else {
                 cbInfo.currentCmdBuf()->draw(fGpu,
-                                            nonIdxMesh->vertexCount(),
-                                            1,
-                                            nonIdxMesh->startVertex(),
-                                            0);
+                                             mesh.fVertexCount * batch.fRepeatCount,
+                                             1,
+                                             batch.fBaseVertex,
+                                             0);
             }
             cbInfo.fIsEmpty = false;
 
             fGpu->stats()->incNumDraws();
-        } while ((nonIdxMesh = iter.next()));
+        }
     }
 
     // Update command buffer bounds
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index 519edb3..3d4d3ee 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -14,7 +14,6 @@
 #include "GrTypes.h"
 #include "GrVkPipelineState.h"
 
-class GrNonInstancedMesh;
 class GrVkGpu;
 class GrVkImage;
 class GrVkRenderPass;
@@ -46,7 +45,9 @@
     void onSubmit() override;
 
     // Bind vertex and index buffers
-    void bindGeometry(const GrPrimitiveProcessor&, const GrNonInstancedMesh&);
+    void bindGeometry(const GrPrimitiveProcessor&,
+                      const GrBuffer* indexBuffer,
+                      const GrBuffer* vertexBuffer);
 
     sk_sp<GrVkPipelineState> prepareDrawState(const GrPipeline&,
                                               const GrPrimitiveProcessor&,
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index b4788de..585014e 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -14,7 +14,6 @@
 
 #include "vk/GrVkDefines.h"
 
-class GrNonInstancedVertices;
 class GrPipeline;
 class GrPrimitiveProcessor;
 class GrStencilSettings;
diff --git a/tests/PreFlushCallbackTest.cpp b/tests/PreFlushCallbackTest.cpp
index da7f93b..fc736a4 100644
--- a/tests/PreFlushCallbackTest.cpp
+++ b/tests/PreFlushCallbackTest.cpp
@@ -145,10 +145,13 @@
         }
 
         GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType,
-                         vertexBuffer, indexBuffer,
-                         firstVertex, firstIndex,
-                         4, 6);
+        mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
+        mesh.fIndexBuffer.reset(indexBuffer);
+        mesh.fIndexCount = 6;
+        mesh.fBaseIndex = firstIndex;
+        mesh.fVertexBuffer.reset(vertexBuffer);
+        mesh.fVertexCount = 4;
+        mesh.fBaseVertex = firstVertex;
 
         target->draw(gp.get(), this->pipeline(), mesh);
     }