Store GrMeshDrawOps' meshes in GrOpFlushState's arena.

Remove late draw consolidation in GrOpFlushState. Rarely did anything
and doesn't work with new allocation strategy. Ops can use GrMesh arrays
to acheive the same thing. (Each Op that cared to would have to implement
but it isn't applicable to most Ops).

Modify GrMeshDrawOp::Target::draw() to take array of meshes, with single
mesh as a special case.

Change-Id: I552677de47b9ffd2fcaf55af85f70f290e5aa9c7
Reviewed-on: https://skia-review.googlesource.com/145426
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrMesh.h b/src/gpu/GrMesh.h
index fb7b525..e8a3f5c 100644
--- a/src/gpu/GrMesh.h
+++ b/src/gpu/GrMesh.h
@@ -21,12 +21,12 @@
  */
 class GrMesh {
 public:
-    GrMesh(GrPrimitiveType primitiveType)
-        : fPrimitiveType(primitiveType)
-        , fBaseVertex(0) {
+    GrMesh(GrPrimitiveType primitiveType = GrPrimitiveType::kTriangles)
+            : fPrimitiveType(primitiveType), fBaseVertex(0) {
         SkDEBUGCODE(fNonIndexNonInstanceData.fVertexCount = -1;)
     }
 
+    void setPrimitiveType(GrPrimitiveType type) { fPrimitiveType = type; }
     GrPrimitiveType primitiveType() const { return fPrimitiveType; }
 
     bool isIndexed() const { return SkToBool(fIndexBuffer.get()); }
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index 8709e78..6385c9f 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -45,8 +45,7 @@
         SkASSERT(fCurrDraw->fPipeline->proxy() == this->drawOpArgs().fProxy);
         this->rtCommandBuffer()->draw(*fCurrDraw->fGeometryProcessor, *fCurrDraw->fPipeline,
                                       fCurrDraw->fFixedDynamicState, fCurrDraw->fDynamicStateArrays,
-                                      fMeshes.begin() + fCurrMesh, fCurrDraw->fMeshCnt, opBounds);
-        fCurrMesh += fCurrDraw->fMeshCnt;
+                                      fCurrDraw->fMeshes, fCurrDraw->fMeshCnt, opBounds);
         fTokenTracker->flushToken();
         ++fCurrDraw;
     }
@@ -61,7 +60,6 @@
     // Setup execution iterators.
     fCurrDraw = fDraws.begin();
     fCurrUpload = fInlineUploads.begin();
-    fCurrMesh = 0;
 }
 
 void GrOpFlushState::reset() {
@@ -73,8 +71,6 @@
     fASAPUploads.reset();
     fInlineUploads.reset();
     fDraws.reset();
-    fMeshes.reset();
-    fCurrMesh = 0;
     fBaseDrawToken = GrDeferredUploadToken::AlreadyFlushedToken();
 }
 
