Stop passing GrPrimitiveProcessor to GrMesh::sendToGpu.

It is currently used in GrGLGpu::setupGeometry. Instead:

1) Make GrMesh track whether primitive restart should be enabled.

2) Make GrGLProgram track program attributes.

Change-Id: Ice411a495961fcbc3cedc81e8ae0583537f42153
Reviewed-on: https://skia-review.googlesource.com/132267
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 533c6b4..42a8992 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -251,6 +251,7 @@
     fMipmapProgramArrayBuffer.reset();
     fStencilClipClearArrayBuffer.reset();
 
+    fHWProgram.reset();
     if (fHWProgramID) {
         // detach the current program so there is no confusion on OpenGL's part
         // that we want it to be deleted
@@ -328,6 +329,7 @@
         }
     }
 
+    fHWProgram.reset();
     delete fProgramCache;
     fProgramCache = nullptr;
 
@@ -496,6 +498,7 @@
 
     if (resetBits & kProgram_GrGLBackendState) {
         fHWProgramID = 0;
+        fHWProgram.reset();
     }
 }
 
@@ -1695,11 +1698,7 @@
     this->flushColorWrite(blendInfo.fWriteColor);
     this->flushMinSampleShading(primProc.getSampleShading());
 
-    GrGLuint programID = program->programID();
-    if (fHWProgramID != programID) {
-        GL_CALL(UseProgram(programID));
-        fHWProgramID = programID;
-    }
+    this->flushProgram(std::move(program));
 
     if (blendInfo.fWriteColor) {
         // Swizzle the blend to match what the shader will output.
@@ -1708,7 +1707,7 @@
         this->flushBlend(blendInfo, swizzle);
     }
 
-    program->setData(primProc, pipeline);
+    fHWProgram->setData(primProc, pipeline);
 
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.renderTarget());
     GrStencilSettings stencil;
@@ -1730,13 +1729,41 @@
     return true;
 }
 
