Add a bindBuffers() call to GrOpsRenderPass

Adds a bindBuffers() call and removes the GrBuffer arguments from the
draw calls.

Change-Id: I43c2dd8afe80c41e48c1d9d5210affcfe6f095fc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/273840
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrMesh.h b/src/gpu/GrMesh.h
index 19e5f22..a739687 100644
--- a/src/gpu/GrMesh.h
+++ b/src/gpu/GrMesh.h
@@ -200,30 +200,33 @@
 inline void GrMesh::draw(GrOpsRenderPass* opsRenderPass) const {
     if (this->isInstanced()) {
         if (!this->isIndexed()) {
-            opsRenderPass->drawInstanced(fInstanceBuffer.get(), fInstanceData.fInstanceCount,
-                                         fInstanceData.fBaseInstance, fVertexBuffer.get(),
+            opsRenderPass->bindBuffers(nullptr, fInstanceBuffer.get(),  fVertexBuffer.get());
+            opsRenderPass->drawInstanced(fInstanceData.fInstanceCount, fInstanceData.fBaseInstance,
                                          fInstanceNonIndexData.fVertexCount, fBaseVertex);
         } else {
+            opsRenderPass->bindBuffers(fIndexBuffer.get(), fInstanceBuffer.get(),
+                                       fVertexBuffer.get(), this->primitiveRestart());
             opsRenderPass->drawIndexedInstanced(
-                    fIndexBuffer.get(), fInstanceIndexData.fIndexCount, 0, this->primitiveRestart(),
-                    fInstanceBuffer.get(), fInstanceData.fInstanceCount,
-                    fInstanceData.fBaseInstance, fVertexBuffer.get(), fBaseVertex);
+                    fInstanceIndexData.fIndexCount, 0, fInstanceData.fInstanceCount,
+                    fInstanceData.fBaseInstance, fBaseVertex);
         }
         return;
     }
 
     if (!this->isIndexed()) {
         SkASSERT(fNonIndexNonInstanceData.fVertexCount > 0);
-        opsRenderPass->draw(fVertexBuffer.get(), fNonIndexNonInstanceData.fVertexCount,
-                            fBaseVertex);
+        opsRenderPass->bindBuffers(nullptr, nullptr, fVertexBuffer.get());
+        opsRenderPass->draw(fNonIndexNonInstanceData.fVertexCount, fBaseVertex);
         return;
     }
 
+    opsRenderPass->bindBuffers(fIndexBuffer.get(), nullptr, fVertexBuffer.get(),
+                               this->primitiveRestart());
+
     if (0 == fIndexData.fPatternRepeatCount) {
-        opsRenderPass->drawIndexed(
-                fIndexBuffer.get(), fIndexData.fIndexCount, fNonPatternIndexData.fBaseIndex,
-                this->primitiveRestart(), fNonPatternIndexData.fMinIndexValue,
-                fNonPatternIndexData.fMaxIndexValue, fVertexBuffer.get(), fBaseVertex);
+        opsRenderPass->drawIndexed(fIndexData.fIndexCount, fNonPatternIndexData.fBaseIndex,
+                                   fNonPatternIndexData.fMinIndexValue,
+                                   fNonPatternIndexData.fMaxIndexValue, fBaseVertex);
         return;
     }
 
@@ -237,8 +240,7 @@
         int minIndexValue = 0;
         int maxIndexValue = fPatternData.fVertexCount * repeatCount - 1;
         SkASSERT(!(fFlags & Flags::kUsePrimitiveRestart));
-        opsRenderPass->drawIndexed(fIndexBuffer.get(), indexCount, 0, this->primitiveRestart(),
-                                   minIndexValue, maxIndexValue, fVertexBuffer.get(),
+        opsRenderPass->drawIndexed(indexCount, 0, minIndexValue, maxIndexValue,
                                    fBaseVertex + fPatternData.fVertexCount * baseRepetition);
         baseRepetition += repeatCount;
     } while (baseRepetition < fIndexData.fPatternRepeatCount);
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index 0eb38eb..9ce11a0 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -91,8 +91,11 @@
     }
     fTextureBindingStatus = (hasTextures) ?
             DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled;
-    fHasVertexAttributes = programInfo.primProc().hasVertexAttributes();
-    fHasInstanceAttributes = programInfo.primProc().hasInstanceAttributes();
+    fHasIndexBuffer = false;
+    fInstanceBufferStatus = (programInfo.primProc().hasInstanceAttributes()) ?
+            DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled;
+    fVertexBufferStatus = (programInfo.primProc().hasVertexAttributes()) ?
+            DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled;
 #endif
 
     fDrawPipelineStatus = DrawPipelineStatus::kOk;
@@ -149,6 +152,36 @@
     }
 }
 