@@ -106,29 +102,12 @@
 
 void GrOpFlushState::draw(sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline,
                           const GrPipeline::FixedDynamicState* fixedDynamicState,
-                          const GrMesh& mesh) {
+                          const GrMesh meshes[], int meshCnt) {
     SkASSERT(fOpArgs);
     SkASSERT(fOpArgs->fOp);
-    fMeshes.push_back(mesh);
     bool firstDraw = fDraws.begin() == fDraws.end();
-    if (!firstDraw) {
-        Draw& lastDraw = *fDraws.begin();
-        // If the last draw shares a geometry processor and pipeline and there are no intervening
-        // uploads, add this mesh to it.
-        // Note, we could attempt to convert fixed dynamic states into dynamic state arrays here
-        // if everything else is equal. Maybe it's better to rely on Ops to do that?
-        if (lastDraw.fGeometryProcessor == gp && lastDraw.fPipeline == pipeline &&
-            lastDraw.fFixedDynamicState == fixedDynamicState) {
-            if (fInlineUploads.begin() == fInlineUploads.end() ||
-                fInlineUploads.tail()->fUploadBeforeToken != fTokenTracker->nextDrawToken()) {
-                ++lastDraw.fMeshCnt;
-                return;
-            }
-        }
-    }
     auto& draw = fDraws.append(&fArena);
     GrDeferredUploadToken token = fTokenTracker->issueDrawToken();
-
     for (int i = 0; i < gp->numTextureSamplers(); ++i) {
         fixedDynamicState->fPrimitiveProcessorTextures[i]->addPendingRead();
     }
@@ -136,7 +115,8 @@
     draw.fPipeline = pipeline;
     draw.fFixedDynamicState = fixedDynamicState;
     draw.fDynamicStateArrays = nullptr;
-    draw.fMeshCnt = 1;
+    draw.fMeshes = meshes;
+    draw.fMeshCnt = meshCnt;
     draw.fOpID = fOpArgs->fOp->uniqueID();
     if (firstDraw) {
         fBaseDrawToken = token;
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index b9ec736..0a46ed2 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -73,9 +73,11 @@
     GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&&) final;
 
     /** Overrides of GrMeshDrawOp::Target. */
-
-    void draw(sk_sp<const GrGeometryProcessor>, const GrPipeline*,
-              const GrPipeline::FixedDynamicState*, const GrMesh&) final;
+    void draw(sk_sp<const GrGeometryProcessor>,
+              const GrPipeline*,
+              const GrPipeline::FixedDynamicState*,
+              const GrMesh[],
+              int meshCount) final;
     void* makeVertexSpace(size_t vertexSize, int vertexCount, const GrBuffer**,
                           int* startVertex) final;
     uint16_t* makeIndexSpace(int indexCount, const GrBuffer**, int* startIndex) final;
@@ -123,12 +125,13 @@
                 fFixedDynamicState->fPrimitiveProcessorTextures[i]->completedRead();
             }
         }
-        int fMeshCnt = 0;
         sk_sp<const GrGeometryProcessor> fGeometryProcessor;
-        const GrPipeline* fPipeline;
+        const GrPipeline* fPipeline = nullptr;
         const GrPipeline::FixedDynamicState* fFixedDynamicState;
         const GrPipeline::DynamicStateArrays* fDynamicStateArrays;
-        uint32_t fOpID;
+        const GrMesh* fMeshes = nullptr;
+        int fMeshCnt = 0;
+        uint32_t fOpID = SK_InvalidUniqueID;
     };
 
     // Storage for ops' pipelines, draws, and inline uploads.
@@ -142,9 +145,6 @@
     SkArenaAllocList<GrDeferredTextureUploadFn> fASAPUploads;
     SkArenaAllocList<InlineUpload> fInlineUploads;
     SkArenaAllocList<Draw> fDraws;
-    // TODO: These should go in the arena. However, GrGpuCommandBuffer and other classes currently
-    // accept contiguous arrays of meshes.
-    SkSTArray<16, GrMesh> fMeshes;
 
     // All draws we store have an implicit draw token. This is the draw token for the first draw
     // in fDraws.
@@ -161,7 +161,6 @@
 
     // Variables that are used to track where we are in lists as ops are executed
     SkArenaAllocList<Draw>::Iter fCurrDraw;
-    int fCurrMesh;
     SkArenaAllocList<InlineUpload>::Iter fCurrUpload;
 
     // Used to track the proxies that need to be uninstantiated after we finish a flush
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 6505944..1c8eeb0 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -843,10 +843,10 @@
             extract_lines_only_verts(tess, verts, vertexStride, args.fColor, idxs,
                                      fHelper.compatibleWithAlphaAsCoverage());
 
-            GrMesh mesh(GrPrimitiveType::kTriangles);
-            mesh.setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1,
-                            GrPrimitiveRestart::kNo);
-            mesh.setVertexData(vertexBuffer, firstVertex);
+            GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+            mesh->setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1,
+                             GrPrimitiveRestart::kNo);
+            mesh->setVertexData(vertexBuffer, firstVertex);
             target->draw(gp, pipe.fPipeline, pipe.fFixedDynamicState, mesh);
         }
     }
@@ -928,17 +928,18 @@
             SkSTArray<kPreallocDrawCnt, Draw, true> draws;
             create_vertices(segments, fanPt, args.fColor, &draws, verts, idxs);
 