-void GrGLGpu::setupGeometry(const GrPrimitiveProcessor& primProc,
-                            const GrBuffer* indexBuffer,
+void GrGLGpu::flushProgram(sk_sp<GrGLProgram> program) {
+    if (!program) {
+        fHWProgram.reset();
+        fHWProgramID = 0;
+        return;
+    }
+    SkASSERT((program == fHWProgram) == (fHWProgramID == program->programID()));
+    if (program == fHWProgram) {
+        return;
+    }
+    auto id = program->programID();
+    SkASSERT(id);
+    GL_CALL(UseProgram(id));
+    fHWProgram = std::move(program);
+    fHWProgramID = id;
+}
+
+void GrGLGpu::flushProgram(GrGLuint id) {
+    SkASSERT(id);
+    if (fHWProgramID == id) {
+        SkASSERT(!fHWProgram);
+        return;
+    }
+    fHWProgram.reset();
+    GL_CALL(UseProgram(id));
+    fHWProgramID = id;
+}
+
+void GrGLGpu::setupGeometry(const GrBuffer* indexBuffer,
                             const GrBuffer* vertexBuffer,
                             int baseVertex,
                             const GrBuffer* instanceBuffer,
-                            int baseInstance) {
-    using EnablePrimitiveRestart = GrGLAttribArrayState::EnablePrimitiveRestart;
+                            int baseInstance,
+                            GrPrimitiveRestart enablePrimitiveRestart) {
+    SkASSERT((enablePrimitiveRestart == GrPrimitiveRestart::kNo) || indexBuffer);
 
     GrGLAttribArrayState* attribState;
     if (indexBuffer) {
@@ -1752,30 +1779,29 @@
         size_t            fBufferOffset;
     } bindings[2];
 
-    if (int vertexStride = primProc.getVertexStride()) {
+    if (int vertexStride = fHWProgram->vertexStride()) {
         SkASSERT(vertexBuffer && !vertexBuffer->isMapped());
         bindings[0].fBuffer = vertexBuffer;
         bindings[0].fStride = vertexStride;
         bindings[0].fBufferOffset = vertexBuffer->baseOffset() + baseVertex * vertexStride;
     }
-    if (int instanceStride = primProc.getInstanceStride()) {
+    if (int instanceStride = fHWProgram->instanceStride()) {
         SkASSERT(instanceBuffer && !instanceBuffer->isMapped());
         bindings[1].fBuffer = instanceBuffer;
         bindings[1].fStride = instanceStride;
         bindings[1].fBufferOffset = instanceBuffer->baseOffset() + baseInstance * instanceStride;
     }
 
-    int numAttribs = primProc.numAttribs();
-    auto enableRestart = EnablePrimitiveRestart(primProc.willUsePrimitiveRestart() && indexBuffer);
-    attribState->enableVertexArrays(this, numAttribs, enableRestart);
+    auto numAttributes = fHWProgram->numAttributes();
+    attribState->enableVertexArrays(this, numAttributes, enablePrimitiveRestart);
 
-    for (int i = 0; i < numAttribs; ++i) {
+    for (int i = 0; i < numAttributes; ++i) {
         using InputRate = GrPrimitiveProcessor::Attribute::InputRate;
-        const GrGeometryProcessor::Attribute& attrib = primProc.getAttrib(i);
-        const int divisor = InputRate::kPerInstance == attrib.inputRate() ? 1 : 0;
+        const GrGLProgram::Attribute& attribute = fHWProgram->attribute(i);
+        const int divisor = InputRate::kPerInstance == attribute.fInputRate ? 1 : 0;
         const auto& binding = bindings[divisor];
-        attribState->set(this, i, binding.fBuffer, attrib.type(), binding.fStride,
-                         binding.fBufferOffset + attrib.offsetInRecord(), divisor);
+        attribState->set(this, attribute.fLocation, binding.fBuffer, attribute.fType,
+                         binding.fStride, binding.fBufferOffset + attribute.fOffset, divisor);
     }
 }
 
@@ -2242,7 +2268,7 @@
             GL_CALL(Enable(GR_GL_CULL_FACE));
             GL_CALL(Disable(GR_GL_CULL_FACE));
         }
-        meshes[i].sendToGpu(primProc, this);
+        meshes[i].sendToGpu(this);
         fLastPrimitiveType = meshes[i].primitiveType();
     }
 
@@ -2279,14 +2305,14 @@
     return GR_GL_TRIANGLES;
 }
 
-void GrGLGpu::sendMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType primitiveType,
-                            const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+void GrGLGpu::sendMeshToGpu(GrPrimitiveType primitiveType, const GrBuffer* vertexBuffer,
+                            int vertexCount, int baseVertex) {
     const GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
     if (this->glCaps().drawArraysBaseVertexIsBroken()) {
-        this->setupGeometry(primProc, nullptr, vertexBuffer, baseVertex, nullptr, 0);
+        this->setupGeometry(nullptr, vertexBuffer, baseVertex, nullptr, 0, GrPrimitiveRestart::kNo);
         GL_CALL(DrawArrays(glPrimType, 0, vertexCount));
     } else {
-        this->setupGeometry(primProc, nullptr, vertexBuffer, 0, nullptr, 0);
+        this->setupGeometry(nullptr, vertexBuffer, 0, nullptr, 0, GrPrimitiveRestart::kNo);
         GL_CALL(DrawArrays(glPrimType, baseVertex, vertexCount));
     }
     if (this->glCaps().requiresFlushBetweenNonAndInstancedDraws()) {
@@ -2295,16 +2321,15 @@
     fStats.incNumDraws();
 }
 
-void GrGLGpu::sendIndexedMeshToGpu(const GrPrimitiveProcessor& primProc,
-                                   GrPrimitiveType primitiveType, const GrBuffer* indexBuffer,
+void GrGLGpu::sendIndexedMeshToGpu(GrPrimitiveType primitiveType, const GrBuffer* indexBuffer,
                                    int indexCount, int baseIndex, uint16_t minIndexValue,
                                    uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
-                                   int baseVertex) {
+                                   int baseVertex, GrPrimitiveRestart enablePrimitiveRestart) {
     const GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
     GrGLvoid* const indices = reinterpret_cast<void*>(indexBuffer->baseOffset() +
                                                       sizeof(uint16_t) * baseIndex);
 
-    this->setupGeometry(primProc, indexBuffer, vertexBuffer, baseVertex, nullptr, 0);
+    this->setupGeometry(indexBuffer, vertexBuffer, baseVertex, nullptr, 0, enablePrimitiveRestart);
 
     if (this->glCaps().drawRangeElementsSupport()) {
         GL_CALL(DrawRangeElements(glPrimType, minIndexValue, maxIndexValue, indexCount,
@@ -2318,8 +2343,7 @@
     fStats.incNumDraws();
 }
 
-void GrGLGpu::sendInstancedMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType
-                                     primitiveType, const GrBuffer* vertexBuffer,
+void GrGLGpu::sendInstancedMeshToGpu(GrPrimitiveType primitiveType, const GrBuffer* vertexBuffer,
                                      int vertexCount, int baseVertex,
                                      const GrBuffer* instanceBuffer, int instanceCount,
                                      int baseInstance) {
@@ -2331,19 +2355,20 @@
     GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
     int maxInstances = this->glCaps().maxInstancesPerDrawArraysWithoutCrashing(instanceCount);
     for (int i = 0; i < instanceCount; i += maxInstances) {
-        this->setupGeometry(primProc, nullptr, vertexBuffer, 0, instanceBuffer, baseInstance + i);
+        this->setupGeometry(nullptr, vertexBuffer, 0, instanceBuffer, baseInstance + i,
+                            GrPrimitiveRestart::kNo);
         GL_CALL(DrawArraysInstanced(glPrimType, baseVertex, vertexCount,
                                     SkTMin(instanceCount - i, maxInstances)));
         fStats.incNumDraws();
     }
 }
 
-void GrGLGpu::sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor& primProc,
-                                            GrPrimitiveType primitiveType,
+void GrGLGpu::sendIndexedInstancedMeshToGpu(GrPrimitiveType primitiveType,
                                             const GrBuffer* indexBuffer, int indexCount,
                                             int baseIndex, const GrBuffer* vertexBuffer,
                                             int baseVertex, const GrBuffer* instanceBuffer,
-                                            int instanceCount, int baseInstance) {
+                                            int instanceCount, int baseInstance,
+                                            GrPrimitiveRestart enablePrimitiveRestart) {
     if (fRequiresFlushBeforeNextInstancedDraw) {
         SkASSERT(this->glCaps().requiresFlushBetweenNonAndInstancedDraws());
         GL_CALL(Flush());
@@ -2352,8 +2377,8 @@
     const GrGLenum glPrimType = gr_primitive_type_to_gl_mode(primitiveType);
     GrGLvoid* indices = reinterpret_cast<void*>(indexBuffer->baseOffset() +
                                                 sizeof(uint16_t) * baseIndex);
-    this->setupGeometry(primProc, indexBuffer, vertexBuffer, baseVertex,
-                        instanceBuffer, baseInstance);
+    this->setupGeometry(indexBuffer, vertexBuffer, baseVertex, instanceBuffer, baseInstance,
+                        enablePrimitiveRestart);
     GL_CALL(DrawElementsInstanced(glPrimType, indexCount, GR_GL_UNSIGNED_SHORT, indices,
                                   instanceCount));
     fStats.incNumDraws();
@@ -3458,8 +3483,7 @@
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(rt->asRenderTarget());
     this->flushRenderTarget(glRT);
 
-    GL_CALL(UseProgram(fStencilClipClearProgram));
-    fHWProgramID = fStencilClipClearProgram;
+    this->flushProgram(fStencilClipClearProgram);
 
     fHWVertexArrayState.setVertexArrayID(this, 0);
 
@@ -3571,8 +3595,7 @@
     this->flushViewport(dstVP);
     fHWBoundRenderTargetUniqueID.makeInvalid();
 
-    GL_CALL(UseProgram(fClearColorProgram.fProgram));
-    fHWProgramID = fClearColorProgram.fProgram;
+    this->flushProgram(fClearColorProgram.fProgram);
 
     fHWVertexArrayState.setVertexArrayID(this, 0);
 
@@ -3632,8 +3655,7 @@
 
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, w, h);
 
-    GL_CALL(UseProgram(fCopyPrograms[progIdx].fProgram));
-    fHWProgramID = fCopyPrograms[progIdx].fProgram;
+    this->flushProgram(fCopyPrograms[progIdx].fProgram);
 
     fHWVertexArrayState.setVertexArrayID(this, 0);
 
@@ -3885,8 +3907,7 @@
                 return false;
             }
         }
-        GL_CALL(UseProgram(fMipmapPrograms[progIdx].fProgram));
-        fHWProgramID = fMipmapPrograms[progIdx].fProgram;
+        this->flushProgram(fMipmapPrograms[progIdx].fProgram);
 
         // Texcoord uniform is expected to contain (1/w, (w-1)/w, 1/h, (h-1)/h)
         const float invWidth = 1.0f / width;
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 7240f9d..48cd21b 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -95,24 +95,22 @@
 
     // GrMesh::SendToGpuImpl methods. These issue the actual GL draw calls.
     // Marked final as a hint to the compiler to not use virtual dispatch.
-    void sendMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
-                       const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) final;
+    void sendMeshToGpu(GrPrimitiveType, const GrBuffer* vertexBuffer, int vertexCount,
+                       int baseVertex) final;
 
-    void sendIndexedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
-                              const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-                              uint16_t minIndexValue, uint16_t maxIndexValue,
-                              const GrBuffer* vertexBuffer, int baseVertex) final;
+    void sendIndexedMeshToGpu(GrPrimitiveType, const GrBuffer* indexBuffer, int indexCount,
+                              int baseIndex, uint16_t minIndexValue, uint16_t maxIndexValue,
+                              const GrBuffer* vertexBuffer, int baseVertex,
+                              GrPrimitiveRestart) final;
 
-    void sendInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
-                                const GrBuffer* vertexBuffer, int vertexCount, int baseVertex,
-                                const GrBuffer* instanceBuffer, int instanceCount,
+    void sendInstancedMeshToGpu(GrPrimitiveType, const GrBuffer* vertexBuffer, int vertexCount,
+                                int baseVertex, const GrBuffer* instanceBuffer, int instanceCount,
                                 int baseInstance) final;
 
-    void sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
-                                       const GrBuffer* indexBuffer, int indexCount, int baseIndex,
-                                       const GrBuffer* vertexBuffer, int baseVertex,
+    void sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrBuffer* indexBuffer, int indexCount,
+                                       int baseIndex, const GrBuffer* vertexBuffer, int baseVertex,
                                        const GrBuffer* instanceBuffer, int instanceCount,
-                                       int baseInstance) final;
+                                       int baseInstance, GrPrimitiveRestart) final;
 
     // The GrGLGpuRTCommandBuffer 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
@@ -260,13 +258,18 @@
     // willDrawPoints must be true if point primitives will be rendered after setting the GL state.
     bool flushGLState(const GrPipeline&, const GrPrimitiveProcessor&, bool willDrawPoints);
 
+    void flushProgram(sk_sp<GrGLProgram>);
+
+    // Version for programs that aren't GrGLProgram.
+    void flushProgram(GrGLuint);
+
     // Sets up vertex/instance attribute pointers and strides.
-    void setupGeometry(const GrPrimitiveProcessor&,
-                       const GrBuffer* indexBuffer,
+    void setupGeometry(const GrBuffer* indexBuffer,
                        const GrBuffer* vertexBuffer,
                        int baseVertex,
                        const GrBuffer* instanceBuffer,
-                       int baseInstance);
+                       int baseInstance,
+                       GrPrimitiveRestart);
 
     void flushBlend(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle&);
 
@@ -408,7 +411,9 @@
     ///@name Caching of GL State
     ///@{
     int                         fHWActiveTextureUnitIdx;
+
     GrGLuint                    fHWProgramID;
+    sk_sp<GrGLProgram>          fHWProgram;
 
     enum TriState {
         kNo_TriState,
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index ea5c18c..262f0b2 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -36,13 +36,21 @@
         std::unique_ptr<GrGLSLPrimitiveProcessor> geometryProcessor,
         std::unique_ptr<GrGLSLXferProcessor> xferProcessor,
         std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fragmentProcessors,
-        int fragmentProcessorCnt)
+        int fragmentProcessorCnt,
+        std::unique_ptr<Attribute[]> attributes,
+        int attributeCnt,
+        int vertexStride,
+        int instanceStride)
         : fBuiltinUniformHandles(builtinUniforms)
         , fProgramID(programID)