+void GrOpsRenderPass::bindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                                  const GrBuffer* vertexBuffer, GrPrimitiveRestart primRestart) {
+    if (DrawPipelineStatus::kOk != fDrawPipelineStatus) {
+        SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus);
+        return;
+    }
+
+#ifdef SK_DEBUG
+    if (indexBuffer) {
+        fHasIndexBuffer = true;
+    }
+
+    SkASSERT((DynamicStateStatus::kDisabled == fInstanceBufferStatus) != SkToBool(instanceBuffer));
+    if (instanceBuffer) {
+        fInstanceBufferStatus = DynamicStateStatus::kConfigured;
+    }
+
+    SkASSERT((DynamicStateStatus::kDisabled == fVertexBufferStatus) != SkToBool(vertexBuffer));
+    if (vertexBuffer) {
+        fVertexBufferStatus = DynamicStateStatus::kConfigured;
+    }
+
+    if (GrPrimitiveRestart::kYes == primRestart) {
+        SkASSERT(this->gpu()->caps()->usePrimitiveRestart());
+    }
+#endif
+
+    this->onBindBuffers(indexBuffer, instanceBuffer, vertexBuffer, primRestart);
+}
+
 bool GrOpsRenderPass::prepareToDraw() {
     if (DrawPipelineStatus::kOk != fDrawPipelineStatus) {
         SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus);
@@ -164,52 +197,45 @@
     return true;
 }
 
-void GrOpsRenderPass::draw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+void GrOpsRenderPass::draw(int vertexCount, int baseVertex) {
     if (!this->prepareToDraw()) {
         return;
     }
-    SkASSERT(SkToBool(vertexBuffer) == fHasVertexAttributes);
-    this->onDraw(vertexBuffer, vertexCount, baseVertex);
+    SkASSERT(!fHasIndexBuffer);
+    SkASSERT(DynamicStateStatus::kConfigured != fInstanceBufferStatus);
+    SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus);
+    this->onDraw(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) {
+void GrOpsRenderPass::drawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                                  uint16_t maxIndexValue, 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);
+    SkASSERT(fHasIndexBuffer);
+    SkASSERT(DynamicStateStatus::kConfigured != fInstanceBufferStatus);
+    SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus);
+    this->onDrawIndexed(indexCount, baseIndex, minIndexValue, maxIndexValue, baseVertex);
 }
 
-void GrOpsRenderPass::drawInstanced(const GrBuffer* instanceBuffer, int instanceCount, int
-                                    baseInstance, const GrBuffer* vertexBuffer, int vertexCount,
+void GrOpsRenderPass::drawInstanced(int instanceCount, int baseInstance, int vertexCount,
                                     int baseVertex) {
     if (!this->prepareToDraw()) {
         return;
     }
-    SkASSERT(SkToBool(vertexBuffer) == fHasVertexAttributes);
-    SkASSERT(SkToBool(instanceBuffer) == fHasInstanceAttributes);
-    this->onDrawInstanced(instanceBuffer, instanceCount, baseInstance, vertexBuffer, vertexCount,
-                          baseVertex);
+    SkASSERT(!fHasIndexBuffer);
+    SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus);
+    SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus);
+    this->onDrawInstanced(instanceCount, baseInstance, 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) {
+void GrOpsRenderPass::drawIndexedInstanced(int indexCount, int baseIndex, int instanceCount,
+                                           int baseInstance, 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);
+    SkASSERT(fHasIndexBuffer);
+    SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus);
+    SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus);
+    this->onDrawIndexedInstanced(indexCount, baseIndex, instanceCount, baseInstance, baseVertex);
 }
diff --git a/src/gpu/GrOpsRenderPass.h b/src/gpu/GrOpsRenderPass.h
index a96c6d5..3804a8a 100644
--- a/src/gpu/GrOpsRenderPass.h
+++ b/src/gpu/GrOpsRenderPass.h
@@ -66,9 +66,14 @@
     // again with a different array for primProcTextures. (On subsequent calls, if the backend is
     // capable of updating the primitive processor textures independently, then it will
     // automatically skip binding textures from GrPipeline.)
+    //
+    // If the current program does not use textures, this is a no-op.
     void bindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
                       const GrSurfaceProxy* const primProcTextures[]);
 
+    void bindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                     const GrBuffer* vertexBuffer, GrPrimitiveRestart = GrPrimitiveRestart::kNo);
+
     // Draws the given array of meshes using the current pipeline state. The client must call
     // bindPipeline() before using this method.
     //
@@ -76,18 +81,19 @@
     // setScissor() and bindTextures() on the client's behalf.
     void drawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount);
 
-    // 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,
+    // These methods issue draws using the current pipeline state. Before drawing, the caller must
+    // configure the pipeline and dynamic state:
+    //
+    //   - Call bindPipeline()
+    //   - If the scissor test is enabled, call setScissorRect()
+    //   - If the current program uses textures, call bindTextures()
+    //   - Call bindBuffers() (even if all buffers are null)
+    void draw(int vertexCount, int baseVertex);
+    void drawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue, uint16_t maxIndexValue,
                      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);
+    void drawInstanced(int instanceCount, int baseInstance, int vertexCount, int baseVertex);
+    void drawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance,
+                              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;
@@ -132,17 +138,15 @@
     virtual void onSetScissorRect(const SkIRect&) = 0;
     virtual bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
                                 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,