-            GrMesh mesh(GrPrimitiveType::kTriangles);
-
+            GrMesh* meshes = target->allocMeshes(draws.count());
             for (int j = 0; j < draws.count(); ++j) {
                 const Draw& draw = draws[j];
-                mesh.setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0, draw.fVertexCnt - 1,
-                                GrPrimitiveRestart::kNo);
-                mesh.setVertexData(vertexBuffer, firstVertex);
-                target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, mesh);
+                meshes[j].setPrimitiveType(GrPrimitiveType::kTriangles);
+                meshes[j].setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0,
+                                     draw.fVertexCnt - 1, GrPrimitiveRestart::kNo);
+                meshes[j].setVertexData(vertexBuffer, firstVertex);
                 firstIndex += draw.fIndexCnt;
                 firstVertex += draw.fVertexCnt;
             }
+            target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, meshes,
+                         draws.count());
         }
     }
 
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index 4b939bb..f26811d 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -265,10 +265,9 @@
         SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
         sk_sp<const GrBuffer> indexBuffer = get_index_buffer(target->resourceProvider());
-        PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices =
-                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerAAFillRect,
-                            kIndicesPerAAFillRect, fRectCnt);
+        PatternHelper helper(target, GrPrimitiveType::kTriangles, vertexStride, indexBuffer.get(),
+                             kVertsPerAAFillRect, kIndicesPerAAFillRect, fRectCnt);
+        void* vertices = helper.vertices();
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index b192474..c4039d3 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -975,10 +975,10 @@
             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
         }
 
-        GrMesh mesh(GrPrimitiveType::kTriangles);
-        mesh.setIndexedPatterned(linesIndexBuffer.get(), kIdxsPerLineSeg, kLineSegNumVertices,
-                                 lineCount, kLineSegsNumInIdxBuffer);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+        mesh->setIndexedPatterned(linesIndexBuffer.get(), kIdxsPerLineSeg, kLineSegNumVertices,
+                                  lineCount, kLineSegsNumInIdxBuffer);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         target->draw(std::move(lineGP), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
@@ -1030,19 +1030,19 @@
         }
 
         if (quadCount > 0) {
-            GrMesh mesh(GrPrimitiveType::kTriangles);
-            mesh.setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
-                                     quadCount, kQuadsNumInIdxBuffer);
-            mesh.setVertexData(vertexBuffer, firstVertex);
+            GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+            mesh->setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
+                                      quadCount, kQuadsNumInIdxBuffer);
+            mesh->setVertexData(vertexBuffer, firstVertex);
             target->draw(std::move(quadGP), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
         if (conicCount > 0) {
-            GrMesh mesh(GrPrimitiveType::kTriangles);
-            mesh.setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
-                                     conicCount, kQuadsNumInIdxBuffer);
-            mesh.setVertexData(vertexBuffer, firstVertex);
+            GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+            mesh->setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
+                                      conicCount, kQuadsNumInIdxBuffer);
+            mesh->setVertexData(vertexBuffer, firstVertex);
             target->draw(std::move(conicGP), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
         }
     }
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index 63aac85..99f9f9e 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -217,7 +217,6 @@
             return;
         }
         const GrBuffer* vertexBuffer;
-        GrMesh mesh(GrPrimitiveType::kTriangles);
         int firstVertex;
         void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
                                               &firstVertex);
@@ -235,9 +234,10 @@
             return;
         }
         memcpy(idxs, indices, indexCount * sizeof(uint16_t));
-        mesh.setIndexed(indexBuffer, indexCount, firstIndex, 0, vertexCount - 1,
-                        GrPrimitiveRestart::kNo);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+        mesh->setIndexed(indexBuffer, indexCount, firstIndex, 0, vertexCount - 1,
+                         GrPrimitiveRestart::kNo);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         target->draw(std::move(gp), pipeline, fixedDynamicState, mesh);
     }
 
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index 0645e64..60196b8 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -282,11 +282,11 @@
     int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
     int instanceCount = fRects.count();
 
