Remove GrMesh::SendToGpuImpl

Replaces GrMesh::SendToGpuImpl with direct draw(), drawIndexed(),
drawInstanced(), and drawIndexedInstanced() calls on GrOpsRenderPass.
For now these calls take the index/instance/vertex buffers, but we
will soon switch to binding those in a separate call.

Change-Id: I55d225b5695d88ecc661916c2aeb3f31d21e5585
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/273179
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrMesh.h b/src/gpu/GrMesh.h
index 979ec0a..19e5f22 100644
--- a/src/gpu/GrMesh.h
+++ b/src/gpu/GrMesh.h
@@ -10,6 +10,7 @@
 
 #include "src/gpu/GrBuffer.h"
 #include "src/gpu/GrGpuBuffer.h"
+#include "src/gpu/GrOpsRenderPass.h"
 
 class GrPrimitiveProcessor;
 
@@ -57,23 +58,7 @@
 
     void setVertexData(sk_sp<const GrBuffer> vertexBuffer, int baseVertex = 0);
 
-    class SendToGpuImpl {
-    public:
-        virtual void sendArrayMeshToGpu(GrPrimitiveType, const GrMesh&, int vertexCount,
-                                        int baseVertex) = 0;
-        virtual void sendIndexedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount,
-                                          int baseIndex, uint16_t minIndexValue,
-                                          uint16_t maxIndexValue, int baseVertex) = 0;
-        virtual void sendInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int vertexCount,
-                                            int baseVertex, int instanceCount,
-                                            int baseInstance) = 0;
-        virtual void sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount,
-                                                   int baseIndex, int baseVertex, int instanceCount,
-                                                   int baseInstance) = 0;
-        virtual ~SendToGpuImpl() {}
-    };
-
-    void sendToGpu(GrPrimitiveType, SendToGpuImpl*) const;
+    void draw(GrOpsRenderPass*) const;
 
 private:
     enum class Flags : uint8_t {
@@ -212,32 +197,33 @@
     fBaseVertex = baseVertex;
 }
 
-inline void GrMesh::sendToGpu(GrPrimitiveType primitiveType, SendToGpuImpl* impl) const {
+inline void GrMesh::draw(GrOpsRenderPass* opsRenderPass) const {
     if (this->isInstanced()) {
         if (!this->isIndexed()) {
-            impl->sendInstancedMeshToGpu(primitiveType, *this, fInstanceNonIndexData.fVertexCount,
-                                         fBaseVertex, fInstanceData.fInstanceCount,
-                                         fInstanceData.fBaseInstance);
+            opsRenderPass->drawInstanced(fInstanceBuffer.get(), fInstanceData.fInstanceCount,
+                                         fInstanceData.fBaseInstance, fVertexBuffer.get(),
+                                         fInstanceNonIndexData.fVertexCount, fBaseVertex);
         } else {
-            impl->sendIndexedInstancedMeshToGpu(
-                    primitiveType, *this, fInstanceIndexData.fIndexCount, 0, fBaseVertex,
-                    fInstanceData.fInstanceCount, fInstanceData.fBaseInstance);
+            opsRenderPass->drawIndexedInstanced(
+                    fIndexBuffer.get(), fInstanceIndexData.fIndexCount, 0, this->primitiveRestart(),
+                    fInstanceBuffer.get(), fInstanceData.fInstanceCount,
+                    fInstanceData.fBaseInstance, fVertexBuffer.get(), fBaseVertex);
         }
         return;
     }
 
     if (!this->isIndexed()) {
         SkASSERT(fNonIndexNonInstanceData.fVertexCount > 0);
-        impl->sendArrayMeshToGpu(primitiveType, *this, fNonIndexNonInstanceData.fVertexCount,
-                                 fBaseVertex);
+        opsRenderPass->draw(fVertexBuffer.get(), fNonIndexNonInstanceData.fVertexCount,
+                            fBaseVertex);
         return;
     }
 
     if (0 == fIndexData.fPatternRepeatCount) {
-        impl->sendIndexedMeshToGpu(primitiveType, *this, fIndexData.fIndexCount,
-                                   fNonPatternIndexData.fBaseIndex,
-                                   fNonPatternIndexData.fMinIndexValue,
-                                   fNonPatternIndexData.fMaxIndexValue, fBaseVertex);
+        opsRenderPass->drawIndexed(
+                fIndexBuffer.get(), fIndexData.fIndexCount, fNonPatternIndexData.fBaseIndex,
+                this->primitiveRestart(), fNonPatternIndexData.fMinIndexValue,
+                fNonPatternIndexData.fMaxIndexValue, fVertexBuffer.get(), fBaseVertex);
         return;
     }
 
@@ -251,8 +237,8 @@
         int minIndexValue = 0;
         int maxIndexValue = fPatternData.fVertexCount * repeatCount - 1;
         SkASSERT(!(fFlags & Flags::kUsePrimitiveRestart));
-        impl->sendIndexedMeshToGpu(primitiveType, *this, indexCount, 0, minIndexValue,
-                                   maxIndexValue,
+        opsRenderPass->drawIndexed(fIndexBuffer.get(), indexCount, 0, this->primitiveRestart(),
+                                   minIndexValue, maxIndexValue, fVertexBuffer.get(),
                                    fBaseVertex + fPatternData.fVertexCount * baseRepetition);
         baseRepetition += repeatCount;
     } while (baseRepetition < fIndexData.fPatternRepeatCount);
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index ed82228..0eb38eb 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -145,26 +145,71 @@
             this->bindTextures(programInfo.primProc(), programInfo.pipeline(),
                                programInfo.dynamicPrimProcTextures(i));
         }
-        this->drawMesh(programInfo.primitiveType(), meshes[i]);
+        meshes[i].draw(this);
     }
 }
 