-        , fGeometryProcessor(std::move(geometryProcessor))
+        , fPrimitiveProcessor(std::move(geometryProcessor))
         , fXferProcessor(std::move(xferProcessor))
         , fFragmentProcessors(std::move(fragmentProcessors))
         , fFragmentProcessorCnt(fragmentProcessorCnt)
+        , fAttributes(std::move(attributes))
+        , fAttributeCnt(attributeCnt)
+        , fVertexStride(vertexStride)
+        , fInstanceStride(instanceStride)
         , fGpu(gpu)
         , fProgramDataManager(gpu, programID, uniforms, pathProcVaryings)
         , fNumTextureSamplers(textureSamplers.count())
@@ -76,8 +84,8 @@
     // Within each group we will bind them in primProc, fragProcs, XP order.
     int nextTexSamplerIdx = 0;
     int nextTexelBufferIdx = fNumTextureSamplers;
-    fGeometryProcessor->setData(fProgramDataManager, primProc,
-                                GrFragmentProcessor::CoordTransformIter(pipeline));
+    fPrimitiveProcessor->setData(fProgramDataManager, primProc,
+                                 GrFragmentProcessor::CoordTransformIter(pipeline));
     this->bindTextures(primProc, &nextTexSamplerIdx, &nextTexelBufferIdx);
 
     this->setFragmentData(primProc, pipeline, &nextTexSamplerIdx, &nextTexelBufferIdx);
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index a43e4f4..6ea1f2c 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -10,6 +10,7 @@
 #define GrGLProgram_DEFINED
 
 #include "GrGLProgramDataManager.h"