-    sk_sp<const GrBuffer> indexBuffer = GetIndexBuffer(target->resourceProvider(), this->miterStroke());
-    PatternHelper helper(GrPrimitiveType::kTriangles);
-    void* vertices =
-            helper.init(target, vertexStride, indexBuffer.get(),
-                        verticesPerInstance, indicesPerInstance, instanceCount);
+    sk_sp<const GrBuffer> indexBuffer =
+            GetIndexBuffer(target->resourceProvider(), this->miterStroke());
+    PatternHelper helper(target, GrPrimitiveType::kTriangles, vertexStride, indexBuffer.get(),
+                         verticesPerInstance, indicesPerInstance, instanceCount);
+    void* vertices = helper.vertices();
     if (!vertices || !indexBuffer) {
         SkDebugf("Could not allocate vertices\n");
         return;
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index bd24ef6..2b10e19 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -425,12 +425,12 @@
                                                                       samplerState);
         }
     }
-    GrMesh mesh(GrPrimitiveType::kTriangles);
     int maxGlyphsPerDraw =
             static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
-    mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph,
-                             flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
-    mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
+    GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+    mesh->setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph,
+                              flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
+    mesh->setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
     target->draw(flushInfo->fGeometryProcessor, flushInfo->fPipeline, flushInfo->fFixedDynamicState,
                  mesh);
     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index 5393fe0..ba17497 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -625,7 +625,6 @@
             return;
         }
 
-        QuadHelper helper;
         size_t vertexStride;
         if (fullDash) {
             vertexStride =
@@ -634,7 +633,8 @@
             vertexStride = sizeof(SkPoint);
         }
         SkASSERT(vertexStride == gp->debugOnly_vertexStride());
-        void* vertices = helper.init(target, vertexStride, totalRectCount);
+        QuadHelper helper(target, vertexStride, totalRectCount);
+        void* vertices = helper.vertices();
         if (!vertices) {
             return;
         }
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 1e8e116..e8e0949 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -67,7 +67,7 @@
     PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
                    sk_sp<const GrGeometryProcessor> geometryProcessor, const GrPipeline* pipeline,
                    const GrPipeline::FixedDynamicState* fixedDynamicState)
-            : fMesh(primitiveType)
+            : fPrimitiveType(primitiveType)
             , fTarget(target)
             , fVertexStride(sizeof(SkPoint))
             , fGeometryProcessor(std::move(geometryProcessor))
@@ -200,15 +200,15 @@
      *  TODO: Cache some of these for better performance, rather than re-computing?
      */
     bool isIndexed() const {
-        return GrPrimitiveType::kLines == fMesh.primitiveType() ||
-               GrPrimitiveType::kTriangles == fMesh.primitiveType();
+        return GrPrimitiveType::kLines == fPrimitiveType ||
+               GrPrimitiveType::kTriangles == fPrimitiveType;
     }
     bool isHairline() const {
-        return GrPrimitiveType::kLines == fMesh.primitiveType() ||
-               GrPrimitiveType::kLineStrip == fMesh.primitiveType();
+        return GrPrimitiveType::kLines == fPrimitiveType ||
+               GrPrimitiveType::kLineStrip == fPrimitiveType;
     }
     int indexScale() const {
-        switch (fMesh.primitiveType()) {
+        switch (fPrimitiveType) {
             case GrPrimitiveType::kLines:
                 return 2;
             case GrPrimitiveType::kTriangles:
@@ -271,14 +271,15 @@
         SkASSERT(indexCount <= fIndicesInChunk);
 
         if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
+            GrMesh* mesh = fTarget->allocMesh(fPrimitiveType);
             if (!this->isIndexed()) {
-                fMesh.setNonIndexedNonInstanced(vertexCount);
+                mesh->setNonIndexedNonInstanced(vertexCount);
             } else {
-                fMesh.setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1,
+                mesh->setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1,
                                  GrPrimitiveRestart::kNo);
             }
-            fMesh.setVertexData(fVertexBuffer, fFirstVertex);
-            fTarget->draw(fGeometryProcessor, fPipeline, fFixedDynamicState, fMesh);
+            mesh->setVertexData(fVertexBuffer, fFirstVertex);
+            fTarget->draw(fGeometryProcessor, fPipeline, fFixedDynamicState, mesh);
         }
 
         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
@@ -311,7 +312,7 @@
         }
     }
 
-    GrMesh fMesh;
+    GrPrimitiveType fPrimitiveType;
     GrMeshDrawOp::Target* fTarget;
     size_t fVertexStride;
     sk_sp<const GrGeometryProcessor> fGeometryProcessor;
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index 5e22416..0c965ce 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -130,9 +130,9 @@
             sizeof(SkPoint) + sizeof(SkPoint) + (this->hasColors() ? sizeof(GrColor) : 0);
     SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