-void GrOpsRenderPass::drawMesh(GrPrimitiveType primitiveType, const GrMesh& mesh) {
+bool GrOpsRenderPass::prepareToDraw() {
     if (DrawPipelineStatus::kOk != fDrawPipelineStatus) {
         SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus);
         this->gpu()->stats()->incNumFailedDraws();
-        return;
+        return false;
     }
-
     SkASSERT(DynamicStateStatus::kUninitialized != fScissorStatus);
     SkASSERT(DynamicStateStatus::kUninitialized != fTextureBindingStatus);
-    SkASSERT(SkToBool(mesh.vertexBuffer()) == fHasVertexAttributes);
-    SkASSERT(SkToBool(mesh.instanceBuffer()) == fHasInstanceAttributes);
-    SkASSERT(GrPrimitiveRestart::kNo == mesh.primitiveRestart() ||
-             this->gpu()->caps()->usePrimitiveRestart());
 
     if (kNone_GrXferBarrierType != fXferBarrierType) {
         this->gpu()->xferBarrier(fRenderTarget, fXferBarrierType);
     }
-    this->onDrawMesh(primitiveType, mesh);
+    return true;
+}
+
+void GrOpsRenderPass::draw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+    if (!this->prepareToDraw()) {
+        return;
+    }
+    SkASSERT(SkToBool(vertexBuffer) == fHasVertexAttributes);
+    this->onDraw(vertexBuffer, vertexCount, baseVertex);
+}
+
+void GrOpsRenderPass::drawIndexed(const GrBuffer* indexBuffer, int indexCount,
+                                  int baseIndex, GrPrimitiveRestart primitiveRestart,
+                                  uint16_t minIndexValue, uint16_t maxIndexValue,
+                                  const GrBuffer* vertexBuffer, int baseVertex) {
+    if (!this->prepareToDraw()) {
+        return;
+    }
+    SkASSERT(GrPrimitiveRestart::kNo == primitiveRestart ||
+             this->gpu()->caps()->usePrimitiveRestart());
+    SkASSERT(SkToBool(vertexBuffer) == fHasVertexAttributes);
+    this->onDrawIndexed(indexBuffer, indexCount, baseIndex, primitiveRestart, minIndexValue,
+                        maxIndexValue, vertexBuffer, baseVertex);
+}
+
+void GrOpsRenderPass::drawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int
+                                    baseInstance, const GrBuffer* vertexBuffer, int vertexCount,
+                                    int baseVertex) {
+    if (!this->prepareToDraw()) {
+        return;
+    }
+    SkASSERT(SkToBool(vertexBuffer) == fHasVertexAttributes);
+    SkASSERT(SkToBool(instanceBuffer) == fHasInstanceAttributes);
+    this->onDrawInstanced(instanceBuffer, instanceCount, baseInstance, vertexBuffer, vertexCount,
+                          baseVertex);
+}
+
+void GrOpsRenderPass::drawIndexedInstanced(
+        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
+        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+    if (!this->prepareToDraw()) {
+        return;
+    }
+    SkASSERT(GrPrimitiveRestart::kNo == primitiveRestart ||
+             this->gpu()->caps()->usePrimitiveRestart());
+    SkASSERT(SkToBool(vertexBuffer) == fHasVertexAttributes);
+    SkASSERT(SkToBool(instanceBuffer) == fHasInstanceAttributes);
+    this->onDrawIndexedInstanced(indexBuffer, indexCount, baseIndex, primitiveRestart,
+                                 instanceBuffer, instanceCount, baseInstance, vertexBuffer,
+                                 baseVertex);
 }
diff --git a/src/gpu/GrOpsRenderPass.h b/src/gpu/GrOpsRenderPass.h
index 0bea996..a96c6d5 100644
--- a/src/gpu/GrOpsRenderPass.h
+++ b/src/gpu/GrOpsRenderPass.h
@@ -76,9 +76,18 @@
     // setScissor() and bindTextures() on the client's behalf.
     void drawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount);
 
-    // Draws the given mesh using the current pipeline state. The client must call bindPipeline(),
-    // followed setScissor() and/or bindTextures() if necessary, before using this method.
-    void drawMesh(GrPrimitiveType, const GrMesh&);
+    // These methods issue draws using the current pipeline state. The client must call
+    // bindPipeline(), followed by setScissor() and/or bindTextures() if applicable, before using
+    // these methods.
+    void draw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex);
+    void drawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex, GrPrimitiveRestart,
+                     uint16_t minIndexValue, uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
+                     int baseVertex);
+    void drawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                       const GrBuffer* vertexBuffer, int vertexCount, int baseVertex);
+    void drawIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                              GrPrimitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
+                              int baseInstance, const GrBuffer* vertexBuffer, int baseVertex);
 
     // Performs an upload of vertex data in the middle of a set of a set of draws
     virtual void inlineUpload(GrOpFlushState*, GrDeferredTextureUploadFn&) = 0;
@@ -116,12 +125,24 @@
 private:
     virtual GrGpu* gpu() = 0;
 
+    bool prepareToDraw();
+
     // overridden by backend-specific derived class to perform the rendering command.
     virtual bool onBindPipeline(const GrProgramInfo&, const SkRect& drawBounds) = 0;
     virtual void onSetScissorRect(const SkIRect&) = 0;
     virtual bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
-                                const GrSurfaceProxy* const primProcTextures[] = nullptr) = 0;
-    virtual void onDrawMesh(GrPrimitiveType, const GrMesh&) = 0;
+                                const GrSurfaceProxy* const primProcTextures[]) = 0;
+    virtual void onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) = 0;
+    virtual void onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                               GrPrimitiveRestart, uint16_t minIndexValue, uint16_t maxIndexValue,
+                               const GrBuffer* vertexBuffer, int baseVertex) = 0;
+    virtual void onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
+                                 int baseInstance, const GrBuffer* vertexBuffer, int vertexCount,
+                                 int baseVertex) = 0;
+    virtual void onDrawIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                        GrPrimitiveRestart, const GrBuffer* instanceBuffer,
+                                        int instanceCount, int baseInstance,
+                                        const GrBuffer* vertexBuffer, int baseVertex) = 0;
     virtual void onClear(const GrFixedClip&, const SkPMColor4f&) = 0;
     virtual void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) = 0;
     virtual void onExecuteDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) {}
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 55d7719..55d8ae0 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2323,19 +2323,14 @@
     }
 }
 