+#include "GrPrimitiveProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 
@@ -22,16 +23,23 @@
 class GrResourceIOProcessor;
 
 /**
- * This class manages a GPU program and records per-program information.
- * We can specify the attribute locations so that they are constant
- * across our shaders. But the driver determines the uniform locations
- * at link time. We don't need to remember the sampler uniform location
- * because we will bind a texture slot to it and never change it
- * Uniforms are program-local so we can't rely on fHWState to hold the
- * previous uniform state after a program change.
+ * This class manages a GPU program and records per-program information. It also records the vertex
+ * and instance attribute layouts that are to be used with the program.
  */
 class GrGLProgram : public SkRefCnt {
 public:
+    /**
+     * This class has its own Attribute representation as it does not need the name and we don't
+     * want to worry about copying the name string to memory with life time of GrGLProgram.
+     * Additionally, these store the attribute location.
+     */
+    struct Attribute {
+        GrVertexAttribType fType;
+        int fOffset;
+        GrGLint fLocation;
+        GrPrimitiveProcessor::Attribute::InputRate fInputRate;
+    };
+
     using UniformHandle = GrGLSLProgramDataManager::UniformHandle;
     using UniformInfoArray = GrGLProgramDataManager::UniformInfoArray;
     using VaryingInfoArray = GrGLProgramDataManager::VaryingInfoArray;
@@ -46,7 +54,11 @@
                 std::unique_ptr<GrGLSLPrimitiveProcessor> geometryProcessor,
                 std::unique_ptr<GrGLSLXferProcessor> xferProcessor,
                 std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fragmentProcessors,
-                int fragmentProcessorCnt);
+                int fragmentProcessorCnt,
+                std::unique_ptr<Attribute[]>,
+                int attributeCnt,
+                int vertexStride,
+                int instanceStride);
 
     ~GrGLProgram();
 