+    virtual void onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                               const GrBuffer* vertexBuffer, GrPrimitiveRestart) = 0;
+    virtual void onDraw(int vertexCount, int baseVertex) = 0;
+    virtual void onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                               uint16_t maxIndexValue, int baseVertex) = 0;
+    virtual void onDrawInstanced(int instanceCount, int baseInstance, 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 onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount,
+                                        int baseInstance, 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>) {}
@@ -165,8 +169,9 @@
 
     DynamicStateStatus fScissorStatus = DynamicStateStatus::kDisabled;
     DynamicStateStatus fTextureBindingStatus = DynamicStateStatus::kDisabled;
-    bool fHasVertexAttributes = false;
-    bool fHasInstanceAttributes = false;
+    bool fHasIndexBuffer = false;
+    DynamicStateStatus fInstanceBufferStatus = DynamicStateStatus::kDisabled;
+    DynamicStateStatus fVertexBufferStatus = DynamicStateStatus::kDisabled;
 #endif
 
     typedef GrOpsRenderPass INHERITED;
diff --git a/src/gpu/gl/GrGLOpsRenderPass.cpp b/src/gpu/gl/GrGLOpsRenderPass.cpp
index 27c8a11..23f45a1 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.cpp
+++ b/src/gpu/gl/GrGLOpsRenderPass.cpp
@@ -46,9 +46,9 @@
     return true;
 }
 
-void GrGLOpsRenderPass::setupGeometry(const GrBuffer* indexBuffer, const GrBuffer* vertexBuffer,
-                                      int baseVertex, const GrBuffer* instanceBuffer,
-                                      int baseInstance, GrPrimitiveRestart primitiveRestart) {
+void GrGLOpsRenderPass::onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                                      const GrBuffer* vertexBuffer,
+                                      GrPrimitiveRestart primitiveRestart) {
     SkASSERT((primitiveRestart == GrPrimitiveRestart::kNo) || indexBuffer);
     GrGLProgram* program = fGpu->currentProgram();
     if (!program) {
@@ -56,7 +56,29 @@
     }
 
     int numAttribs = program->numVertexAttributes() + program->numInstanceAttributes();
-    auto* attribState = fGpu->bindInternalVertexArray(indexBuffer, numAttribs, primitiveRestart);
+    fAttribArrayState = fGpu->bindInternalVertexArray(indexBuffer, numAttribs, primitiveRestart);
+
+    if (indexBuffer) {
+        if (indexBuffer->isCpuBuffer()) {
+            auto* cpuIndexBuffer = static_cast<const GrCpuBuffer*>(indexBuffer);
+            fIndexPointer = reinterpret_cast<const uint16_t*>(cpuIndexBuffer->data());
+        } else {
+            fIndexPointer = nullptr;
+        }
+    }
+
+    // We defer binding of instance and vertex buffers because GL does not (always) support base
+    // instance and/or base vertex.
+    fDeferredInstanceBuffer = sk_ref_sp(instanceBuffer);
+    fDeferredVertexBuffer = sk_ref_sp(vertexBuffer);
+}
+
+void GrGLOpsRenderPass::setupGeometry(const GrBuffer* vertexBuffer, int baseVertex,
+                                      const GrBuffer* instanceBuffer, int baseInstance) {
+    GrGLProgram* program = fGpu->currentProgram();
+    if (!program) {
+        return;
+    }
 
     if (int vertexStride = program->vertexStride()) {
         SkASSERT(vertexBuffer);
@@ -66,8 +88,9 @@
         for (int i = 0; i < program->numVertexAttributes(); ++i) {
             const auto& attrib = program->vertexAttribute(i);
             static constexpr int kDivisor = 0;
-            attribState->set(fGpu, attrib.fLocation, vertexBuffer, attrib.fCPUType, attrib.fGPUType,
-                             vertexStride, bufferOffset + attrib.fOffset, kDivisor);
+            fAttribArrayState->set(fGpu, attrib.fLocation, vertexBuffer, attrib.fCPUType,
+                                   attrib.fGPUType, vertexStride, bufferOffset + attrib.fOffset,
+                                   kDivisor);
         }
     }
     if (int instanceStride = program->instanceStride()) {
@@ -79,69 +102,55 @@
         for (int i = 0; i < program->numInstanceAttributes(); ++i, ++attribIdx) {
             const auto& attrib = program->instanceAttribute(i);
             static constexpr int kDivisor = 1;
-            attribState->set(fGpu, attrib.fLocation, instanceBuffer, attrib.fCPUType,
-                             attrib.fGPUType, instanceStride, bufferOffset + attrib.fOffset,
-                             kDivisor);
+            fAttribArrayState->set(fGpu, attrib.fLocation, instanceBuffer, attrib.fCPUType,
+                                   attrib.fGPUType, instanceStride, bufferOffset + attrib.fOffset,
+                                   kDivisor);
         }
     }
 }
 