-void GrGLGpu::drawMesh(GrRenderTarget* renderTarget, GrPrimitiveType primitiveType,
-                       const GrMesh& mesh) {
+GrGLenum GrGLGpu::prepareToDraw(GrPrimitiveType primitiveType) {
     if (this->glCaps().requiresCullFaceEnableDisableWhenDrawingLinesAfterNonLines() &&
         GrIsPrimTypeLines(primitiveType) && !GrIsPrimTypeLines(fLastPrimitiveType)) {
         GL_CALL(Enable(GR_GL_CULL_FACE));
         GL_CALL(Disable(GR_GL_CULL_FACE));
     }
-
-    mesh.sendToGpu(primitiveType, this);
     fLastPrimitiveType = primitiveType;
-}
 
-static GrGLenum gr_primitive_type_to_gl_mode(GrPrimitiveType primitiveType) {
     switch (primitiveType) {
         case GrPrimitiveType::kTriangles:
             return GR_GL_TRIANGLES;
@@ -2356,15 +2351,14 @@
     SK_ABORT("invalid GrPrimitiveType");
 }
 
-void GrGLGpu::sendArrayMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh, int vertexCount,
-                                 int baseVertex) {
-    const GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
+void GrGLGpu::draw(GrPrimitiveType primitiveType, const GrBuffer* vertexBuffer, int vertexCount,
+                   int baseVertex) {
+    GrGLenum glPrimType = this->prepareToDraw(primitiveType);
     if (this->glCaps().drawArraysBaseVertexIsBroken()) {
-        this->setupGeometry(nullptr, mesh.vertexBuffer(), baseVertex, nullptr, 0,
-                            GrPrimitiveRestart::kNo);
+        this->setupGeometry(nullptr, vertexBuffer, baseVertex, nullptr, 0, GrPrimitiveRestart::kNo);
         GL_CALL(DrawArrays(glPrimType, 0, vertexCount));
     } else {
-        this->setupGeometry(nullptr, mesh.vertexBuffer(), 0, nullptr, 0, GrPrimitiveRestart::kNo);
+        this->setupGeometry(nullptr, vertexBuffer, 0, nullptr, 0, GrPrimitiveRestart::kNo);
         GL_CALL(DrawArrays(glPrimType, baseVertex, vertexCount));
     }
     fStats.incNumDraws();
@@ -2379,15 +2373,13 @@
     }
 }
 
-void GrGLGpu::sendIndexedMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh,
-                                   int indexCount, int baseIndex, uint16_t minIndexValue,
-                                   uint16_t maxIndexValue, int baseVertex) {
-    const GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
-    const GrGLvoid* elementPtr = element_ptr(mesh.indexBuffer(), baseIndex);
-
-    this->setupGeometry(mesh.indexBuffer(), mesh.vertexBuffer(), baseVertex, nullptr, 0,
-                        mesh.primitiveRestart());
-
+void GrGLGpu::drawIndexed(GrPrimitiveType primitiveType, const GrBuffer* indexBuffer,
+                          int indexCount, int baseIndex, GrPrimitiveRestart primitiveRestart,
+                          uint16_t minIndexValue, uint16_t maxIndexValue,
+                          const GrBuffer* vertexBuffer, int baseVertex) {
+    GrGLenum glPrimType = this->prepareToDraw(primitiveType);
+    const GrGLvoid* elementPtr = element_ptr(indexBuffer, baseIndex);
+    this->setupGeometry(indexBuffer, vertexBuffer, baseVertex, nullptr, 0, primitiveRestart);
     if (this->glCaps().drawRangeElementsSupport()) {
         GL_CALL(DrawRangeElements(glPrimType, minIndexValue, maxIndexValue, indexCount,
                                   GR_GL_UNSIGNED_SHORT, elementPtr));
@@ -2397,29 +2389,30 @@
     fStats.incNumDraws();
 }
 
-void GrGLGpu::sendInstancedMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh,
-                                     int vertexCount, int baseVertex, int instanceCount,
-                                     int baseInstance) {
-    GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
+void GrGLGpu::drawInstanced(GrPrimitiveType primitiveType, const GrBuffer* instanceBuffer,
+                            int instanceCount, int baseInstance, const GrBuffer* vertexBuffer,
+                            int vertexCount, int baseVertex) {
+    GrGLenum glPrimType = this->prepareToDraw(primitiveType);
     int maxInstances = this->glCaps().maxInstancesPerDrawWithoutCrashing(instanceCount);
     for (int i = 0; i < instanceCount; i += maxInstances) {
-        this->setupGeometry(nullptr, mesh.vertexBuffer(), 0, mesh.instanceBuffer(),
-                            baseInstance + i, GrPrimitiveRestart::kNo);
+        this->setupGeometry(nullptr, vertexBuffer, 0, instanceBuffer, baseInstance + i,
+                            GrPrimitiveRestart::kNo);
         GL_CALL(DrawArraysInstanced(glPrimType, baseVertex, vertexCount,
                                     std::min(instanceCount - i, maxInstances)));
         fStats.incNumDraws();
     }
 }
 
-void GrGLGpu::sendIndexedInstancedMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh,
-                                            int indexCount, int baseIndex, int baseVertex,
-                                            int instanceCount, int baseInstance) {
-    const GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
-    const GrGLvoid* elementPtr = element_ptr(mesh.indexBuffer(), baseIndex);
+void GrGLGpu::drawIndexedInstanced(
+        GrPrimitiveType primitiveType, const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
+        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+    GrGLenum glPrimType = this->prepareToDraw(primitiveType);
+    const GrGLvoid* elementPtr = element_ptr(indexBuffer, baseIndex);
     int maxInstances = this->glCaps().maxInstancesPerDrawWithoutCrashing(instanceCount);
     for (int i = 0; i < instanceCount; i += maxInstances) {
-        this->setupGeometry(mesh.indexBuffer(), mesh.vertexBuffer(), baseVertex,
-                            mesh.instanceBuffer(), baseInstance + i, mesh.primitiveRestart());
+        this->setupGeometry(indexBuffer, vertexBuffer, baseVertex,
+                            instanceBuffer, baseInstance + i, primitiveRestart);
         GL_CALL(DrawElementsInstanced(glPrimType, indexCount, GR_GL_UNSIGNED_SHORT, elementPtr,
                                       std::min(instanceCount - i, maxInstances)));
         fStats.incNumDraws();
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index de2c53f..f6cc07c 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -31,7 +31,7 @@
 class GrPipeline;
 class GrSwizzle;
 
-class GrGLGpu final : public GrGpu, private GrMesh::SendToGpuImpl {
+class GrGLGpu final : public GrGpu {
 public:
     static sk_sp<GrGpu> Make(sk_sp<const GrGLInterface>, const GrContextOptions&, GrContext*);
     ~GrGLGpu() override;
@@ -81,24 +81,18 @@
         fHWProgram->bindTextures(primProc, pipeline, primProcTextures);
     }
 
-    // The GrGLOpsRenderPass does not buffer up draws before submitting them to the gpu.
-    // Thus this is the implementation of the draw call for the corresponding passthrough function
-    // on GrGLOpsRenderPass.
-    //
-    // The client must call flushGLState before this method.
-    void drawMesh(GrRenderTarget*, GrPrimitiveType, const GrMesh&);
-
-    // GrMesh::SendToGpuImpl methods. These issue the actual GL draw calls.
-    // Marked final as a hint to the compiler to not use virtual dispatch.
-    void sendArrayMeshToGpu(GrPrimitiveType primitiveType, const GrMesh&, int vertexCount,
-                            int baseVertex) final;
-    void sendIndexedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount, int baseIndex,
-                              uint16_t minIndexValue, uint16_t maxIndexValue, int baseVertex) final;
-    void sendInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int vertexCount, int baseVertex,
-                                int instanceCount, int baseInstance) final;
-    void sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount,
-                                       int baseIndex, int baseVertex, int instanceCount,
-                                       int baseInstance) final;
+    // These methods issue draws using the current GL state. The client must call flushGLState,
+    // followed by flushScissorRect and/or bindTextures if applicable, before using these method.
+    void draw(GrPrimitiveType, const GrBuffer* vertexBuffer, int vertexCount, int baseVertex);
+    void drawIndexed(GrPrimitiveType, const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                     GrPrimitiveRestart, uint16_t minIndexValue, uint16_t maxIndexValue,
+                     const GrBuffer* vertexBuffer, int baseVertex);
+    void drawInstanced(GrPrimitiveType, const GrBuffer* instanceBuffer, int instanceCount, int
+                       baseInstance, const GrBuffer* vertexBuffer, int vertexCount, int baseVertex);
+    void drawIndexedInstanced(GrPrimitiveType, const GrBuffer* indexBuffer, int indexCount,
+                              int baseIndex, GrPrimitiveRestart, const GrBuffer* instanceBuffer,
+                              int instanceCount, int baseInstance, const GrBuffer* vertexBuffer,
+                              int baseVertex);
 
     // The GrGLOpsRenderPass does not buffer up draws before submitting them to the gpu.
     // Thus this is the implementation of the clear call for the corresponding passthrough function
@@ -307,6 +301,9 @@
                        int baseInstance,
                        GrPrimitiveRestart);
 
+    // Applies any necessary workarounds and returns the GL primitive type to use in draw calls.
+    GrGLenum prepareToDraw(GrPrimitiveType primitiveType);
+
     void flushBlendAndColorWrite(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle&);
 
     bool onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
diff --git a/src/gpu/gl/GrGLOpsRenderPass.cpp b/src/gpu/gl/GrGLOpsRenderPass.cpp
index 217fdb0..60af18e 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.cpp
+++ b/src/gpu/gl/GrGLOpsRenderPass.cpp
@@ -9,6 +9,7 @@
 
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrFixedClip.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTargetPriv.h"
 
 void GrGLOpsRenderPass::set(GrRenderTarget* rt, const SkIRect& contentBounds,
@@ -23,3 +24,58 @@
     fColorLoadAndStoreInfo = colorInfo;
     fStencilLoadAndStoreInfo = stencilInfo;
 }
+
+bool GrGLOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,
+                                       const SkRect& drawBounds) {
+    fPrimitiveType = programInfo.primitiveType();
+    return fGpu->flushGLState(fRenderTarget, programInfo);
+}
+
+void GrGLOpsRenderPass::onSetScissorRect(const SkIRect& scissor) {
+    fGpu->flushScissorRect(scissor, fRenderTarget->width(), fRenderTarget->height(), fOrigin);
+}
+
+bool GrGLOpsRenderPass::onBindTextures(const GrPrimitiveProcessor& primProc,
+                                       const GrPipeline& pipeline,
+                                       const GrSurfaceProxy* const primProcTextures[]) {
+    fGpu->bindTextures(primProc, pipeline, primProcTextures);
+    return true;
+}
+
+void GrGLOpsRenderPass::onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+    fGpu->draw(fPrimitiveType, vertexBuffer, vertexCount, baseVertex);
+}
+
+void GrGLOpsRenderPass::onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                      GrPrimitiveRestart primitiveRestart, uint16_t minIndexValue,
+                                      uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
+                                      int baseVertex) {
+    fGpu->drawIndexed(fPrimitiveType, indexBuffer, indexCount, baseIndex, primitiveRestart,
+                      minIndexValue, maxIndexValue, vertexBuffer, baseVertex);
+}
+
+void GrGLOpsRenderPass::onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
+                                        int baseInstance, const GrBuffer* vertexBuffer,
+                                        int vertexCount, int baseVertex) {
+    fGpu->drawInstanced(fPrimitiveType, instanceBuffer, instanceCount, baseInstance, vertexBuffer,
+                      vertexCount, baseVertex);
+}
+
+void GrGLOpsRenderPass::onDrawIndexedInstanced(
+        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
+        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+    fGpu->drawIndexedInstanced(fPrimitiveType, indexBuffer, indexCount, baseIndex, primitiveRestart,
+                               instanceBuffer, instanceCount, baseInstance, vertexBuffer,
+                               baseVertex);
+}
+
+void GrGLOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
+    fGpu->clear(clip, color, fRenderTarget, fOrigin);
+}
+
+void GrGLOpsRenderPass::onClearStencilClip(const GrFixedClip& clip,
+                                           bool insideStencilMask) {
+    fGpu->clearStencilClip(clip, insideStencilMask, fRenderTarget, fOrigin);
+}
+
diff --git a/src/gpu/gl/GrGLOpsRenderPass.h b/src/gpu/gl/GrGLOpsRenderPass.h
index 9a00aec..e79558d 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.h
+++ b/src/gpu/gl/GrGLOpsRenderPass.h
@@ -49,37 +49,31 @@
 private:
     GrGpu* gpu() override { return fGpu; }
 