@@ -109,6 +121,11 @@
      * ensures that any textures requiring mipmaps have their mipmaps correctly built.
      */
     void generateMipmaps(const GrPrimitiveProcessor&, const GrPipeline&);
+    int vertexStride() const { return fVertexStride; }
+    int instanceStride() const { return fInstanceStride; }
+
+    int numAttributes() const { return fAttributeCnt; }
+    const Attribute& attribute(int i) const { return fAttributes[i]; }
 
 private:
     // A helper to loop over effects, set the transforms (via subclass) and bind textures
@@ -130,11 +147,16 @@
     GrGLuint fProgramID;
 
     // the installed effects
-    std::unique_ptr<GrGLSLPrimitiveProcessor> fGeometryProcessor;
+    std::unique_ptr<GrGLSLPrimitiveProcessor> fPrimitiveProcessor;
     std::unique_ptr<GrGLSLXferProcessor> fXferProcessor;
     std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fFragmentProcessors;
     int fFragmentProcessorCnt;
 
+    std::unique_ptr<Attribute[]> fAttributes;
+    int fAttributeCnt;
+    int fVertexStride;
+    int fInstanceStride;
+
     GrGLGpu* fGpu;
     GrGLProgramDataManager fProgramDataManager;
 
diff --git a/src/gpu/gl/GrGLVertexArray.cpp b/src/gpu/gl/GrGLVertexArray.cpp
index dbfa0f4..68bcf21 100644
--- a/src/gpu/gl/GrGLVertexArray.cpp
+++ b/src/gpu/gl/GrGLVertexArray.cpp
@@ -150,7 +150,7 @@
 }
 
 void GrGLAttribArrayState::enableVertexArrays(const GrGLGpu* gpu, int enabledCount,
-                                              EnablePrimitiveRestart enablePrimitiveRestart) {
+                                              GrPrimitiveRestart enablePrimitiveRestart) {
     SkASSERT(enabledCount <= fAttribArrayStates.count());
 
     if (!fEnableStateIsValid || enabledCount != fNumEnabledArrays) {
@@ -167,12 +167,12 @@
         fNumEnabledArrays = enabledCount;
     }
 
-    SkASSERT(EnablePrimitiveRestart::kNo == enablePrimitiveRestart ||
+    SkASSERT(GrPrimitiveRestart::kNo == enablePrimitiveRestart ||
              gpu->caps()->usePrimitiveRestart());
 
     if (gpu->caps()->usePrimitiveRestart() &&
         (!fEnableStateIsValid || enablePrimitiveRestart != fPrimitiveRestartEnabled)) {
-        if (EnablePrimitiveRestart::kYes == enablePrimitiveRestart) {
+        if (GrPrimitiveRestart::kYes == enablePrimitiveRestart) {
             GR_GL_CALL(gpu->glInterface(), Enable(GR_GL_PRIMITIVE_RESTART_FIXED_INDEX));
         } else {
             GR_GL_CALL(gpu->glInterface(), Disable(GR_GL_PRIMITIVE_RESTART_FIXED_INDEX));
diff --git a/src/gpu/gl/GrGLVertexArray.h b/src/gpu/gl/GrGLVertexArray.h
index 20b21ae..e460c04 100644
--- a/src/gpu/gl/GrGLVertexArray.h
+++ b/src/gpu/gl/GrGLVertexArray.h
@@ -45,16 +45,11 @@
              size_t offsetInBytes,
              int divisor = 0);
 
-    enum class EnablePrimitiveRestart : bool {
-        kYes = true,
-        kNo = false
-    };
-
     /**
      * This function enables the first 'enabledCount' vertex arrays and disables the rest.
      */
     void enableVertexArrays(const GrGLGpu*, int enabledCount,
-                            EnablePrimitiveRestart = EnablePrimitiveRestart::kNo);
+                            GrPrimitiveRestart = GrPrimitiveRestart::kNo);
 
     void invalidate() {
         int count = fAttribArrayStates.count();
@@ -90,7 +85,7 @@
 
     SkSTArray<16, AttribArrayState, true> fAttribArrayStates;
     int fNumEnabledArrays;
-    EnablePrimitiveRestart fPrimitiveRestartEnabled;
+    GrPrimitiveRestart fPrimitiveRestartEnabled;
     bool fEnableStateIsValid = false;
 };
 
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 86dcfef..0cdc3b5 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -66,11 +66,13 @@
                                        const GrPipeline& pipeline,
                                        const GrPrimitiveProcessor& primProc,
                                        GrProgramDesc* desc)
-    : INHERITED(pipeline, primProc, desc)
-    , fGpu(gpu)
-    , fVaryingHandler(this)
-    , fUniformHandler(this) {
-}
+        : INHERITED(pipeline, primProc, desc)
+        , fGpu(gpu)
+        , fVaryingHandler(this)
+        , fUniformHandler(this)
+        , fAttributeCnt(0)
+        , fVertexStride(0)
+        , fInstanceStride(0) {}
 
 const GrCaps* GrGLProgramBuilder::caps() const {
     return fGpu->caps();
@@ -225,9 +227,17 @@
         // NVPR actually requires a vertex shader to compile
         bool useNvpr = primProc.isPathRendering();
         if (!useNvpr) {
-            int vaCount = primProc.numAttribs();
-            for (int i = 0; i < vaCount; i++) {
-                GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).name()));
+            fAttributeCnt = primProc.numAttribs();
+            fAttributes.reset(new GrGLProgram::Attribute[fAttributeCnt]);
+            fVertexStride = primProc.getVertexStride();
+            fInstanceStride = primProc.getInstanceStride();
+            for (int i = 0; i < fAttributeCnt; i++) {
+                const auto& attr = primProc.getAttrib(i);
+                fAttributes[i].fInputRate = attr.inputRate();
+                fAttributes[i].fType = attr.type();
+                fAttributes[i].fOffset = attr.offsetInRecord();
+                fAttributes[i].fLocation = i;
+                GL_CALL(BindAttribLocation(programID, i, attr.name()));
             }
         }
 
@@ -379,7 +389,7 @@
 }
 void GrGLProgramBuilder::cleanupShaders(const SkTDArray<GrGLuint>& shaderIDs) {
     for (int i = 0; i < shaderIDs.count(); ++i) {
-      GL_CALL(DeleteShader(shaderIDs[i]));
+        GL_CALL(DeleteShader(shaderIDs[i]));
     }
 }
 
@@ -394,5 +404,9 @@
                            std::move(fGeometryProcessor),
                            std::move(fXferProcessor),
                            std::move(fFragmentProcessors),
-                           fFragmentProcessorCnt);
+                           fFragmentProcessorCnt,
+                           std::move(fAttributes),
+                           fAttributeCnt,
+                           fVertexStride,
+                           fInstanceStride);
 }
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 54c91b4..ea3432b 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -9,6 +9,7 @@
 #define GrGLProgramBuilder_DEFINED
 
 #include "GrPipeline.h"
+#include "gl/GrGLProgram.h"
 #include "gl/GrGLProgramDataManager.h"
 #include "gl/GrGLUniformHandler.h"
 #include "gl/GrGLVaryingHandler.h"
@@ -79,6 +80,11 @@
     GrGLVaryingHandler    fVaryingHandler;
     GrGLUniformHandler    fUniformHandler;
 
+    std::unique_ptr<GrGLProgram::Attribute[]> fAttributes;
+    int fAttributeCnt;
+    int fVertexStride;
+    int fInstanceStride;
+
     // shader pulled from cache. Data is organized as:
     // SkSL::Program::Inputs inputs
     // int binaryFormat