-static const GrGLvoid* get_gl_index_ptr(const GrBuffer* indexBuffer, int baseIndex) {
-    size_t baseOffset = baseIndex * sizeof(uint16_t);
-    if (indexBuffer->isCpuBuffer()) {
-        return static_cast<const GrCpuBuffer*>(indexBuffer)->data() + baseOffset;
-    } else {
-        return reinterpret_cast<const GrGLvoid*>(baseOffset);
-    }
-}
-
-void GrGLOpsRenderPass::onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+void GrGLOpsRenderPass::onDraw(int vertexCount, int baseVertex) {
     if (fGpu->glCaps().drawArraysBaseVertexIsBroken()) {
-        this->setupGeometry(nullptr, vertexBuffer, baseVertex, nullptr, 0, GrPrimitiveRestart::kNo);
+        this->setupGeometry(fDeferredVertexBuffer.get(), baseVertex, nullptr, 0);
         fGpu->drawArrays(fPrimitiveType, 0, vertexCount);
         return;
     }
 
-    this->setupGeometry(nullptr, vertexBuffer, 0, nullptr, 0, GrPrimitiveRestart::kNo);
+    this->setupGeometry(fDeferredVertexBuffer.get(), 0, nullptr, 0);
     fGpu->drawArrays(fPrimitiveType, baseVertex, vertexCount);
 }
 
-void GrGLOpsRenderPass::onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-                                      GrPrimitiveRestart primitiveRestart, uint16_t minIndexValue,
-                                      uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
-                                      int baseVertex) {
-    const GrGLvoid* indexPtr = get_gl_index_ptr(indexBuffer, baseIndex);
-    this->setupGeometry(indexBuffer, vertexBuffer, baseVertex, nullptr, 0, primitiveRestart);
+void GrGLOpsRenderPass::onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                                      uint16_t maxIndexValue, int baseVertex) {
+    this->setupGeometry(fDeferredVertexBuffer.get(), baseVertex, nullptr, 0);
     if (fGpu->glCaps().drawRangeElementsSupport()) {
         fGpu->drawRangeElements(fPrimitiveType, minIndexValue, maxIndexValue, indexCount,
-                                GR_GL_UNSIGNED_SHORT, indexPtr);
+                                GR_GL_UNSIGNED_SHORT, fIndexPointer + baseIndex);
     } else {
-        fGpu->drawElements(fPrimitiveType, indexCount, GR_GL_UNSIGNED_SHORT, indexPtr);
+        fGpu->drawElements(fPrimitiveType, indexCount, GR_GL_UNSIGNED_SHORT,
+                           fIndexPointer + baseIndex);
     }
 }
 
-void GrGLOpsRenderPass::onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
-                                        int baseInstance, const GrBuffer* vertexBuffer,
-                                        int vertexCount, int baseVertex) {
+void GrGLOpsRenderPass::onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
+                                        int baseVertex) {
     int maxInstances = fGpu->glCaps().maxInstancesPerDrawWithoutCrashing(instanceCount);
     for (int i = 0; i < instanceCount; i += maxInstances) {
-        this->setupGeometry(nullptr, vertexBuffer, 0, instanceBuffer, baseInstance + i,
-                            GrPrimitiveRestart::kNo);
+        this->setupGeometry(fDeferredVertexBuffer.get(), 0, fDeferredInstanceBuffer.get(),
+                            baseInstance + i);
         fGpu->drawArraysInstanced(fPrimitiveType, baseVertex, vertexCount,
                                   std::min(instanceCount - i, maxInstances));
     }
 }
 
-void GrGLOpsRenderPass::onDrawIndexedInstanced(
-        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
-        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
-    const GrGLvoid* indexPtr = get_gl_index_ptr(indexBuffer, baseIndex);
+void GrGLOpsRenderPass::onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount,
+                                               int baseInstance, int baseVertex) {
     int maxInstances = fGpu->glCaps().maxInstancesPerDrawWithoutCrashing(instanceCount);
     for (int i = 0; i < instanceCount; i += maxInstances) {
-        this->setupGeometry(indexBuffer, vertexBuffer, baseVertex,
-                            instanceBuffer, baseInstance + i, primitiveRestart);
-        fGpu->drawElementsInstanced(fPrimitiveType, indexCount, GR_GL_UNSIGNED_SHORT, indexPtr,
+        this->setupGeometry(fDeferredVertexBuffer.get(), baseVertex, fDeferredInstanceBuffer.get(),
+                            baseInstance + i);
+        fGpu->drawElementsInstanced(fPrimitiveType, indexCount, GR_GL_UNSIGNED_SHORT,
+                                    fIndexPointer + baseIndex,
                                     std::min(instanceCount - i, maxInstances));
     }
 }
diff --git a/src/gpu/gl/GrGLOpsRenderPass.h b/src/gpu/gl/GrGLOpsRenderPass.h
index 5e4faef..a7eb31c 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.h
+++ b/src/gpu/gl/GrGLOpsRenderPass.h
@@ -49,23 +49,21 @@
 private:
     GrGpu* gpu() override { return fGpu; }
 
-    void setupGeometry(const GrBuffer* indexBuffer, const GrBuffer* vertexBuffer, int baseVertex,
-                       const GrBuffer* instanceBuffer, int baseInstance,
-                       GrPrimitiveRestart enablePrimitiveRestart);
+    void setupGeometry(const GrBuffer* vertexBuffer, int baseVertex, const GrBuffer* instanceBuffer,
+                       int baseInstance);
 
     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;
-    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,
+    void onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                       const GrBuffer* vertexBuffer, GrPrimitiveRestart) override;
+    void onDraw(int vertexCount, int baseVertex) override;
+    void onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                       uint16_t maxIndexValue, int baseVertex) override;
+    void onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
+                         int baseVertex) override;
+    void onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance,
                                 int baseVertex) override;
     void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
     void onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) override;