-    bool onBindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) override {
-        return fGpu->flushGLState(fRenderTarget, programInfo);
-    }
-
-    void onSetScissorRect(const SkIRect& scissor) override {
-        fGpu->flushScissorRect(scissor, fRenderTarget->width(), fRenderTarget->height(), fOrigin);
-    }
-
+    bool onBindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) override;
+    void onSetScissorRect(const SkIRect& scissor) override;
     bool onBindTextures(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline,
-                        const GrSurfaceProxy* const primProcTextures[]) override {
-        fGpu->bindTextures(primProc, pipeline, primProcTextures);
-        return true;
-    }
+                        const GrSurfaceProxy* const primProcTextures[]) override;
+    void onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override;
+    void onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                       GrPrimitiveRestart, uint16_t minIndexValue, uint16_t maxIndexValue,
+                       const GrBuffer* vertexBuffer, int baseVertex) override;
+    void onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                         const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override;
+    void onDrawIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                GrPrimitiveRestart, const GrBuffer* instanceBuffer,
+                                int instanceCount, int baseInstance, const GrBuffer* vertexBuffer,
+                                int baseVertex) override;
+    void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
+    void onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) override;
 
-    void onDrawMesh(GrPrimitiveType primitiveType, const GrMesh& mesh) override {
-        fGpu->drawMesh(fRenderTarget, primitiveType, mesh);
-    }
-
-    void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override {
-        fGpu->clear(clip, color, fRenderTarget, fOrigin);
-    }
-
-    void onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) override {
-        fGpu->clearStencilClip(clip, insideStencilMask, fRenderTarget, fOrigin);
-    }
-
-    GrGLGpu*                fGpu;
-    SkIRect                 fContentBounds;
-    LoadAndStoreInfo        fColorLoadAndStoreInfo;
+    GrGLGpu* fGpu;
+    SkIRect fContentBounds;
+    LoadAndStoreInfo fColorLoadAndStoreInfo;
     StencilLoadAndStoreInfo fStencilLoadAndStoreInfo;
 