-    QuadHelper helper;
     int numQuads = this->quadCount();
-    void* verts = helper.init(target, vertexStride, numQuads);
+    QuadHelper helper(target, vertexStride, numQuads);
+    void* verts = helper.vertices();
     if (!verts) {
         SkDebugf("Could not allocate vertices\n");
         return;
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index b0cd3f7..70a63c5 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -470,15 +470,14 @@
                                     int firstVertex,
                                     const GrBuffer* indexBuffer,
                                     int firstIndex) {
-    GrMesh mesh(this->primitiveType());
+    GrMesh* mesh = target->allocMesh(this->primitiveType());
     if (this->isIndexed()) {
-        mesh.setIndexed(indexBuffer, fIndexCount,
-                        firstIndex, 0, fVertexCount - 1,
-                        GrPrimitiveRestart::kNo);
+        mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertexCount - 1,
+                         GrPrimitiveRestart::kNo);
     } else {
-        mesh.setNonIndexedNonInstanced(fVertexCount);
+        mesh->setNonIndexedNonInstanced(fVertexCount);
     }
-    mesh.setVertexData(vertexBuffer, firstVertex);
+    mesh->setVertexData(vertexBuffer, firstVertex);
     auto pipe = fHelper.makePipeline(target);
     target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
 }
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 283754c..053477f 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -223,9 +223,9 @@
         }
 
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
-        PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices = helper.init(target, kVertexStide, indexBuffer.get(), kVertsPerRect,
-                                     kIndicesPerRect, numRects);
+        PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStide, indexBuffer.get(),
+                             kVertsPerRect, kIndicesPerRect, numRects);
+        void* vertices = helper.vertices();
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index ab1f364..620ea47 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -14,50 +14,61 @@
 
 void GrMeshDrawOp::onPrepare(GrOpFlushState* state) { this->onPrepareDraws(state); }
 
-void* GrMeshDrawOp::PatternHelper::init(Target* target, size_t vertexStride,
-                                        const GrBuffer* indexBuffer, int verticesPerRepetition,
-                                        int indicesPerRepetition, int repeatCount) {
+void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
+    state->executeDrawsAndUploadsForMeshDrawOp(this->uniqueID(), this->bounds());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrMeshDrawOp::PatternHelper::PatternHelper(Target* target, GrPrimitiveType primitiveType,
+                                           size_t vertexStride, const GrBuffer* indexBuffer,
+                                           int verticesPerRepetition, int indicesPerRepetition,
+                                           int repeatCount) {
+    this->init(target, primitiveType, vertexStride, indexBuffer, verticesPerRepetition,
+               indicesPerRepetition, repeatCount);
+}
+
+void GrMeshDrawOp::PatternHelper::init(Target* target, GrPrimitiveType primitiveType,
+                                       size_t vertexStride, const GrBuffer* indexBuffer,
+                                       int verticesPerRepetition, int indicesPerRepetition,
+                                       int repeatCount) {
     SkASSERT(target);
     if (!indexBuffer) {
-        return nullptr;
+        return;
     }
     const GrBuffer* vertexBuffer;
     int firstVertex;
     int vertexCount = verticesPerRepetition * repeatCount;
-    void* vertices =
-            target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
-    if (!vertices) {
-        SkDebugf("Vertices could not be allocated for instanced rendering.");
-        return nullptr;
+    fVertices = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
+    if (!fVertices) {
+        SkDebugf("Vertices could not be allocated for patterned rendering.");
+        return;
     }
     SkASSERT(vertexBuffer);
     size_t ibSize = indexBuffer->gpuMemorySize();
     int maxRepetitions = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerRepetition));
-
-    fMesh.setIndexedPatterned(indexBuffer, indicesPerRepetition, verticesPerRepetition,
-                              repeatCount, maxRepetitions);
-    fMesh.setVertexData(vertexBuffer, firstVertex);
-    return vertices;
+    fMesh = target->allocMesh(primitiveType);
+    fMesh->setIndexedPatterned(indexBuffer, indicesPerRepetition, verticesPerRepetition,
+                               repeatCount, maxRepetitions);
+    fMesh->setVertexData(vertexBuffer, firstVertex);
 }
 
 void GrMeshDrawOp::PatternHelper::recordDraw(
         Target* target, sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline,
-        const GrPipeline::FixedDynamicState* fixedDynamicState) {
+        const GrPipeline::FixedDynamicState* fixedDynamicState) const {
     target->draw(std::move(gp), pipeline, fixedDynamicState, fMesh);
 }
 