@@ -77,6 +75,16 @@
 
     // Per-pipeline state.
     GrPrimitiveType fPrimitiveType;
+    GrGLAttribArrayState* fAttribArrayState = nullptr;
+
+    // If using an index buffer, this gets set during onBindBuffers. It is either the CPU address of
+    // the indices, or nullptr if they reside physically in GPU memory.
+    const uint16_t* fIndexPointer;
+
+    // We may defer binding of instance and vertex buffers because GL does not always support a base
+    // instance and/or vertex.
+    sk_sp<const GrBuffer> fDeferredInstanceBuffer;
+    sk_sp<const GrBuffer> fDeferredVertexBuffer;
 
     typedef GrOpsRenderPass INHERITED;
 };
diff --git a/src/gpu/mock/GrMockOpsRenderPass.h b/src/gpu/mock/GrMockOpsRenderPass.h
index 756a14c..f5984fa 100644
--- a/src/gpu/mock/GrMockOpsRenderPass.h
+++ b/src/gpu/mock/GrMockOpsRenderPass.h
@@ -34,35 +34,17 @@
     int numDraws() const { return fNumDraws; }
 
 private:
-    bool onBindPipeline(const GrProgramInfo&, const SkRect&) override {
-        return true;
-    }
+    bool onBindPipeline(const GrProgramInfo&, const SkRect&) override { return true; }
     void onSetScissorRect(const SkIRect&) override {}
     bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
-                        const GrSurfaceProxy* const primProcTextures[]) override {
-        return true;
-    }
-    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();
-    }
+                        const GrSurfaceProxy* const primProcTextures[]) override { return true; }
+    void onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                       const GrBuffer* vertexBuffer, GrPrimitiveRestart) override {}
+    void onDraw(int, int) override { this->dummyDraw(); }
+    void onDrawIndexed(int, int, uint16_t, uint16_t, int) override { this->dummyDraw(); }
+    void onDrawInstanced(int, int, int, int) override { this->dummyDraw(); }
+    void onDrawIndexedInstanced(int, int, int, int, int) override { this->dummyDraw(); }
+    void onClear(const GrFixedClip&, const SkPMColor4f&) override { this->markRenderTargetDirty(); }
     void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) override {}
     void dummyDraw() {
         this->markRenderTargetDirty();
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.h b/src/gpu/mtl/GrMtlOpsRenderPass.h
index 90136fe..8724108 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.h
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.h
@@ -43,15 +43,14 @@
     void onSetScissorRect(const SkIRect&) override;
     bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
                         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,
+    void onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                       const GrBuffer* vertexBuffer, GrPrimitiveRestart) override;
+    void onDraw(int vertexCount, int baseVertex) override;
+    void onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                       uint16_t maxIndexValue, int baseVertex) override;
+    void onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
+                         int baseVertex) override;
+    void onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance,
                                 int baseVertex) override;
 
     void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
@@ -61,11 +60,8 @@
     void setupRenderPass(const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
                          const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo);
 
-    void bindGeometry(const GrBuffer* vertexBuffer, size_t vertexOffset,
-                      const GrBuffer* instanceBuffer);
-
     void setVertexBuffer(id<MTLRenderCommandEncoder>, const GrMtlBuffer*, size_t offset,
-                         size_t index);
+                         size_t inputBufferIndex);
     void resetBufferBindings();
     void precreateCmdEncoder();
 
@@ -76,6 +72,12 @@
     MTLPrimitiveType            fActivePrimitiveType;
     MTLRenderPassDescriptor*    fRenderPassDesc;
     SkRect                      fBounds;
+
+    // The index buffer in metal is an argument to the draw call, rather than a stateful binding.
+    sk_sp<const GrMtlBuffer>    fIndexBuffer;
+
+    // We defer binding of the vertex buffer because Metal doesn't have baseVertex for drawIndexed.
+    sk_sp<const GrMtlBuffer>    fDeferredVertexBuffer;
     size_t                      fCurrentVertexStride;
 
     static constexpr size_t kNumBindings = GrMtlUniformHandler::kLastUniformBinding + 3;
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm
index 7729c16..be0d3b3 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.mm
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm
@@ -246,70 +246,63 @@
     fActiveRenderCmdEncoder = nil;
 }
 