+    // Per-pipeline state.
+    GrPrimitiveType fPrimitiveType;
+
     typedef GrOpsRenderPass INHERITED;
 };
 
diff --git a/src/gpu/mock/GrMockOpsRenderPass.h b/src/gpu/mock/GrMockOpsRenderPass.h
index d5af562..756a14c 100644
--- a/src/gpu/mock/GrMockOpsRenderPass.h
+++ b/src/gpu/mock/GrMockOpsRenderPass.h
@@ -42,14 +42,32 @@
                         const GrSurfaceProxy* const primProcTextures[]) override {
         return true;
     }
-    void onDrawMesh(GrPrimitiveType, const GrMesh&) override {
-        this->markRenderTargetDirty();
-        ++fNumDraws;
+    void onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override {
+        this->dummyDraw();
+    }
+    void onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                       GrPrimitiveRestart, uint16_t minIndexValue, uint16_t maxIndexValue,
+                       const GrBuffer* vertexBuffer, int baseVertex) override {
+        this->dummyDraw();
+    }
+    void onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                         const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override {
+        this->dummyDraw();
+    }
+    void onDrawIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                GrPrimitiveRestart, const GrBuffer* instanceBuffer,
+                                int instanceCount, int baseInstance, const GrBuffer* vertexBuffer,
+                                int baseVertex) override {
+        this->dummyDraw();
     }
     void onClear(const GrFixedClip&, const SkPMColor4f&) override {
         this->markRenderTargetDirty();
     }
     void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) override {}
+    void dummyDraw() {
+        this->markRenderTargetDirty();
+        ++fNumDraws;
+    }
     void markRenderTargetDirty() {
         if (auto* tex = fRenderTarget->asTexture()) {
             tex->texturePriv().markMipMapsDirty();
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.h b/src/gpu/mtl/GrMtlOpsRenderPass.h
index 9613405..90136fe 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.h
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.h
@@ -20,7 +20,7 @@
 class GrMtlPipelineState;
 class GrMtlRenderTarget;
 
-class GrMtlOpsRenderPass : public GrOpsRenderPass, private GrMesh::SendToGpuImpl {
+class GrMtlOpsRenderPass : public GrOpsRenderPass {
 public:
     GrMtlOpsRenderPass(GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin,
                        const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
@@ -43,7 +43,16 @@
     void onSetScissorRect(const SkIRect&) override;
     bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
                         const GrSurfaceProxy* const primProcTextures[]) override;
-    void onDrawMesh(GrPrimitiveType, const GrMesh&) override;
+    void onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override;
+    void onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                       GrPrimitiveRestart, uint16_t minIndexValue, uint16_t maxIndexValue,
+                       const GrBuffer* vertexBuffer, int baseVertex) override;
+    void onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                         const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override;
+    void onDrawIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                GrPrimitiveRestart, const GrBuffer* instanceBuffer,
+                                int instanceCount, int baseInstance, const GrBuffer* vertexBuffer,
+                                int baseVertex) override;
 
     void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
 
@@ -55,18 +64,6 @@
     void bindGeometry(const GrBuffer* vertexBuffer, size_t vertexOffset,
                       const GrBuffer* instanceBuffer);
 
-    // GrMesh::SendToGpuImpl methods. These issue the actual Metal draw commands.
-    // Marked final as a hint to the compiler to not use virtual dispatch.
-    void sendArrayMeshToGpu(GrPrimitiveType, const GrMesh&, int vertexCount, int baseVertex) final;
-    void sendIndexedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount, int baseIndex,
-                              uint16_t /*minIndexValue*/, uint16_t /*maxIndexValue*/,
-                              int baseVertex) final;
-    void sendInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int vertexCount, int baseVertex,
-                                int instanceCount, int baseInstance) final;
-    void sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount,
-                                       int baseIndex, int baseVertex, int instanceCount,
-                                       int baseInstance) final;
-
     void setVertexBuffer(id<MTLRenderCommandEncoder>, const GrMtlBuffer*, size_t offset,
                          size_t index);
     void resetBufferBindings();