-void* GrMeshDrawOp::QuadHelper::init(Target* target, size_t vertexStride, int quadsToDraw) {
+//////////////////////////////////////////////////////////////////////////////
+
+GrMeshDrawOp::QuadHelper::QuadHelper(Target* target, size_t vertexStride, int quadsToDraw) {
     sk_sp<const GrBuffer> quadIndexBuffer = target->resourceProvider()->refQuadIndexBuffer();
     if (!quadIndexBuffer) {
         SkDebugf("Could not get quad index buffer.");
-        return nullptr;
+        return;
     }
-    return this->INHERITED::init(target, vertexStride, quadIndexBuffer.get(), kVerticesPerQuad,
-                                 kIndicesPerQuad, quadsToDraw);
-}
-
-void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
-    state->executeDrawsAndUploadsForMeshDrawOp(this->uniqueID(), this->bounds());
+    this->init(target, GrPrimitiveType::kTriangles, vertexStride, quadIndexBuffer.get(),
+               kVerticesPerQuad, kIndicesPerQuad, quadsToDraw);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 38b7bb4..a6e7da5 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -33,18 +33,23 @@
         space for the vertices and flushes the draws to the GrMeshDrawOp::Target. */
     class PatternHelper {
     public:
-        PatternHelper(GrPrimitiveType primitiveType) : fMesh(primitiveType) {}
-        /** Returns the allocated storage for the vertices. The caller should populate the vertices
-            before calling recordDraws(). */
-        void* init(Target*, size_t vertexStride, const GrBuffer*, int verticesPerRepetition,
-                   int indicesPerRepetition, int repeatCount);
+        PatternHelper(Target*, GrPrimitiveType, size_t vertexStride, const GrBuffer*,
+                      int verticesPerRepetition, int indicesPerRepetition, int repeatCount);
 
-        /** Call after init() to issue draws to the GrMeshDrawOp::Target.*/
+        /** Called to issue draws to the GrMeshDrawOp::Target.*/
         void recordDraw(Target*, sk_sp<const GrGeometryProcessor>, const GrPipeline*,
-                        const GrPipeline::FixedDynamicState*);
+                        const GrPipeline::FixedDynamicState*) const;
+
+        void* vertices() const { return fVertices; }
+
+    protected:
+        PatternHelper() = default;
+        void init(Target*, GrPrimitiveType, size_t vertexStride, const GrBuffer*,
+                  int verticesPerRepetition, int indicesPerRepetition, int repeatCount);
 
     private:
-        GrMesh fMesh;
+        void* fVertices = nullptr;
+        GrMesh* fMesh = nullptr;
     };
 
     static const int kVerticesPerQuad = 4;