-void GrMtlOpsRenderPass::bindGeometry(const GrBuffer* vertexBuffer,
-                                      size_t vertexOffset,
-                                      const GrBuffer* instanceBuffer) {
-    size_t bufferIndex = GrMtlUniformHandler::kLastUniformBinding + 1;
+void GrMtlOpsRenderPass::onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                                       const GrBuffer* vertexBuffer,
+                                       GrPrimitiveRestart primRestart) {
+    SkASSERT(GrPrimitiveRestart::kNo == primRestart);
+    int inputBufferIndex = 0;
     if (vertexBuffer) {
         SkASSERT(!vertexBuffer->isCpuBuffer());
         SkASSERT(!static_cast<const GrGpuBuffer*>(vertexBuffer)->isMapped());
-
-        const GrMtlBuffer* grMtlBuffer = static_cast<const GrMtlBuffer*>(vertexBuffer);
-        this->setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer, vertexOffset, bufferIndex++);
+        fDeferredVertexBuffer = sk_ref_sp(static_cast<const GrMtlBuffer*>(vertexBuffer));
+        ++inputBufferIndex;
     }
     if (instanceBuffer) {
         SkASSERT(!instanceBuffer->isCpuBuffer());
         SkASSERT(!static_cast<const GrGpuBuffer*>(instanceBuffer)->isMapped());
 
         const GrMtlBuffer* grMtlBuffer = static_cast<const GrMtlBuffer*>(instanceBuffer);
-        this->setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer, 0, bufferIndex++);
+        this->setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer, 0, inputBufferIndex++);
+    }
+    if (indexBuffer) {
+        SkASSERT(!indexBuffer->isCpuBuffer());
+        SkASSERT(!static_cast<const GrGpuBuffer*>(indexBuffer)->isMapped());
+        fIndexBuffer = sk_ref_sp(static_cast<const GrMtlBuffer*>(indexBuffer));
     }
 }
 
-void GrMtlOpsRenderPass::onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+void GrMtlOpsRenderPass::onDraw(int vertexCount, int baseVertex) {
     SkASSERT(fActivePipelineState);
     SkASSERT(nil != fActiveRenderCmdEncoder);
-    this->bindGeometry(vertexBuffer, 0, nullptr);
+    this->setVertexBuffer(fActiveRenderCmdEncoder, fDeferredVertexBuffer.get(), 0, 0);
 
     [fActiveRenderCmdEncoder drawPrimitives:fActivePrimitiveType
                                 vertexStart:baseVertex
                                 vertexCount:vertexCount];
 }
 
-void GrMtlOpsRenderPass::onDrawIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-                                       GrPrimitiveRestart primitiveRestart, uint16_t minIndexValue,
-                                       uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
-                                       int baseVertex) {
+void GrMtlOpsRenderPass::onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                                       uint16_t maxIndexValue, int baseVertex) {
     SkASSERT(fActivePipelineState);
     SkASSERT(nil != fActiveRenderCmdEncoder);
-    this->bindGeometry(vertexBuffer, fCurrentVertexStride*baseVertex, nullptr);
+    SkASSERT(fIndexBuffer);
+    this->setVertexBuffer(fActiveRenderCmdEncoder, fDeferredVertexBuffer.get(),
+                          fCurrentVertexStride * baseVertex, 0);
 
-    id<MTLBuffer> mtlIndexBuffer = nil;
-    if (indexBuffer) {
-        SkASSERT(!indexBuffer->isCpuBuffer());
-        SkASSERT(!static_cast<const GrGpuBuffer*>(indexBuffer)->isMapped());
-
-        mtlIndexBuffer = static_cast<const GrMtlBuffer*>(indexBuffer)->mtlBuffer();
-        SkASSERT(mtlIndexBuffer);
-    }
-
-    SkASSERT(primitiveRestart == GrPrimitiveRestart::kNo);
-    size_t indexOffset = static_cast<const GrMtlBuffer*>(indexBuffer)->offset() +
-                         sizeof(uint16_t) * baseIndex;
+    size_t indexOffset = fIndexBuffer->offset() + sizeof(uint16_t) * baseIndex;
     [fActiveRenderCmdEncoder drawIndexedPrimitives:fActivePrimitiveType
                                         indexCount:indexCount
                                          indexType:MTLIndexTypeUInt16
-                                       indexBuffer:mtlIndexBuffer
+                                       indexBuffer:fIndexBuffer->mtlBuffer()
                                  indexBufferOffset:indexOffset];
     fGpu->stats()->incNumDraws();
 }
 
