text: /*
   * Copyright 2013 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "GrGLVertexArray.h"
  #include "GrGLGpu.h"
  
  struct AttribLayout {
      GrGLint     fCount;
      GrGLenum    fType;
      GrGLboolean fNormalized;  // Only used by floating point types.
  };
  
  static const AttribLayout gLayouts[kGrVertexAttribTypeCount] = {
      {1, GR_GL_FLOAT, false},         // kFloat_GrVertexAttribType
      {2, GR_GL_FLOAT, false},         // kVec2f_GrVertexAttribType
      {3, GR_GL_FLOAT, false},         // kVec3f_GrVertexAttribType
      {4, GR_GL_FLOAT, false},         // kVec4f_GrVertexAttribType
      {1, GR_GL_UNSIGNED_BYTE, true},  // kUByte_GrVertexAttribType
      {4, GR_GL_UNSIGNED_BYTE, true},  // kVec4ub_GrVertexAttribType
      {2, GR_GL_UNSIGNED_SHORT, true}, // kVec2s_GrVertexAttribType
      {1, GR_GL_INT, false},           // kInt_GrVertexAttribType
      {1, GR_GL_UNSIGNED_INT, false},  // kUint_GrVertexAttribType
  };
  
  GR_STATIC_ASSERT(0 == kFloat_GrVertexAttribType);
  GR_STATIC_ASSERT(1 == kVec2f_GrVertexAttribType);
  GR_STATIC_ASSERT(2 == kVec3f_GrVertexAttribType);
  GR_STATIC_ASSERT(3 == kVec4f_GrVertexAttribType);
  GR_STATIC_ASSERT(4 == kUByte_GrVertexAttribType);
  GR_STATIC_ASSERT(5 == kVec4ub_GrVertexAttribType);
  GR_STATIC_ASSERT(6 == kVec2us_GrVertexAttribType);
  GR_STATIC_ASSERT(7 == kInt_GrVertexAttribType);
  GR_STATIC_ASSERT(8 == kUint_GrVertexAttribType);
  
  void GrGLAttribArrayState::set(GrGLGpu* gpu,
                                 int index,
                                 GrGLuint vertexBufferID,
                                 GrVertexAttribType type,
                                 GrGLsizei stride,
                                 GrGLvoid* offset) {
      SkASSERT(index >= 0 && index < fAttribArrayStates.count());
      AttribArrayState* array = &fAttribArrayStates[index];
      if (!array->fEnableIsValid || !array->fEnabled) {
          GR_GL_CALL(gpu->glInterface(), EnableVertexAttribArray(index));
          array->fEnableIsValid = true;
          array->fEnabled = true;
      }
      if (!array->fAttribPointerIsValid ||
          array->fVertexBufferID != vertexBufferID ||
          array->fType != type ||
          array->fStride != stride ||
          array->fOffset != offset) {
  
          gpu->bindVertexBuffer(vertexBufferID);
          const AttribLayout& layout = gLayouts[type];
          if (!GrVertexAttribTypeIsIntType(type)) {
              GR_GL_CALL(gpu->glInterface(), VertexAttribPointer(index,
                                                                 layout.fCount,
                                                                 layout.fType,
                                                                 layout.fNormalized,
                                                                 stride,
                                                                 offset));
          } else {
              SkASSERT(gpu->caps()->shaderCaps()->integerSupport());
              SkASSERT(!layout.fNormalized);
              GR_GL_CALL(gpu->glInterface(), VertexAttribIPointer(index,
                                                                  layout.fCount,
                                                                  layout.fType,
                                                                  stride,
                                                                  offset));
          }
          array->fAttribPointerIsValid = true;
          array->fVertexBufferID = vertexBufferID;
          array->fType = type;
          array->fStride = stride;
          array->fOffset = offset;
      }
  }
  
  void GrGLAttribArrayState::disableUnusedArrays(const GrGLGpu* gpu, uint64_t usedMask) {
      int count = fAttribArrayStates.count();
      for (int i = 0; i < count; ++i) {
          if (!(usedMask & 0x1)) {
              if (!fAttribArrayStates[i].fEnableIsValid || fAttribArrayStates[i].fEnabled) {
                  GR_GL_CALL(gpu->glInterface(), DisableVertexAttribArray(i));
                  fAttribArrayStates[i].fEnableIsValid = true;
                  fAttribArrayStates[i].fEnabled = false;
              }
          } else {
              SkASSERT(fAttribArrayStates[i].fEnableIsValid && fAttribArrayStates[i].fEnabled);
          }
          // if the count is greater than 64 then this will become 0 and we will disable arrays 64+.
          usedMask >>= 1;
      }
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  
  GrGLVertexArray::GrGLVertexArray(GrGLint id, int attribCount)
      : fID(id)
      , fAttribArrays(attribCount)
      , fIndexBufferIDIsValid(false) {
  }
  
  GrGLAttribArrayState* GrGLVertexArray::bind(GrGLGpu* gpu) {
      if (0 == fID) {
          return nullptr;
      }
      gpu->bindVertexArray(fID);
      return &fAttribArrays;
  }
  
  GrGLAttribArrayState* GrGLVertexArray::bindWithIndexBuffer(GrGLGpu* gpu, GrGLuint ibufferID) {
      GrGLAttribArrayState* state = this->bind(gpu);
      if (state) {
          if (!fIndexBufferIDIsValid || ibufferID != fIndexBufferID) {            
              GR_GL_CALL(gpu->glInterface(), BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, ibufferID));
              fIndexBufferIDIsValid = true;
              fIndexBufferID = ibufferID;
          }
      }
      return state;
  }
  
  void GrGLVertexArray::notifyIndexBufferDelete(GrGLuint bufferID) {
      if (fIndexBufferIDIsValid && bufferID == fIndexBufferID) {
          fIndexBufferID = 0;
      }
   }
  
  void GrGLVertexArray::invalidateCachedState() {
      fAttribArrays.invalidate();
      fIndexBufferIDIsValid = false;
  }
  