@@ -76,6 +73,7 @@
 
     id<MTLRenderCommandEncoder> fActiveRenderCmdEncoder;
     GrMtlPipelineState*         fActivePipelineState = nullptr;
+    MTLPrimitiveType            fActivePrimitiveType;
     MTLRenderPassDescriptor*    fRenderPassDesc;
     SkRect                      fBounds;
     size_t                      fCurrentVertexStride;
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm
index 8c902d5..7729c16 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.mm
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm
@@ -49,6 +49,24 @@
     fActiveRenderCmdEncoder = nil;
 }
 
+static MTLPrimitiveType gr_to_mtl_primitive(GrPrimitiveType primitiveType) {
+    const static MTLPrimitiveType mtlPrimitiveType[] {
+        MTLPrimitiveTypeTriangle,
+        MTLPrimitiveTypeTriangleStrip,
+        MTLPrimitiveTypePoint,
+        MTLPrimitiveTypeLine,
+        MTLPrimitiveTypeLineStrip
+    };
+    static_assert((int)GrPrimitiveType::kTriangles == 0);
+    static_assert((int)GrPrimitiveType::kTriangleStrip == 1);
+    static_assert((int)GrPrimitiveType::kPoints == 2);
+    static_assert((int)GrPrimitiveType::kLines == 3);
+    static_assert((int)GrPrimitiveType::kLineStrip == 4);
+
+    SkASSERT(primitiveType <= GrPrimitiveType::kLineStrip);
+    return mtlPrimitiveType[static_cast<int>(primitiveType)];
+}
+
 bool GrMtlOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,
                                         const SkRect& drawBounds) {
     fActivePipelineState = fGpu->resourceProvider().findOrCreateCompatiblePipelineState(
@@ -83,6 +101,7 @@
                                                                        fRenderTarget->height()));
     }
 
+    fActivePrimitiveType = gr_to_mtl_primitive(programInfo.primitiveType());
     fBounds.join(drawBounds);
     return true;
 }
@@ -104,12 +123,6 @@
     return true;
 }
 
-void GrMtlOpsRenderPass::onDrawMesh(GrPrimitiveType primitiveType, const GrMesh& mesh) {
-    SkASSERT(fActivePipelineState);
-    SkASSERT(nil != fActiveRenderCmdEncoder);
-    mesh.sendToGpu(primitiveType, this);
-}
-
 void GrMtlOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
     // We should never end up here since all clears should either be done as draws or load ops in
     // metal. If we hit this assert then we missed a chance to set a load op on the
@@ -233,24 +246,6 @@
     fActiveRenderCmdEncoder = nil;
 }
 
-static MTLPrimitiveType gr_to_mtl_primitive(GrPrimitiveType primitiveType) {
-    const static MTLPrimitiveType mtlPrimitiveType[] {
-        MTLPrimitiveTypeTriangle,
-        MTLPrimitiveTypeTriangleStrip,
-        MTLPrimitiveTypePoint,
-        MTLPrimitiveTypeLine,
-        MTLPrimitiveTypeLineStrip
-    };
-    static_assert((int)GrPrimitiveType::kTriangles == 0);
-    static_assert((int)GrPrimitiveType::kTriangleStrip == 1);
-    static_assert((int)GrPrimitiveType::kPoints == 2);
-    static_assert((int)GrPrimitiveType::kLines == 3);
-    static_assert((int)GrPrimitiveType::kLineStrip == 4);
-
-    SkASSERT(primitiveType <= GrPrimitiveType::kLineStrip);
-    return mtlPrimitiveType[static_cast<int>(primitiveType)];
-}
-
 void GrMtlOpsRenderPass::bindGeometry(const GrBuffer* vertexBuffer,
                                       size_t vertexOffset,
                                       const GrBuffer* instanceBuffer) {
@@ -271,34 +266,37 @@
     }
 }
 