-void GrMtlOpsRenderPass::onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
-                                         int baseInstance, const GrBuffer* vertexBuffer,
-                                         int vertexCount, int baseVertex) {
+void GrMtlOpsRenderPass::onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
+                                         int baseVertex) {
     SkASSERT(fActivePipelineState);
     SkASSERT(nil != fActiveRenderCmdEncoder);
-    this->bindGeometry(vertexBuffer, 0, instanceBuffer);
+    this->setVertexBuffer(fActiveRenderCmdEncoder, fDeferredVertexBuffer.get(), 0, 0);
 
     if (@available(macOS 10.11, iOS 9.0, *)) {
         [fActiveRenderCmdEncoder drawPrimitives:fActivePrimitiveType
@@ -323,31 +316,18 @@
 }
 
 void GrMtlOpsRenderPass::onDrawIndexedInstanced(
-        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
-        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+        int indexCount, int baseIndex, int instanceCount, int baseInstance, int baseVertex) {
     SkASSERT(fActivePipelineState);
     SkASSERT(nil != fActiveRenderCmdEncoder);
-    this->bindGeometry(vertexBuffer, 0, instanceBuffer);
+    SkASSERT(fIndexBuffer);
+    this->setVertexBuffer(fActiveRenderCmdEncoder, fDeferredVertexBuffer.get(), 0, 0);
 
-    id<MTLBuffer> mtlIndexBuffer = nil;
-    if (indexBuffer) {
-        SkASSERT(!indexBuffer->isCpuBuffer());
-        SkASSERT(!static_cast<const GrGpuBuffer*>(indexBuffer)->isMapped());
-
-        mtlIndexBuffer = static_cast<const GrMtlBuffer*>(indexBuffer)->mtlBuffer();
-        SkASSERT(mtlIndexBuffer);
-    }
-
-    SkASSERT(primitiveRestart == GrPrimitiveRestart::kNo);
-    size_t indexOffset = static_cast<const GrMtlBuffer*>(indexBuffer)->offset() +
-                         sizeof(uint16_t) * baseIndex;
-
+    size_t indexOffset = fIndexBuffer->offset() + sizeof(uint16_t) * baseIndex;
     if (@available(macOS 10.11, iOS 9.0, *)) {
         [fActiveRenderCmdEncoder drawIndexedPrimitives:fActivePrimitiveType
                                             indexCount:indexCount
                                              indexType:MTLIndexTypeUInt16
-                                           indexBuffer:mtlIndexBuffer
+                                           indexBuffer:fIndexBuffer->mtlBuffer()
                                      indexBufferOffset:indexOffset
                                          instanceCount:instanceCount
                                             baseVertex:baseVertex
@@ -361,7 +341,9 @@
 void GrMtlOpsRenderPass::setVertexBuffer(id<MTLRenderCommandEncoder> encoder,
                                          const GrMtlBuffer* buffer,
                                          size_t vertexOffset,
-                                         size_t index) {
+                                         size_t inputBufferIndex) {
+    constexpr static int kFirstBufferBindingIdx = GrMtlUniformHandler::kLastUniformBinding + 1;
+    int index = inputBufferIndex + kFirstBufferBindingIdx;
     SkASSERT(index < 4);
     id<MTLBuffer> mtlVertexBuffer = buffer->mtlBuffer();
     SkASSERT(mtlVertexBuffer);
diff --git a/src/gpu/vk/GrVkOpsRenderPass.cpp b/src/gpu/vk/GrVkOpsRenderPass.cpp
index 695e2c2..920da28 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.cpp
+++ b/src/gpu/vk/GrVkOpsRenderPass.cpp
@@ -443,42 +443,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrVkOpsRenderPass::bindGeometry(const GrGpuBuffer* indexBuffer,
-                                          const GrGpuBuffer* vertexBuffer,
-                                          const GrGpuBuffer* instanceBuffer) {
-    GrVkCommandBuffer* currCmdBuf = this->currentCommandBuffer();
-    // There is no need to put any memory barriers to make sure host writes have finished here.
-    // When a command buffer is submitted to a queue, there is an implicit memory barrier that
-    // occurs for all host writes. Additionally, BufferMemoryBarriers are not allowed inside of
-    // an active RenderPass.
-
-    // Here our vertex and instance inputs need to match the same 0-based bindings they were
-    // assigned in GrVkPipeline. That is, vertex first (if any) followed by instance.
-    uint32_t binding = 0;
-
-    if (vertexBuffer) {
-        SkASSERT(vertexBuffer);
-        SkASSERT(!vertexBuffer->isMapped());
-
-        currCmdBuf->bindInputBuffer(fGpu, binding++,
-                                    static_cast<const GrVkVertexBuffer*>(vertexBuffer));
-    }
-
-    if (instanceBuffer) {
-        SkASSERT(instanceBuffer);
-        SkASSERT(!instanceBuffer->isMapped());
-
-        currCmdBuf->bindInputBuffer(fGpu, binding++,
-                                    static_cast<const GrVkVertexBuffer*>(instanceBuffer));
-    }
-    if (indexBuffer) {
-        SkASSERT(indexBuffer);
-        SkASSERT(!indexBuffer->isMapped());
-
-        currCmdBuf->bindIndexBuffer(fGpu, static_cast<const GrVkIndexBuffer*>(indexBuffer));
-    }
-}
-
 #ifdef SK_DEBUG
 void check_sampled_texture(GrTexture* tex, GrRenderTarget* rt, GrVkGpu* gpu) {
     SkASSERT(!tex->isProtected() || (rt->isProtected() && gpu->protectedContext()));
@@ -582,45 +546,70 @@
 bool GrVkOpsRenderPass::onBindTextures(const GrPrimitiveProcessor& primProc,
                                        const GrPipeline& pipeline,
                                        const GrSurfaceProxy* const primProcTextures[]) {
+    SkASSERT(fCurrentPipelineState);
     return fCurrentPipelineState->setAndBindTextures(fGpu, primProc, pipeline, primProcTextures,
                                                      this->currentCommandBuffer());
 }
 
-void GrVkOpsRenderPass::onDrawInstanced(const GrBuffer* instanceBuffer, int instanceCount,
-                                        int baseInstance, const GrBuffer* vertexBuffer,
+void GrVkOpsRenderPass::onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                                      const GrBuffer* vertexBuffer,
+                                      GrPrimitiveRestart primRestart) {
+    SkASSERT(GrPrimitiveRestart::kNo == primRestart);
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
+    SkASSERT(fCurrentPipelineState);
+    SkASSERT(!fGpu->caps()->usePrimitiveRestart());  // Ignore primitiveRestart parameter.
+
+    GrVkCommandBuffer* currCmdBuf = this->currentCommandBuffer();
+    SkASSERT(currCmdBuf);
+
+    // There is no need to put any memory barriers to make sure host writes have finished here.
+    // When a command buffer is submitted to a queue, there is an implicit memory barrier that
+    // occurs for all host writes. Additionally, BufferMemoryBarriers are not allowed inside of
+    // an active RenderPass.
+
+    // Here our vertex and instance inputs need to match the same 0-based bindings they were
+    // assigned in GrVkPipeline. That is, vertex first (if any) followed by instance.
+    uint32_t binding = 0;
+    if (auto* vkVertexBuffer = static_cast<const GrVkVertexBuffer*>(vertexBuffer)) {
+        SkASSERT(!vkVertexBuffer->isCpuBuffer());
+        SkASSERT(!vkVertexBuffer->isMapped());
+        currCmdBuf->bindInputBuffer(fGpu, binding++, vkVertexBuffer);
+    }
+    if (auto* vkInstanceBuffer = static_cast<const GrVkVertexBuffer*>(instanceBuffer)) {
+        SkASSERT(!vkInstanceBuffer->isCpuBuffer());
+        SkASSERT(!vkInstanceBuffer->isMapped());
+        currCmdBuf->bindInputBuffer(fGpu, binding++, vkInstanceBuffer);
+    }
+    if (auto* vkIndexBuffer = static_cast<const GrVkIndexBuffer*>(indexBuffer)) {
+        SkASSERT(!vkIndexBuffer->isCpuBuffer());
+        SkASSERT(!vkIndexBuffer->isMapped());
+        currCmdBuf->bindIndexBuffer(fGpu, vkIndexBuffer);
+    }
+}
+
+void GrVkOpsRenderPass::onDrawInstanced(int instanceCount,
+                                        int baseInstance,
                                         int vertexCount, int baseVertex) {
     if (!fCurrentRenderPass) {
         SkASSERT(fGpu->isDeviceLost());
         return;
     }
     SkASSERT(fCurrentPipelineState);
-    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::onDrawIndexedInstanced(
-        const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-        GrPrimitiveRestart primitiveRestart, const GrBuffer* instanceBuffer, int instanceCount,
-        int baseInstance, const GrBuffer* vertexBuffer, int baseVertex) {
+void GrVkOpsRenderPass::onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount,
+                                               int baseInstance, 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();
diff --git a/src/gpu/vk/GrVkOpsRenderPass.h b/src/gpu/vk/GrVkOpsRenderPass.h
index 7ecdbe8..652e9f1 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.h
+++ b/src/gpu/vk/GrVkOpsRenderPass.h
@@ -63,30 +63,22 @@
 
     GrVkCommandBuffer* currentCommandBuffer();
 
-    // Bind vertex and index buffers
-    void bindGeometry(const GrGpuBuffer* indexBuffer,
-                      const GrGpuBuffer* vertexBuffer,
-                      const GrGpuBuffer* instanceBuffer);
-
     bool onBindPipeline(const GrProgramInfo&, const SkRect& drawBounds) override;
     void onSetScissorRect(const SkIRect&) override;
     bool onBindTextures(const GrPrimitiveProcessor&, const GrPipeline&,
                         const GrSurfaceProxy* const primProcTextures[]) override;
-    void onDraw(const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) override {
-        this->onDrawInstanced(nullptr, 1, 0, vertexBuffer, vertexCount, baseVertex);
+    void onBindBuffers(const GrBuffer* indexBuffer, const GrBuffer* instanceBuffer,
+                       const GrBuffer* vertexBuffer, GrPrimitiveRestart) override;
+    void onDraw(int vertexCount, int baseVertex) override {
+        this->onDrawInstanced(1, 0, vertexCount, baseVertex);
     }
-    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 onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
+                       uint16_t maxIndexValue, int baseVertex) override {
+        this->onDrawIndexedInstanced(indexCount, baseIndex, 1, 0, baseVertex);
     }
-    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,
+    void onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
+                         int baseVertex) override;
+    void onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance,
                                 int baseVertex) override;
 
     void onClear(const GrFixedClip&, const SkPMColor4f& color) override;