@@ -53,13 +58,11 @@
     /** A specialization of InstanceHelper for quad rendering. */
     class QuadHelper : private PatternHelper {
     public:
-        QuadHelper() : INHERITED(GrPrimitiveType::kTriangles) {}
-        /** Finds the cached quad index buffer and reserves vertex space. Returns nullptr on failure
-            and on success a pointer to the vertex data that the caller should populate before
-            calling recordDraws(). */
-        void* init(Target*, size_t vertexStride, int quadsToDraw);
+        QuadHelper() = delete;
+        QuadHelper(Target* target, size_t vertexStride, int quadsToDraw);
 
         using PatternHelper::recordDraw;
+        using PatternHelper::vertices;
 
     private:
         typedef PatternHelper INHERITED;
@@ -77,8 +80,19 @@
     virtual ~Target() {}
 
     /** Adds a draw of a mesh. */
-    virtual void draw(sk_sp<const GrGeometryProcessor>, const GrPipeline*,
-                      const GrPipeline::FixedDynamicState*, const GrMesh&) = 0;
+    virtual void draw(sk_sp<const GrGeometryProcessor>,
+                      const GrPipeline*,
+                      const GrPipeline::FixedDynamicState*,
+                      const GrMesh[],
+                      int meshCount) = 0;
+
+    /** Helper for drawing a single GrMesh. */
+    void draw(sk_sp<const GrGeometryProcessor> gp,
+              const GrPipeline* pipeline,
+              const GrPipeline::FixedDynamicState* fixedDynamicState,
+              const GrMesh* mesh) {
+        this->draw(std::move(gp), pipeline, fixedDynamicState, mesh, 1);
+    }
 
     /**
      * Makes space for vertex data. The returned pointer is the location where vertex data
@@ -131,6 +145,12 @@
         return this->pipelineArena()->make<GrPipeline>(std::forward<Args>(args)...);
     }
 
+    GrMesh* allocMesh(GrPrimitiveType primitiveType) {
+        return this->pipelineArena()->make<GrMesh>(primitiveType);
+    }
+
+    GrMesh* allocMeshes(int n) { return this->pipelineArena()->makeArray<GrMesh>(n); }
+
     GrPipeline::FixedDynamicState* allocFixedDynamicState(const SkIRect& rect,
                                                           int numPrimitiveProcessorTextures = 0) {
         auto result = this->pipelineArena()->make<GrPipeline::FixedDynamicState>(rect);
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index f7fea58..213561c 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -192,9 +192,9 @@
         int rectCount = fRects.count();
 
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
-        PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices = helper.init(target, kVertexStride, indexBuffer.get(), kVertsPerRect,
-                                     kIndicesPerRect, rectCount);
+        PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStride, indexBuffer.get(),
+                             kVertsPerRect, kIndicesPerRect, rectCount);
+        void* vertices = helper.vertices();
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -325,9 +325,9 @@
         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,
-                                     kIndicesPerRect, rectCount);
+        PatternHelper helper(target, GrPrimitiveType::kTriangles, vertexStride, indexBuffer.get(),
+                             kVertsPerRect, kIndicesPerRect, rectCount);
+        void* vertices = helper.vertices();
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index cbeeb45..2b7fc46 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -188,9 +188,9 @@
             vertex[4].set(fRect.fLeft, fRect.fTop);
         }
 
-        GrMesh mesh(primType);
-        mesh.setNonIndexedNonInstanced(vertexCount);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(primType);
+        mesh->setNonIndexedNonInstanced(vertexCount);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         auto pipe = fHelper.makePipeline(target);
         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 436b67a..df2c5ab 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -1470,10 +1470,10 @@
             vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
         }
 
-        GrMesh mesh(GrPrimitiveType::kTriangles);
-        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
-                        GrPrimitiveRestart::kNo);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+        mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
+                         GrPrimitiveRestart::kNo);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         auto pipe = fHelper.makePipeline(target);
         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
@@ -1786,10 +1786,10 @@
             vertices += circle_type_to_vert_count(true) * kVertexStride;
         }
 
-        GrMesh mesh(GrPrimitiveType::kTriangles);
-        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
-                        GrPrimitiveRestart::kNo);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+        mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
+                         GrPrimitiveRestart::kNo);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         auto pipe = fHelper.makePipeline(target);
         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
@@ -1984,10 +1984,9 @@
         // Setup geometry processor
         sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
 
-        QuadHelper helper;
         SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
-        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
-                helper.init(target, sizeof(EllipseVertex), fEllipses.count()));
+        QuadHelper helper(target, sizeof(EllipseVertex), fEllipses.count());
+        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(helper.vertices());
         if (!verts) {
             return;
         }
@@ -2219,9 +2218,8 @@
                 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
 
         SkASSERT(sizeof(DIEllipseVertex) == gp->debugOnly_vertexStride());
-        QuadHelper helper;
-        DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
-                helper.init(target, sizeof(DIEllipseVertex), fEllipses.count()));
+        QuadHelper helper(target, sizeof(DIEllipseVertex), fEllipses.count());
+        DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(helper.vertices());
         if (!verts) {
             return;
         }
@@ -2725,10 +2723,10 @@
             currStartVertex += rrect_type_to_vert_count(rrect.fType);
         }
 
-        GrMesh mesh(GrPrimitiveType::kTriangles);
-        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
-                        GrPrimitiveRestart::kNo);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+        mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
+                         GrPrimitiveRestart::kNo);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         auto pipe = fHelper.makePipeline(target);
         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
@@ -2927,10 +2925,10 @@
         sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
                 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
 
-        PatternHelper helper(GrPrimitiveType::kTriangles);
-        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
-                helper.init(target, sizeof(EllipseVertex), indexBuffer.get(),
-                            kVertsPerStandardRRect, indicesPerInstance, fRRects.count()));
+        PatternHelper helper(target, GrPrimitiveType::kTriangles, sizeof(EllipseVertex),
+                             indexBuffer.get(), kVertsPerStandardRRect, indicesPerInstance,
+                             fRRects.count());
+        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(helper.vertices());
         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 238ad08..5614b88 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -129,9 +129,9 @@
             return;
         }
         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
-        PatternHelper helper(GrPrimitiveType::kTriangles);
-        void* vertices = helper.init(target, kVertexStride, indexBuffer.get(), kVertsPerInstance,
-                                     kIndicesPerInstance, numRects);
+        PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStride, indexBuffer.get(),
+                             kVertsPerInstance, kIndicesPerInstance, numRects);
+        void* vertices = helper.vertices();
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index c437cb4..b35c4fe 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -627,10 +627,10 @@
         auto pipe = target->makePipeline(kPipelineFlags, GrProcessorSet::MakeEmptySet(),
                                          target->detachAppliedClip());
 
-        GrMesh mesh(GrPrimitiveType::kTriangles);
-        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
-                        GrPrimitiveRestart::kNo);
-        mesh.setVertexData(vertexBuffer, firstVertex);
+        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
+        mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
+                         GrPrimitiveRestart::kNo);
+        mesh->setVertexData(vertexBuffer, firstVertex);
         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
 
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index 20b6b64..4a2bfbe 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -833,13 +833,13 @@
         }
 
         if (flushInfo->fInstancesToFlush) {
-            GrMesh mesh(GrPrimitiveType::kTriangles);
+            GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
             int maxInstancesPerDraw =
                 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
-            mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerQuad,
-                                     kVerticesPerQuad, flushInfo->fInstancesToFlush,
-                                     maxInstancesPerDraw);
-            mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
+            mesh->setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerQuad,
+                                      kVerticesPerQuad, flushInfo->fInstancesToFlush,
+                                      maxInstancesPerDraw);
+            mesh->setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
             target->draw(flushInfo->fGeometryProcessor, flushInfo->fPipeline,
                          flushInfo->fFixedDynamicState, mesh);
             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 3eda75b..b46af59 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -358,9 +358,10 @@
 
     void drawVertices(Target* target, sk_sp<const GrGeometryProcessor> gp, const GrBuffer* vb,
                       int firstVertex, int count) {
-        GrMesh mesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines : GrPrimitiveType::kTriangles);
-        mesh.setNonIndexedNonInstanced(count);
-        mesh.setVertexData(vb, firstVertex);
+        GrMesh* mesh = target->allocMesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines
+                                                               : GrPrimitiveType::kTriangles);
+        mesh->setNonIndexedNonInstanced(count);
+        mesh->setVertexData(vb, firstVertex);
         auto pipe = fHelper.makePipeline(target);
         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
     }
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 77f194c..89a890e 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -858,19 +858,19 @@
 
         GrPrimitiveType primitiveType =
                 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
-        GrMesh mesh(primitiveType);
+        GrMesh* mesh = target->allocMesh(primitiveType);
         if (fDraws.count() > 1) {
             sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
             if (!ibuffer) {
                 SkDebugf("Could not allocate quad indices\n");
                 return;
             }
-            mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
-                                     GrResourceProvider::QuadCountOfQuadBuffer());
+            mesh->setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
+                                      GrResourceProvider::QuadCountOfQuadBuffer());
         } else {
-            mesh.setNonIndexedNonInstanced(4);
+            mesh->setNonIndexedNonInstanced(4);
         }
-        mesh.setVertexData(vbuffer, vstart);
+        mesh->setVertexData(vbuffer, vstart);
         target->draw(std::move(gp), pipeline, fixedDynamicState, mesh);
     }