-void GrMtlOpsRenderPass::sendArrayMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh,
-                                            int vertexCount, int baseVertex) {
-    this->bindGeometry(mesh.vertexBuffer(), 0, nullptr);
+void GrMtlOpsRenderPass::onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+    SkASSERT(fActivePipelineState);
+    SkASSERT(nil != fActiveRenderCmdEncoder);
+    this->bindGeometry(vertexBuffer, 0, nullptr);
 
-    [fActiveRenderCmdEncoder drawPrimitives:gr_to_mtl_primitive(primitiveType)
+    [fActiveRenderCmdEncoder drawPrimitives:fActivePrimitiveType
                                 vertexStart:baseVertex
                                 vertexCount:vertexCount];
 }
 
-void GrMtlOpsRenderPass::sendIndexedMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh,
-                                              int indexCount, int baseIndex,
-                                              uint16_t /*minIndexValue*/,
-                                              uint16_t /*maxIndexValue*/, int baseVertex) {
-    this->bindGeometry(mesh.vertexBuffer(), fCurrentVertexStride*baseVertex, nullptr);
+void GrMtlOpsRenderPass::onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                       GrPrimitiveRestart primitiveRestart, uint16_t minIndexValue,
+                                       uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
+                                       int baseVertex) {
+    SkASSERT(fActivePipelineState);
+    SkASSERT(nil != fActiveRenderCmdEncoder);
+    this->bindGeometry(vertexBuffer, fCurrentVertexStride*baseVertex, nullptr);
 
     id<MTLBuffer> mtlIndexBuffer = nil;
-    if (mesh.indexBuffer()) {
-        SkASSERT(!mesh.indexBuffer()->isCpuBuffer());
-        SkASSERT(!static_cast<const GrGpuBuffer*>(mesh.indexBuffer())->isMapped());
+    if (indexBuffer) {
+        SkASSERT(!indexBuffer->isCpuBuffer());
+        SkASSERT(!static_cast<const GrGpuBuffer*>(indexBuffer)->isMapped());
 
-        mtlIndexBuffer = static_cast<const GrMtlBuffer*>(mesh.indexBuffer())->mtlBuffer();
+        mtlIndexBuffer = static_cast<const GrMtlBuffer*>(indexBuffer)->mtlBuffer();
         SkASSERT(mtlIndexBuffer);
     }
 
-    SkASSERT(mesh.primitiveRestart() == GrPrimitiveRestart::kNo);
-    size_t indexOffset = static_cast<const GrMtlBuffer*>(mesh.indexBuffer())->offset() +
+    SkASSERT(primitiveRestart == GrPrimitiveRestart::kNo);
+    size_t indexOffset = static_cast<const GrMtlBuffer*>(indexBuffer)->offset() +
                          sizeof(uint16_t) * baseIndex;
-    [fActiveRenderCmdEncoder drawIndexedPrimitives:gr_to_mtl_primitive(primitiveType)
+    [fActiveRenderCmdEncoder drawIndexedPrimitives:fActivePrimitiveType
                                         indexCount:indexCount
                                          indexType:MTLIndexTypeUInt16
                                        indexBuffer:mtlIndexBuffer
@@ -306,13 +304,15 @@
     fGpu->stats()->incNumDraws();
 }
 
-void GrMtlOpsRenderPass::sendInstancedMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh,
-                                                int vertexCount, int baseVertex, int instanceCount,
-                                                int baseInstance) {
-    this->bindGeometry(mesh.vertexBuffer(), 0, mesh.instanceBuffer());
+void GrMtlOpsRenderPass::onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
+                                         int baseInstance, const GrBuffer* vertexBuffer,
+                                         int vertexCount, int baseVertex) {
+    SkASSERT(fActivePipelineState);
+    SkASSERT(nil != fActiveRenderCmdEncoder);
+    this->bindGeometry(vertexBuffer, 0, instanceBuffer);
 
     if (@available(macOS 10.11, iOS 9.0, *)) {
-        [fActiveRenderCmdEncoder drawPrimitives:gr_to_mtl_primitive(primitiveType)
+        [fActiveRenderCmdEncoder drawPrimitives:fActivePrimitiveType
                                     vertexStart:baseVertex
                                     vertexCount:vertexCount
                                   instanceCount:instanceCount
@@ -322,27 +322,29 @@
     }
 }
 
-void GrMtlOpsRenderPass::sendIndexedInstancedMeshToGpu(GrPrimitiveType primitiveType,
-                                                       const GrMesh& mesh, int indexCount,
-                                                       int baseIndex, int baseVertex,
-                                                       int instanceCount, int baseInstance) {
-    this->bindGeometry(mesh.vertexBuffer(), 0, mesh.instanceBuffer());
+void GrMtlOpsRenderPass::onDrawIndexedInstanced(
+        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
+        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+    SkASSERT(fActivePipelineState);
+    SkASSERT(nil != fActiveRenderCmdEncoder);
+    this->bindGeometry(vertexBuffer, 0, instanceBuffer);
 
     id<MTLBuffer> mtlIndexBuffer = nil;
-    if (mesh.indexBuffer()) {
-        SkASSERT(!mesh.indexBuffer()->isCpuBuffer());
-        SkASSERT(!static_cast<const GrGpuBuffer*>(mesh.indexBuffer())->isMapped());
+    if (indexBuffer) {
+        SkASSERT(!indexBuffer->isCpuBuffer());
+        SkASSERT(!static_cast<const GrGpuBuffer*>(indexBuffer)->isMapped());
 
-        mtlIndexBuffer = static_cast<const GrMtlBuffer*>(mesh.indexBuffer())->mtlBuffer();
+        mtlIndexBuffer = static_cast<const GrMtlBuffer*>(indexBuffer)->mtlBuffer();
         SkASSERT(mtlIndexBuffer);
     }
 
-    SkASSERT(mesh.primitiveRestart() == GrPrimitiveRestart::kNo);
-    size_t indexOffset = static_cast<const GrMtlBuffer*>(mesh.indexBuffer())->offset() +
+    SkASSERT(primitiveRestart == GrPrimitiveRestart::kNo);
+    size_t indexOffset = static_cast<const GrMtlBuffer*>(indexBuffer)->offset() +
                          sizeof(uint16_t) * baseIndex;
 
     if (@available(macOS 10.11, iOS 9.0, *)) {
-        [fActiveRenderCmdEncoder drawIndexedPrimitives:gr_to_mtl_primitive(primitiveType)
+        [fActiveRenderCmdEncoder drawIndexedPrimitives:fActivePrimitiveType
                                             indexCount:indexCount
                                              indexType:MTLIndexTypeUInt16
                                            indexBuffer:mtlIndexBuffer
diff --git a/src/gpu/vk/GrVkOpsRenderPass.cpp b/src/gpu/vk/GrVkOpsRenderPass.cpp
index 92319a5..695e2c2 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.cpp
+++ b/src/gpu/vk/GrVkOpsRenderPass.cpp
@@ -586,43 +586,45 @@
                                                      this->currentCommandBuffer());
 }
 
-void GrVkOpsRenderPass::onDrawMesh(GrPrimitiveType primitiveType, const GrMesh& mesh) {
+void GrVkOpsRenderPass::onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
+                                        int baseInstance, const GrBuffer* vertexBuffer,
+                                        int vertexCount, int baseVertex) {
     if (!fCurrentRenderPass) {
         SkASSERT(fGpu->isDeviceLost());
         return;
     }
-
     SkASSERT(fCurrentPipelineState);
-    mesh.sendToGpu(primitiveType, this);
-    fCurrentCBIsEmpty = false;
-}
-
-void GrVkOpsRenderPass::sendInstancedMeshToGpu(GrPrimitiveType, const GrMesh& mesh, int vertexCount,
-                                               int baseVertex, int instanceCount,
-                                               int baseInstance) {
-    SkASSERT(!mesh.vertexBuffer() || !mesh.vertexBuffer()->isCpuBuffer());
-    SkASSERT(!mesh.instanceBuffer() || !mesh.instanceBuffer()->isCpuBuffer());
-    auto gpuVertexBuffer = static_cast<const GrGpuBuffer*>(mesh.vertexBuffer());
-    auto gpuInstanceBuffer = static_cast<const GrGpuBuffer*>(mesh.instanceBuffer());
+    SkASSERT(!vertexBuffer || !vertexBuffer->isCpuBuffer());
+    SkASSERT(!instanceBuffer || !instanceBuffer->isCpuBuffer());
+    auto gpuVertexBuffer = static_cast<const GrGpuBuffer*>(vertexBuffer);
+    auto gpuInstanceBuffer = static_cast<const GrGpuBuffer*>(instanceBuffer);
     this->bindGeometry(nullptr, gpuVertexBuffer, gpuInstanceBuffer);
     this->currentCommandBuffer()->draw(fGpu, vertexCount, instanceCount, baseVertex, baseInstance);
     fGpu->stats()->incNumDraws();
+    fCurrentCBIsEmpty = false;
 }
 
-void GrVkOpsRenderPass::sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrMesh& mesh,
-                                                      int indexCount, int baseIndex, int baseVertex,
-                                                      int instanceCount, int baseInstance) {
-    SkASSERT(mesh.primitiveRestart() == GrPrimitiveRestart::kNo);
-    SkASSERT(!mesh.vertexBuffer() || !mesh.vertexBuffer()->isCpuBuffer());
-    SkASSERT(!mesh.instanceBuffer() || !mesh.instanceBuffer()->isCpuBuffer());
-    SkASSERT(!mesh.indexBuffer()->isCpuBuffer());
-    auto gpuIndexxBuffer = static_cast<const GrGpuBuffer*>(mesh.indexBuffer());
-    auto gpuVertexBuffer = static_cast<const GrGpuBuffer*>(mesh.vertexBuffer());
-    auto gpuInstanceBuffer = static_cast<const GrGpuBuffer*>(mesh.instanceBuffer());
+void GrVkOpsRenderPass::onDrawIndexedInstanced(
+        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
+        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
+    SkASSERT(fCurrentPipelineState);
+    SkASSERT(primitiveRestart == GrPrimitiveRestart::kNo);
+    SkASSERT(!vertexBuffer || !vertexBuffer->isCpuBuffer());
+    SkASSERT(!instanceBuffer || !instanceBuffer->isCpuBuffer());
+    SkASSERT(!indexBuffer->isCpuBuffer());
+    auto gpuIndexxBuffer = static_cast<const GrGpuBuffer*>(indexBuffer);
+    auto gpuVertexBuffer = static_cast<const GrGpuBuffer*>(vertexBuffer);
+    auto gpuInstanceBuffer = static_cast<const GrGpuBuffer*>(instanceBuffer);
     this->bindGeometry(gpuIndexxBuffer, gpuVertexBuffer, gpuInstanceBuffer);
     this->currentCommandBuffer()->drawIndexed(fGpu, indexCount, instanceCount,
                                               baseIndex, baseVertex, baseInstance);
     fGpu->stats()->incNumDraws();
+    fCurrentCBIsEmpty = false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkOpsRenderPass.h b/src/gpu/vk/GrVkOpsRenderPass.h
index 96bbe56..7ecdbe8 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.h
+++ b/src/gpu/vk/GrVkOpsRenderPass.h
@@ -23,7 +23,7 @@
 class GrVkRenderTarget;
 class GrVkSecondaryCommandBuffer;
 
-class GrVkOpsRenderPass : public GrOpsRenderPass, private GrMesh::SendToGpuImpl {
+class GrVkOpsRenderPass : public GrOpsRenderPass {
 public:
     GrVkOpsRenderPass(GrVkGpu*);
 
@@ -72,27 +72,22 @@
     void onSetScissorRect(const SkIRect&) override;
     bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
                         const GrSurfaceProxy* const primProcTextures[]) override;
-    void onDrawMesh(GrPrimitiveType, const GrMesh&) override;
-
-    // GrMesh::SendToGpuImpl methods. These issue the actual Vulkan draw commands.
-    // Marked final as a hint to the compiler to not use virtual dispatch.
-    void sendArrayMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh, int vertexCount,
-                            int baseVertex) final {
-        SkASSERT(!mesh.instanceBuffer());
-        this->sendInstancedMeshToGpu(primitiveType, mesh, vertexCount, baseVertex, 1, 0);
+    void onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override {
+        this->onDrawInstanced(nullptr, 1, 0, vertexBuffer, vertexCount, baseVertex);
     }
-    void sendIndexedMeshToGpu(GrPrimitiveType primitiveType, const GrMesh& mesh, int indexCount,
-                              int baseIndex, uint16_t minIndexValue, uint16_t maxIndexValue,
-                              int baseVertex) final {
-        SkASSERT(!mesh.instanceBuffer());
-        this->sendIndexedInstancedMeshToGpu(primitiveType, mesh, indexCount, baseIndex, baseVertex,
-                                            1, 0);
+    void onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                       GrPrimitiveRestart primitiveRestart, uint16_t minIndexValue,
+                       uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
+                       int baseVertex) override {
+        this->onDrawIndexedInstanced(indexBuffer, indexCount, baseIndex, primitiveRestart, nullptr,
+                                     1, 0, vertexBuffer, baseVertex);
     }
-    void sendInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int vertexCount, int baseVertex,
-                                int instanceCount, int baseInstance) final;
-    void sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrMesh&, int indexCount,
-                                       int baseIndex, int baseVertex, int instanceCount,
-                                       int baseInstance) final;
+    void onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                         const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override;
+    void onDrawIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                GrPrimitiveRestart, const GrBuffer* instanceBuffer,
+                                int instanceCount, int baseInstance, const GrBuffer* vertexBuffer,
+                                int baseVertex) override;
 
     void onClear(const GrFixedClip&, const SkPMColor4f& color) override;