mesa: Add a vao parameter to get_vertex_array_attrib
[mesa.git] / src / mesa / main / varray.c
index f1a57c1f08980f2ed00334667f82e2d3a8392ff2..7f14ffb297096d9811e7b42c8b578c3c478213da 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.6
  *
  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  */
 
 
+#include <stdio.h>
+#include <inttypes.h>  /* for PRId64 macro */
+
 #include "glheader.h"
 #include "imports.h"
 #include "bufferobj.h"
@@ -33,7 +36,6 @@
 #include "hash.h"
 #include "image.h"
 #include "macros.h"
-#include "mfeatures.h"
 #include "mtypes.h"
 #include "varray.h"
 #include "arrayobj.h"
 /** Used to indicate which GL datatypes are accepted by each of the
  * glVertex/Color/Attrib/EtcPointer() functions.
  */
-#define BOOL_BIT             0x1
-#define BYTE_BIT             0x2
-#define UNSIGNED_BYTE_BIT    0x4
-#define SHORT_BIT            0x8
-#define UNSIGNED_SHORT_BIT   0x10
-#define INT_BIT              0x20
-#define UNSIGNED_INT_BIT     0x40
-#define HALF_BIT             0x80
-#define FLOAT_BIT            0x100
-#define DOUBLE_BIT           0x200
-#define FIXED_ES_BIT         0x400
-#define FIXED_GL_BIT         0x800
-#define UNSIGNED_INT_2_10_10_10_REV_BIT 0x1000
-#define INT_2_10_10_10_REV_BIT 0x2000
+#define BOOL_BIT                          (1 << 0)
+#define BYTE_BIT                          (1 << 1)
+#define UNSIGNED_BYTE_BIT                 (1 << 2)
+#define SHORT_BIT                         (1 << 3)
+#define UNSIGNED_SHORT_BIT                (1 << 4)
+#define INT_BIT                           (1 << 5)
+#define UNSIGNED_INT_BIT                  (1 << 6)
+#define HALF_BIT                          (1 << 7)
+#define FLOAT_BIT                         (1 << 8)
+#define DOUBLE_BIT                        (1 << 9)
+#define FIXED_ES_BIT                      (1 << 10)
+#define FIXED_GL_BIT                      (1 << 11)
+#define UNSIGNED_INT_2_10_10_10_REV_BIT   (1 << 12)
+#define INT_2_10_10_10_REV_BIT            (1 << 13)
+#define UNSIGNED_INT_10F_11F_11F_REV_BIT  (1 << 14)
+#define ALL_TYPE_BITS                    ((1 << 15) - 1)
+
+#define ATTRIB_FORMAT_TYPES_MASK (BYTE_BIT | UNSIGNED_BYTE_BIT | \
+                                  SHORT_BIT | UNSIGNED_SHORT_BIT | \
+                                  INT_BIT | UNSIGNED_INT_BIT | \
+                                  HALF_BIT | FLOAT_BIT | DOUBLE_BIT | \
+                                  FIXED_GL_BIT | \
+                                  UNSIGNED_INT_2_10_10_10_REV_BIT | \
+                                  INT_2_10_10_10_REV_BIT | \
+                                  UNSIGNED_INT_10F_11F_11F_REV_BIT)
+
+#define ATTRIB_IFORMAT_TYPES_MASK (BYTE_BIT | UNSIGNED_BYTE_BIT | \
+                                   SHORT_BIT | UNSIGNED_SHORT_BIT | \
+                                   INT_BIT | UNSIGNED_INT_BIT)
+
+#define ATTRIB_LFORMAT_TYPES_MASK DOUBLE_BIT
 
 
 /** Convert GL datatype enum into a <type>_BIT value seen above */
@@ -92,11 +111,13 @@ type_to_bit(const struct gl_context *ctx, GLenum type)
    case GL_DOUBLE:
       return DOUBLE_BIT;
    case GL_FIXED:
-      return ctx->API == API_OPENGL ? FIXED_GL_BIT : FIXED_ES_BIT;
+      return _mesa_is_desktop_gl(ctx) ? FIXED_GL_BIT : FIXED_ES_BIT;
    case GL_UNSIGNED_INT_2_10_10_10_REV:
       return UNSIGNED_INT_2_10_10_10_REV_BIT;
    case GL_INT_2_10_10_10_REV:
       return INT_2_10_10_10_REV_BIT;
+   case GL_UNSIGNED_INT_10F_11F_11F_REV:
+      return UNSIGNED_INT_10F_11F_11F_REV_BIT;
    default:
       return 0;
    }
@@ -104,53 +125,183 @@ type_to_bit(const struct gl_context *ctx, GLenum type)
 
 
 /**
- * Do error checking and update state for glVertex/Color/TexCoord/...Pointer
- * functions.
- *
- * \param func  name of calling function used for error reporting
- * \param array  the array to update
- * \param dirtyBit  which bit to set in ctx->Array.NewState for this array
- * \param legalTypes  bitmask of *_BIT above indicating legal datatypes
- * \param sizeMin  min allowable size value
- * \param sizeMax  max allowable size value (may also be BGRA_OR_4)
- * \param size  components per element (1, 2, 3 or 4)
- * \param type  datatype of each component (GL_FLOAT, GL_INT, etc)
- * \param stride  stride between elements, in elements
- * \param normalized  are integer types converted to floats in [-1, 1]?
- * \param integer  integer-valued values (will not be normalized to [-1,1])
- * \param ptr  the address (or offset inside VBO) of the array data
+ * Sets the VertexBinding field in the vertex attribute given by attribIndex.
  */
 static void
-update_array(struct gl_context *ctx,
-             const char *func,
-             struct gl_client_array *array,
-             GLbitfield dirtyBit, GLbitfield legalTypesMask,
-             GLint sizeMin, GLint sizeMax,
-             GLint size, GLenum type, GLsizei stride,
-             GLboolean normalized, GLboolean integer,
-             const GLvoid *ptr)
+vertex_attrib_binding(struct gl_context *ctx,
+                      struct gl_vertex_array_object *vao,
+                      GLuint attribIndex,
+                      GLuint bindingIndex)
 {
-   GLbitfield typeBit;
-   GLsizei elementSize;
-   GLenum format = GL_RGBA;
+   struct gl_vertex_attrib_array *array = &vao->VertexAttrib[attribIndex];
+
+   if (array->VertexBinding != bindingIndex) {
+      const GLbitfield64 array_bit = VERT_BIT(attribIndex);
+
+      FLUSH_VERTICES(ctx, _NEW_ARRAY);
+
+      vao->VertexBinding[array->VertexBinding]._BoundArrays &= ~array_bit;
+      vao->VertexBinding[bindingIndex]._BoundArrays |= array_bit;
+
+      array->VertexBinding = bindingIndex;
+
+      vao->NewArrays |= array_bit;
+   }
+}
+
+
+/**
+ * Binds a buffer object to the vertex buffer binding point given by index,
+ * and sets the Offset and Stride fields.
+ */
+static void
+bind_vertex_buffer(struct gl_context *ctx,
+                   struct gl_vertex_array_object *vao,
+                   GLuint index,
+                   struct gl_buffer_object *vbo,
+                   GLintptr offset, GLsizei stride)
+{
+   struct gl_vertex_buffer_binding *binding = &vao->VertexBinding[index];
+
+   if (binding->BufferObj != vbo ||
+       binding->Offset != offset ||
+       binding->Stride != stride) {
+
+      FLUSH_VERTICES(ctx, _NEW_ARRAY);
+
+      _mesa_reference_buffer_object(ctx, &binding->BufferObj, vbo);
+
+      binding->Offset = offset;
+      binding->Stride = stride;
+
+      vao->NewArrays |= binding->_BoundArrays;
+   }
+}
+
+
+/**
+ * Sets the InstanceDivisor field in the vertex buffer binding point
+ * given by bindingIndex.
+ */
+static void
+vertex_binding_divisor(struct gl_context *ctx,
+                       struct gl_vertex_array_object *vao,
+                       GLuint bindingIndex,
+                       GLuint divisor)
+{
+   struct gl_vertex_buffer_binding *binding =
+      &vao->VertexBinding[bindingIndex];
+
+   if (binding->InstanceDivisor != divisor) {
+      FLUSH_VERTICES(ctx, _NEW_ARRAY);
+      binding->InstanceDivisor = divisor;
+      vao->NewArrays |= binding->_BoundArrays;
+   }
+}
 
-   if (ctx->API != API_OPENGLES && ctx->API != API_OPENGLES2) {
-      /* fixed point arrays / data is only allowed with OpenGL ES 1.x/2.0 */
+
+/**
+ * Examine the API profile and extensions to determine which types are legal
+ * for vertex arrays.  This is called once from update_array_format().
+ */
+static GLbitfield
+get_legal_types_mask(const struct gl_context *ctx)
+{
+   GLbitfield legalTypesMask = ALL_TYPE_BITS;
+
+   if (_mesa_is_gles(ctx)) {
+      legalTypesMask &= ~(FIXED_GL_BIT |
+                          DOUBLE_BIT |
+                          UNSIGNED_INT_10F_11F_11F_REV_BIT);
+
+      /* GL_INT and GL_UNSIGNED_INT data is not allowed in OpenGL ES until
+       * 3.0.  The 2_10_10_10 types are added in OpenGL ES 3.0 or
+       * GL_OES_vertex_type_10_10_10_2.  GL_HALF_FLOAT data is not allowed
+       * until 3.0 or with the GL_OES_vertex_half float extension, which isn't
+       * quite as trivial as we'd like because it uses a different enum value
+       * for GL_HALF_FLOAT_OES.
+       */
+      if (ctx->Version < 30) {
+         legalTypesMask &= ~(UNSIGNED_INT_BIT |
+                             INT_BIT |
+                             UNSIGNED_INT_2_10_10_10_REV_BIT |
+                             INT_2_10_10_10_REV_BIT |
+                             HALF_BIT);
+      }
+   }
+   else {
       legalTypesMask &= ~FIXED_ES_BIT;
+
+      if (!ctx->Extensions.ARB_ES2_compatibility)
+         legalTypesMask &= ~FIXED_GL_BIT;
+
+      if (!ctx->Extensions.ARB_vertex_type_2_10_10_10_rev)
+         legalTypesMask &= ~(UNSIGNED_INT_2_10_10_10_REV_BIT |
+                             INT_2_10_10_10_REV_BIT);
+
+      if (!ctx->Extensions.ARB_vertex_type_10f_11f_11f_rev)
+         legalTypesMask &= ~UNSIGNED_INT_10F_11F_11F_REV_BIT;
    }
-   if (!ctx->Extensions.ARB_ES2_compatibility) {
-      legalTypesMask &= ~FIXED_GL_BIT;
+
+   return legalTypesMask;
+}
+
+
+/**
+ * Does error checking and updates the format in an attrib array.
+ *
+ * Called by update_array() and VertexAttrib*Format().
+ *
+ * \param func         Name of calling function used for error reporting
+ * \param attrib       The index of the attribute array
+ * \param legalTypes   Bitmask of *_BIT above indicating legal datatypes
+ * \param sizeMin      Min allowable size value
+ * \param sizeMax      Max allowable size value (may also be BGRA_OR_4)
+ * \param size         Components per element (1, 2, 3 or 4)
+ * \param type         Datatype of each component (GL_FLOAT, GL_INT, etc)
+ * \param normalized   Whether integer types are converted to floats in [-1, 1]
+ * \param integer      Integer-valued values (will not be normalized to [-1, 1])
+ * \param doubles      Double values not reduced to floats
+ * \param relativeOffset Offset of the first element relative to the binding offset.
+ */
+static bool
+update_array_format(struct gl_context *ctx,
+                    const char *func,
+                    struct gl_vertex_array_object *vao,
+                    GLuint attrib, GLbitfield legalTypesMask,
+                    GLint sizeMin, GLint sizeMax,
+                    GLint size, GLenum type,
+                    GLboolean normalized, GLboolean integer, GLboolean doubles,
+                    GLuint relativeOffset)
+{
+   struct gl_vertex_attrib_array *array;
+   GLbitfield typeBit;
+   GLint elementSize;
+   GLenum format = GL_RGBA;
+
+   if (ctx->Array.LegalTypesMask == 0 || ctx->Array.LegalTypesMaskAPI != ctx->API) {
+      /* Compute the LegalTypesMask only once, unless the context API has
+       * changed, in which case we want to compute it again.  We can't do this
+       * in _mesa_init_varrays() below because extensions are not yet enabled
+       * at that point.
+       */
+      ctx->Array.LegalTypesMask = get_legal_types_mask(ctx);
+      ctx->Array.LegalTypesMaskAPI = ctx->API;
    }
-   if (!ctx->Extensions.ARB_vertex_type_2_10_10_10_rev) {
-      legalTypesMask &= ~(UNSIGNED_INT_2_10_10_10_REV_BIT |
-                          INT_2_10_10_10_REV_BIT);
+
+   legalTypesMask &= ctx->Array.LegalTypesMask;
+
+   if (_mesa_is_gles(ctx) && sizeMax == BGRA_OR_4) {
+      /* BGRA ordering is not supported in ES contexts.
+       */
+      sizeMax = 4;
    }
 
    typeBit = type_to_bit(ctx, type);
    if (typeBit == 0x0 || (typeBit & legalTypesMask) == 0x0) {
       _mesa_error(ctx, GL_INVALID_ENUM, "%s(type = %s)",
                   func, _mesa_lookup_enum_by_nr(type));
-      return;
+      return false;
    }
 
    /* Do size parameter checking.
@@ -160,139 +311,262 @@ update_array(struct gl_context *ctx,
    if (ctx->Extensions.EXT_vertex_array_bgra &&
        sizeMax == BGRA_OR_4 &&
        size == GL_BGRA) {
-      GLboolean bgra_error = GL_FALSE;
+      /* Page 298 of the PDF of the OpenGL 4.3 (Core Profile) spec says:
+       *
+       * "An INVALID_OPERATION error is generated under any of the following
+       *  conditions:
+       *    ...
+       *    • size is BGRA and type is not UNSIGNED_BYTE, INT_2_10_10_10_REV
+       *      or UNSIGNED_INT_2_10_10_10_REV;
+       *    ...
+       *    • size is BGRA and normalized is FALSE;"
+       */
+      bool bgra_error = false;
 
       if (ctx->Extensions.ARB_vertex_type_2_10_10_10_rev) {
          if (type != GL_UNSIGNED_INT_2_10_10_10_REV &&
              type != GL_INT_2_10_10_10_REV &&
              type != GL_UNSIGNED_BYTE)
-            bgra_error = GL_TRUE;
+            bgra_error = true;
       } else if (type != GL_UNSIGNED_BYTE)
-         bgra_error = GL_TRUE;
+         bgra_error = true;
 
       if (bgra_error) {
-         _mesa_error(ctx, GL_INVALID_VALUE, "%s(GL_BGRA/GLubyte)", func);
-         return;
+         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(size=GL_BGRA and type=%s)",
+                     func, _mesa_lookup_enum_by_nr(type));
+         return false;
+      }
+
+      if (!normalized) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "%s(size=GL_BGRA and normalized=GL_FALSE)", func);
+         return false;
       }
+
       format = GL_BGRA;
       size = 4;
    }
    else if (size < sizeMin || size > sizeMax || size > 4) {
       _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d)", func, size);
-      return;
+      return false;
    }
 
    if (ctx->Extensions.ARB_vertex_type_2_10_10_10_rev &&
        (type == GL_UNSIGNED_INT_2_10_10_10_REV ||
         type == GL_INT_2_10_10_10_REV) && size != 4) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(size=%d)", func, size);
+      return false;
    }
 
-   ASSERT(size <= 4);
-
-   if (stride < 0) {
-      _mesa_error( ctx, GL_INVALID_VALUE, "%s(stride=%d)", func, stride );
-      return;
+   /* The ARB_vertex_attrib_binding_spec says:
+    *
+    *   An INVALID_VALUE error is generated if <relativeoffset> is larger than
+    *   the value of MAX_VERTEX_ATTRIB_RELATIVE_OFFSET.
+    */
+   if (relativeOffset > ctx->Const.MaxVertexAttribRelativeOffset) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(relativeOffset=%d > "
+                  "GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET)",
+                  func, relativeOffset);
+      return false;
    }
 
-   if (ctx->Array.ArrayObj->VBOonly &&
-       ctx->Array.ArrayBufferObj->Name == 0) {
-      /* GL_ARB_vertex_array_object requires that all arrays reside in VBOs.
-       * Generate GL_INVALID_OPERATION if that's not true.
-       */
-      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(non-VBO array)", func);
-      return;
+   if (ctx->Extensions.ARB_vertex_type_10f_11f_11f_rev &&
+         type == GL_UNSIGNED_INT_10F_11F_11F_REV && size != 3) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(size=%d)", func, size);
+      return false;
    }
 
-   elementSize = _mesa_sizeof_type(type) * size;
+   assert(size <= 4);
+
+   elementSize = _mesa_bytes_per_vertex_attrib(size, type);
+   assert(elementSize != -1);
 
+   array = &vao->VertexAttrib[attrib];
    array->Size = size;
    array->Type = type;
    array->Format = format;
-   array->Stride = stride;
-   array->StrideB = stride ? stride : elementSize;
    array->Normalized = normalized;
    array->Integer = integer;
-   array->Ptr = (const GLubyte *) ptr;
+   array->Doubles = doubles;
+   array->RelativeOffset = relativeOffset;
    array->_ElementSize = elementSize;
 
-   _mesa_reference_buffer_object(ctx, &array->BufferObj,
-                                 ctx->Array.ArrayBufferObj);
-
+   vao->NewArrays |= VERT_BIT(attrib);
    ctx->NewState |= _NEW_ARRAY;
-   ctx->Array.NewState |= dirtyBit;
+
+   return true;
+}
+
+
+/**
+ * Do error checking and update state for glVertex/Color/TexCoord/...Pointer
+ * functions.
+ *
+ * \param func  name of calling function used for error reporting
+ * \param attrib  the attribute array index to update
+ * \param legalTypes  bitmask of *_BIT above indicating legal datatypes
+ * \param sizeMin  min allowable size value
+ * \param sizeMax  max allowable size value (may also be BGRA_OR_4)
+ * \param size  components per element (1, 2, 3 or 4)
+ * \param type  datatype of each component (GL_FLOAT, GL_INT, etc)
+ * \param stride  stride between elements, in elements
+ * \param normalized  are integer types converted to floats in [-1, 1]?
+ * \param integer  integer-valued values (will not be normalized to [-1,1])
+ * \param doubles  Double values not reduced to floats
+ * \param ptr  the address (or offset inside VBO) of the array data
+ */
+static void
+update_array(struct gl_context *ctx,
+             const char *func,
+             GLuint attrib, GLbitfield legalTypesMask,
+             GLint sizeMin, GLint sizeMax,
+             GLint size, GLenum type, GLsizei stride,
+             GLboolean normalized, GLboolean integer, GLboolean doubles,
+             const GLvoid *ptr)
+{
+   struct gl_vertex_attrib_array *array;
+   GLsizei effectiveStride;
+
+   /* Page 407 (page 423 of the PDF) of the OpenGL 3.0 spec says:
+    *
+    *     "Client vertex arrays - all vertex array attribute pointers must
+    *     refer to buffer objects (section 2.9.2). The default vertex array
+    *     object (the name zero) is also deprecated. Calling
+    *     VertexAttribPointer when no buffer object or no vertex array object
+    *     is bound will generate an INVALID_OPERATION error..."
+    *
+    * The check for VBOs is handled below.
+    */
+   if (ctx->API == API_OPENGL_CORE
+       && (ctx->Array.VAO == ctx->Array.DefaultVAO)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(no array object bound)",
+                  func);
+      return;
+   }
+
+   if (stride < 0) {
+      _mesa_error( ctx, GL_INVALID_VALUE, "%s(stride=%d)", func, stride );
+      return;
+   }
+
+   if (ctx->API == API_OPENGL_CORE && ctx->Version >= 44 &&
+       stride > ctx->Const.MaxVertexAttribStride) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(stride=%d > "
+                  "GL_MAX_VERTEX_ATTRIB_STRIDE)", func, stride);
+      return;
+   }
+
+   /* Page 29 (page 44 of the PDF) of the OpenGL 3.3 spec says:
+    *
+    *     "An INVALID_OPERATION error is generated under any of the following
+    *     conditions:
+    *
+    *     ...
+    *
+    *     * any of the *Pointer commands specifying the location and
+    *       organization of vertex array data are called while zero is bound
+    *       to the ARRAY_BUFFER buffer object binding point (see section
+    *       2.9.6), and the pointer argument is not NULL."
+    */
+   if (ptr != NULL && ctx->Array.VAO->ARBsemantics &&
+       !_mesa_is_bufferobj(ctx->Array.ArrayBufferObj)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(non-VBO array)", func);
+      return;
+   }
+
+   if (!update_array_format(ctx, func, ctx->Array.VAO, attrib,
+                            legalTypesMask, sizeMin, sizeMax,
+                            size, type, normalized, integer, doubles, 0)) {
+      return;
+   }
+
+   /* Reset the vertex attrib binding */
+   vertex_attrib_binding(ctx, ctx->Array.VAO, attrib, attrib);
+
+   /* The Stride and Ptr fields are not set by update_array_format() */
+   array = &ctx->Array.VAO->VertexAttrib[attrib];
+   array->Stride = stride;
+   array->Ptr = (const GLvoid *) ptr;
+
+   /* Update the vertex buffer binding */
+   effectiveStride = stride != 0 ? stride : array->_ElementSize;
+   bind_vertex_buffer(ctx, ctx->Array.VAO, attrib, ctx->Array.ArrayBufferObj,
+                      (GLintptr) ptr, effectiveStride);
 }
 
 
 void GLAPIENTRY
 _mesa_VertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr)
 {
-   GLbitfield legalTypes = (SHORT_BIT | INT_BIT | FLOAT_BIT |
-                            DOUBLE_BIT | HALF_BIT | FIXED_ES_BIT |
-                            UNSIGNED_INT_2_10_10_10_REV_BIT |
-                            INT_2_10_10_10_REV_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   GLbitfield legalTypes = (ctx->API == API_OPENGLES)
+      ? (BYTE_BIT | SHORT_BIT | FLOAT_BIT | FIXED_ES_BIT)
+      : (SHORT_BIT | INT_BIT | FLOAT_BIT |
+         DOUBLE_BIT | HALF_BIT |
+         UNSIGNED_INT_2_10_10_10_REV_BIT |
+         INT_2_10_10_10_REV_BIT);
 
-   if (ctx->API == API_OPENGLES)
-      legalTypes |= BYTE_BIT;
+   FLUSH_VERTICES(ctx, 0);
 
-   update_array(ctx, "glVertexPointer",
-                &ctx->Array.ArrayObj->Vertex, _NEW_ARRAY_VERTEX,
+   update_array(ctx, "glVertexPointer", VERT_ATTRIB_POS,
                 legalTypes, 2, 4,
-                size, type, stride, GL_FALSE, GL_FALSE, ptr);
+                size, type, stride, GL_FALSE, GL_FALSE, GL_FALSE, ptr);
 }
 
 
 void GLAPIENTRY
 _mesa_NormalPointer(GLenum type, GLsizei stride, const GLvoid *ptr )
 {
-   const GLbitfield legalTypes = (BYTE_BIT | SHORT_BIT | INT_BIT |
-                                  HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
-                                  FIXED_ES_BIT |
-                                  UNSIGNED_INT_2_10_10_10_REV_BIT |
-                                  INT_2_10_10_10_REV_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   const GLbitfield legalTypes = (ctx->API == API_OPENGLES)
+      ? (BYTE_BIT | SHORT_BIT | FLOAT_BIT | FIXED_ES_BIT)
+      : (BYTE_BIT | SHORT_BIT | INT_BIT |
+         HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
+         UNSIGNED_INT_2_10_10_10_REV_BIT |
+         INT_2_10_10_10_REV_BIT);
 
-   update_array(ctx, "glNormalPointer",
-                &ctx->Array.ArrayObj->Normal, _NEW_ARRAY_NORMAL,
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array(ctx, "glNormalPointer", VERT_ATTRIB_NORMAL,
                 legalTypes, 3, 3,
-                3, type, stride, GL_TRUE, GL_FALSE, ptr);
+                3, type, stride, GL_TRUE, GL_FALSE, GL_FALSE, ptr);
 }
 
 
 void GLAPIENTRY
 _mesa_ColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr)
 {
-   const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT |
-                                  SHORT_BIT | UNSIGNED_SHORT_BIT |
-                                  INT_BIT | UNSIGNED_INT_BIT |
-                                  HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
-                                  FIXED_ES_BIT |
-                                  UNSIGNED_INT_2_10_10_10_REV_BIT |
-                                  INT_2_10_10_10_REV_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   update_array(ctx, "glColorPointer",
-                &ctx->Array.ArrayObj->Color, _NEW_ARRAY_COLOR0,
-                legalTypes, 3, BGRA_OR_4,
-                size, type, stride, GL_TRUE, GL_FALSE, ptr);
+   const GLbitfield legalTypes = (ctx->API == API_OPENGLES)
+      ? (UNSIGNED_BYTE_BIT | HALF_BIT | FLOAT_BIT | FIXED_ES_BIT)
+      : (BYTE_BIT | UNSIGNED_BYTE_BIT |
+         SHORT_BIT | UNSIGNED_SHORT_BIT |
+         INT_BIT | UNSIGNED_INT_BIT |
+         HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
+         UNSIGNED_INT_2_10_10_10_REV_BIT |
+         INT_2_10_10_10_REV_BIT);
+   const GLint sizeMin = (ctx->API == API_OPENGLES) ? 4 : 3;
+
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array(ctx, "glColorPointer", VERT_ATTRIB_COLOR0,
+                legalTypes, sizeMin, BGRA_OR_4,
+                size, type, stride, GL_TRUE, GL_FALSE, GL_FALSE, ptr);
 }
 
 
 void GLAPIENTRY
-_mesa_FogCoordPointerEXT(GLenum type, GLsizei stride, const GLvoid *ptr)
+_mesa_FogCoordPointer(GLenum type, GLsizei stride, const GLvoid *ptr)
 {
    const GLbitfield legalTypes = (HALF_BIT | FLOAT_BIT | DOUBLE_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   update_array(ctx, "glFogCoordPointer",
-                &ctx->Array.ArrayObj->FogCoord, _NEW_ARRAY_FOGCOORD,
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array(ctx, "glFogCoordPointer", VERT_ATTRIB_FOG,
                 legalTypes, 1, 1,
-                1, type, stride, GL_FALSE, GL_FALSE, ptr);
+                1, type, stride, GL_FALSE, GL_FALSE, GL_FALSE, ptr);
 }
 
 
@@ -302,17 +576,17 @@ _mesa_IndexPointer(GLenum type, GLsizei stride, const GLvoid *ptr)
    const GLbitfield legalTypes = (UNSIGNED_BYTE_BIT | SHORT_BIT | INT_BIT |
                                   FLOAT_BIT | DOUBLE_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   update_array(ctx, "glIndexPointer",
-                &ctx->Array.ArrayObj->Index, _NEW_ARRAY_INDEX,
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array(ctx, "glIndexPointer", VERT_ATTRIB_COLOR_INDEX,
                 legalTypes, 1, 1,
-                1, type, stride, GL_FALSE, GL_FALSE, ptr);
+                1, type, stride, GL_FALSE, GL_FALSE, GL_FALSE, ptr);
 }
 
 
 void GLAPIENTRY
-_mesa_SecondaryColorPointerEXT(GLint size, GLenum type,
+_mesa_SecondaryColorPointer(GLint size, GLenum type,
                               GLsizei stride, const GLvoid *ptr)
 {
    const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT |
@@ -322,12 +596,12 @@ _mesa_SecondaryColorPointerEXT(GLint size, GLenum type,
                                   UNSIGNED_INT_2_10_10_10_REV_BIT |
                                   INT_2_10_10_10_REV_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   update_array(ctx, "glSecondaryColorPointer",
-                &ctx->Array.ArrayObj->SecondaryColor, _NEW_ARRAY_COLOR1,
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array(ctx, "glSecondaryColorPointer", VERT_ATTRIB_COLOR1,
                 legalTypes, 3, BGRA_OR_4,
-                size, type, stride, GL_TRUE, GL_FALSE, ptr);
+                size, type, stride, GL_TRUE, GL_FALSE, GL_FALSE, ptr);
 }
 
 
@@ -335,25 +609,21 @@ void GLAPIENTRY
 _mesa_TexCoordPointer(GLint size, GLenum type, GLsizei stride,
                       const GLvoid *ptr)
 {
-   GLbitfield legalTypes = (SHORT_BIT | INT_BIT |
-                            HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
-                            FIXED_ES_BIT |
-                            UNSIGNED_INT_2_10_10_10_REV_BIT |
-                            INT_2_10_10_10_REV_BIT);
    GET_CURRENT_CONTEXT(ctx);
+   GLbitfield legalTypes = (ctx->API == API_OPENGLES)
+      ? (BYTE_BIT | SHORT_BIT | FLOAT_BIT | FIXED_ES_BIT)
+      : (SHORT_BIT | INT_BIT |
+         HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
+         UNSIGNED_INT_2_10_10_10_REV_BIT |
+         INT_2_10_10_10_REV_BIT);
+   const GLint sizeMin = (ctx->API == API_OPENGLES) ? 2 : 1;
    const GLuint unit = ctx->Array.ActiveTexture;
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   if (ctx->API == API_OPENGLES)
-      legalTypes |= BYTE_BIT;
+   FLUSH_VERTICES(ctx, 0);
 
-   ASSERT(unit < Elements(ctx->Array.ArrayObj->TexCoord));
-
-   update_array(ctx, "glTexCoordPointer",
-                &ctx->Array.ArrayObj->TexCoord[unit],
-                _NEW_ARRAY_TEXCOORD(unit),
-                legalTypes, 1, 4,
-                size, type, stride, GL_FALSE, GL_FALSE,
+   update_array(ctx, "glTexCoordPointer", VERT_ATTRIB_TEX(unit),
+                legalTypes, sizeMin, 4,
+                size, type, stride, GL_FALSE, GL_FALSE, GL_FALSE,
                 ptr);
 }
 
@@ -362,24 +632,25 @@ void GLAPIENTRY
 _mesa_EdgeFlagPointer(GLsizei stride, const GLvoid *ptr)
 {
    const GLbitfield legalTypes = UNSIGNED_BYTE_BIT;
-   /* see table 2.4 edits in GL_EXT_gpu_shader4 spec: */
-   const GLboolean integer = GL_TRUE;
+   /* this is the same type that glEdgeFlag uses */
+   const GLboolean integer = GL_FALSE;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   update_array(ctx, "glEdgeFlagPointer",
-                &ctx->Array.ArrayObj->EdgeFlag, _NEW_ARRAY_EDGEFLAG,
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array(ctx, "glEdgeFlagPointer", VERT_ATTRIB_EDGEFLAG,
                 legalTypes, 1, 1,
-                1, GL_UNSIGNED_BYTE, stride, GL_FALSE, integer, ptr);
+                1, GL_UNSIGNED_BYTE, stride, GL_FALSE, integer, GL_FALSE, ptr);
 }
 
 
 void GLAPIENTRY
-_mesa_PointSizePointer(GLenum type, GLsizei stride, const GLvoid *ptr)
+_mesa_PointSizePointerOES(GLenum type, GLsizei stride, const GLvoid *ptr)
 {
    const GLbitfield legalTypes = (FLOAT_BIT | FIXED_ES_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   FLUSH_VERTICES(ctx, 0);
 
    if (ctx->API != API_OPENGLES) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
@@ -387,57 +658,19 @@ _mesa_PointSizePointer(GLenum type, GLsizei stride, const GLvoid *ptr)
       return;
    }
       
-   update_array(ctx, "glPointSizePointer",
-                &ctx->Array.ArrayObj->PointSize, _NEW_ARRAY_POINT_SIZE,
+   update_array(ctx, "glPointSizePointer", VERT_ATTRIB_POINT_SIZE,
                 legalTypes, 1, 1,
-                1, type, stride, GL_FALSE, GL_FALSE, ptr);
-}
-
-
-#if FEATURE_NV_vertex_program
-/**
- * Set a vertex attribute array.
- * Note that these arrays DO alias the conventional GL vertex arrays
- * (position, normal, color, fog, texcoord, etc).
- * The generic attribute slots at #16 and above are not touched.
- */
-void GLAPIENTRY
-_mesa_VertexAttribPointerNV(GLuint index, GLint size, GLenum type,
-                            GLsizei stride, const GLvoid *ptr)
-{
-   const GLbitfield legalTypes = (UNSIGNED_BYTE_BIT | SHORT_BIT |
-                                  FLOAT_BIT | DOUBLE_BIT);
-   GLboolean normalized = GL_FALSE;
-   GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
-
-   if (index >= MAX_NV_VERTEX_PROGRAM_INPUTS) {
-      _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerNV(index)");
-      return;
-   }
-
-   if (type == GL_UNSIGNED_BYTE && size != 4) {
-      _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerNV(size!=4)");
-      return;
-   }
-
-   update_array(ctx, "glVertexAttribPointerNV",
-                &ctx->Array.ArrayObj->VertexAttrib[index],
-                _NEW_ARRAY_ATTRIB(index),
-                legalTypes, 1, BGRA_OR_4,
-                size, type, stride, normalized, GL_FALSE, ptr);
+                1, type, stride, GL_FALSE, GL_FALSE, GL_FALSE, ptr);
 }
-#endif
 
 
-#if FEATURE_ARB_vertex_program
 /**
  * Set a generic vertex attribute array.
  * Note that these arrays DO NOT alias the conventional GL vertex arrays
  * (position, normal, color, fog, texcoord, etc).
  */
 void GLAPIENTRY
-_mesa_VertexAttribPointerARB(GLuint index, GLint size, GLenum type,
+_mesa_VertexAttribPointer(GLuint index, GLint size, GLenum type,
                              GLboolean normalized,
                              GLsizei stride, const GLvoid *ptr)
 {
@@ -447,22 +680,19 @@ _mesa_VertexAttribPointerARB(GLuint index, GLint size, GLenum type,
                                   HALF_BIT | FLOAT_BIT | DOUBLE_BIT |
                                   FIXED_ES_BIT | FIXED_GL_BIT |
                                   UNSIGNED_INT_2_10_10_10_REV_BIT |
-                                  INT_2_10_10_10_REV_BIT);
+                                  INT_2_10_10_10_REV_BIT |
+                                  UNSIGNED_INT_10F_11F_11F_REV_BIT);
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerARB(index)");
       return;
    }
 
-   update_array(ctx, "glVertexAttribPointer",
-                &ctx->Array.ArrayObj->VertexAttrib[index],
-                _NEW_ARRAY_ATTRIB(index),
+   update_array(ctx, "glVertexAttribPointer", VERT_ATTRIB_GENERIC(index),
                 legalTypes, 1, BGRA_OR_4,
-                size, type, stride, normalized, GL_FALSE, ptr);
+                size, type, stride, normalized, GL_FALSE, GL_FALSE, ptr);
 }
-#endif
 
 
 /**
@@ -481,61 +711,137 @@ _mesa_VertexAttribIPointer(GLuint index, GLint size, GLenum type,
    const GLboolean normalized = GL_FALSE;
    const GLboolean integer = GL_TRUE;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribIPointer(index)");
       return;
    }
 
-   update_array(ctx, "glVertexAttribIPointer",
-                &ctx->Array.ArrayObj->VertexAttrib[index],
-                _NEW_ARRAY_ATTRIB(index),
+   update_array(ctx, "glVertexAttribIPointer", VERT_ATTRIB_GENERIC(index),
                 legalTypes, 1, 4,
-                size, type, stride, normalized, integer, ptr);
+                size, type, stride, normalized, integer, GL_FALSE, ptr);
 }
 
-
-
 void GLAPIENTRY
-_mesa_EnableVertexAttribArrayARB(GLuint index)
+_mesa_VertexAttribLPointer(GLuint index, GLint size, GLenum type,
+                           GLsizei stride, const GLvoid *ptr)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
+   const GLbitfield legalTypes = (DOUBLE_BIT);
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribLPointer(index)");
+      return;
+   }
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glEnableVertexAttribArrayARB(index)");
+   update_array(ctx, "glVertexAttribLPointer", VERT_ATTRIB_GENERIC(index),
+                legalTypes, 1, 4,
+                size, type, stride, GL_TRUE, GL_FALSE, GL_TRUE, ptr);
+}
+
+
+static void
+enable_vertex_array_attrib(struct gl_context *ctx,
+                           struct gl_vertex_array_object *vao,
+                           GLuint index,
+                           const char *func)
+{
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index)", func);
       return;
    }
 
-   ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib));
+   assert(VERT_ATTRIB_GENERIC(index) < ARRAY_SIZE(vao->VertexAttrib));
 
-   FLUSH_VERTICES(ctx, _NEW_ARRAY);
-   ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_TRUE;
-   ctx->Array.ArrayObj->_Enabled |= _NEW_ARRAY_ATTRIB(index);
-   ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index);
+   if (!vao->VertexAttrib[VERT_ATTRIB_GENERIC(index)].Enabled) {
+      /* was disabled, now being enabled */
+      FLUSH_VERTICES(ctx, _NEW_ARRAY);
+      vao->VertexAttrib[VERT_ATTRIB_GENERIC(index)].Enabled = GL_TRUE;
+      vao->_Enabled |= VERT_BIT_GENERIC(index);
+      vao->NewArrays |= VERT_BIT_GENERIC(index);
+   }
 }
 
 
 void GLAPIENTRY
-_mesa_DisableVertexAttribArrayARB(GLuint index)
+_mesa_EnableVertexAttribArray(GLuint index)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
+   enable_vertex_array_attrib(ctx, ctx->Array.VAO, index,
+                              "glEnableVertexAttribArray");
+}
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glDisableVertexAttribArrayARB(index)");
+
+void GLAPIENTRY
+_mesa_EnableVertexArrayAttrib(GLuint vaobj, GLuint index)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_vertex_array_object *vao;
+
+   /* The ARB_direct_state_access specification says:
+    *
+    *   "An INVALID_OPERATION error is generated by EnableVertexArrayAttrib
+    *    and DisableVertexArrayAttrib if <vaobj> is not
+    *    [compatibility profile: zero or] the name of an existing vertex
+    *    array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, "glEnableVertexArrayAttrib");
+   if (!vao)
+      return;
+
+   enable_vertex_array_attrib(ctx, vao, index, "glEnableVertexArrayAttrib");
+}
+
+
+static void
+disable_vertex_array_attrib(struct gl_context *ctx,
+                            struct gl_vertex_array_object *vao,
+                            GLuint index,
+                            const char *func)
+{
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index)", func);
       return;
    }
 
-   ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib));
+   assert(VERT_ATTRIB_GENERIC(index) < ARRAY_SIZE(vao->VertexAttrib));
+
+   if (vao->VertexAttrib[VERT_ATTRIB_GENERIC(index)].Enabled) {
+      /* was enabled, now being disabled */
+      FLUSH_VERTICES(ctx, _NEW_ARRAY);
+      vao->VertexAttrib[VERT_ATTRIB_GENERIC(index)].Enabled = GL_FALSE;
+      vao->_Enabled &= ~VERT_BIT_GENERIC(index);
+      vao->NewArrays |= VERT_BIT_GENERIC(index);
+   }
+}
+
+
+void GLAPIENTRY
+_mesa_DisableVertexAttribArray(GLuint index)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   disable_vertex_array_attrib(ctx, ctx->Array.VAO, index,
+                               "glDisableVertexAttribArray");
+}
+
+
+void GLAPIENTRY
+_mesa_DisableVertexArrayAttrib(GLuint vaobj, GLuint index)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_vertex_array_object *vao;
+
+   /* The ARB_direct_state_access specification says:
+    *
+    *   "An INVALID_OPERATION error is generated by EnableVertexArrayAttrib
+    *    and DisableVertexArrayAttrib if <vaobj> is not
+    *    [compatibility profile: zero or] the name of an existing vertex
+    *    array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, "glDisableVertexArrayAttrib");
+   if (!vao)
+      return;
 
-   FLUSH_VERTICES(ctx, _NEW_ARRAY);
-   ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_FALSE;
-   ctx->Array.ArrayObj->_Enabled &= ~_NEW_ARRAY_ATTRIB(index);
-   ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index);
+   disable_vertex_array_attrib(ctx, vao, index, "glDisableVertexArrayAttrib");
 }
 
 
@@ -545,25 +851,27 @@ _mesa_DisableVertexAttribArrayARB(GLuint index)
  * not handle the 4-element GL_CURRENT_VERTEX_ATTRIB_ARB query.
  */
 static GLuint
-get_vertex_array_attrib(struct gl_context *ctx, GLuint index, GLenum pname,
-                  const char *caller)
+get_vertex_array_attrib(struct gl_context *ctx,
+                        const struct gl_vertex_array_object *vao,
+                        GLuint index, GLenum pname,
+                        const char *caller)
 {
-   const struct gl_client_array *array;
+   const struct gl_vertex_attrib_array *array;
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
       _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%u)", caller, index);
       return 0;
    }
 
-   ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib));
+   assert(VERT_ATTRIB_GENERIC(index) < ARRAY_SIZE(vao->VertexAttrib));
 
-   array = &ctx->Array.ArrayObj->VertexAttrib[index];
+   array = &vao->VertexAttrib[VERT_ATTRIB_GENERIC(index)];
 
    switch (pname) {
    case GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB:
       return array->Enabled;
    case GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB:
-      return array->Size;
+      return (array->Format == GL_BGRA) ? GL_BGRA : array->Size;
    case GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB:
       return array->Stride;
    case GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB:
@@ -571,15 +879,28 @@ get_vertex_array_attrib(struct gl_context *ctx, GLuint index, GLenum pname,
    case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB:
       return array->Normalized;
    case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB:
-      return array->BufferObj->Name;
+      return vao->VertexBinding[array->VertexBinding].BufferObj->Name;
    case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
-      if (ctx->Extensions.EXT_gpu_shader4) {
+      if ((_mesa_is_desktop_gl(ctx)
+           && (ctx->Version >= 30 || ctx->Extensions.EXT_gpu_shader4))
+          || _mesa_is_gles3(ctx)) {
          return array->Integer;
       }
       goto error;
    case GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB:
-      if (ctx->Extensions.ARB_instanced_arrays) {
-         return array->InstanceDivisor;
+      if ((_mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_instanced_arrays)
+          || _mesa_is_gles3(ctx)) {
+         return vao->VertexBinding[array->VertexBinding].InstanceDivisor;
+      }
+      goto error;
+   case GL_VERTEX_ATTRIB_BINDING:
+      if (_mesa_is_desktop_gl(ctx)) {
+         return array->VertexBinding - VERT_ATTRIB_GENERIC0;
+      }
+      goto error;
+   case GL_VERTEX_ATTRIB_RELATIVE_OFFSET:
+      if (_mesa_is_desktop_gl(ctx)) {
+         return array->RelativeOffset;
       }
       goto error;
    default:
@@ -596,26 +917,27 @@ static const GLfloat *
 get_current_attrib(struct gl_context *ctx, GLuint index, const char *function)
 {
    if (index == 0) {
-      if (ctx->API != API_OPENGLES2) {
+      if (_mesa_attr_zero_aliases_vertex(ctx)) {
         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(index==0)", function);
         return NULL;
       }
    }
-   else if (index >= ctx->Const.VertexProgram.MaxAttribs) {
+   else if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                  "%s(index>=GL_MAX_VERTEX_ATTRIBS)", function);
       return NULL;
    }
 
+   assert(VERT_ATTRIB_GENERIC(index) < ARRAY_SIZE(ctx->Array.VAO->VertexAttrib));
+
    FLUSH_CURRENT(ctx, 0);
-   return ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index];
+   return ctx->Current.Attrib[VERT_ATTRIB_GENERIC(index)];
 }
 
 void GLAPIENTRY
-_mesa_GetVertexAttribfvARB(GLuint index, GLenum pname, GLfloat *params)
+_mesa_GetVertexAttribfv(GLuint index, GLenum pname, GLfloat *params)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) {
       const GLfloat *v = get_current_attrib(ctx, index, "glGetVertexAttribfv");
@@ -624,17 +946,17 @@ _mesa_GetVertexAttribfvARB(GLuint index, GLenum pname, GLfloat *params)
       }
    }
    else {
-      params[0] = (GLfloat) get_vertex_array_attrib(ctx, index, pname,
+      params[0] = (GLfloat) get_vertex_array_attrib(ctx, ctx->Array.VAO,
+                                                    index, pname,
                                                     "glGetVertexAttribfv");
    }
 }
 
 
 void GLAPIENTRY
-_mesa_GetVertexAttribdvARB(GLuint index, GLenum pname, GLdouble *params)
+_mesa_GetVertexAttribdv(GLuint index, GLenum pname, GLdouble *params)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) {
       const GLfloat *v = get_current_attrib(ctx, index, "glGetVertexAttribdv");
@@ -646,17 +968,37 @@ _mesa_GetVertexAttribdvARB(GLuint index, GLenum pname, GLdouble *params)
       }
    }
    else {
-      params[0] = (GLdouble) get_vertex_array_attrib(ctx, index, pname,
+      params[0] = (GLdouble) get_vertex_array_attrib(ctx, ctx->Array.VAO,
+                                                     index, pname,
                                                      "glGetVertexAttribdv");
    }
 }
 
+void GLAPIENTRY
+_mesa_GetVertexAttribLdv(GLuint index, GLenum pname, GLdouble *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) {
+      const GLdouble *v = (const GLdouble *)get_current_attrib(ctx, index, "glGetVertexAttribLdv");
+      if (v != NULL) {
+         params[0] = v[0];
+         params[1] = v[1];
+         params[2] = v[2];
+         params[3] = v[3];
+      }
+   }
+   else {
+      params[0] = (GLdouble) get_vertex_array_attrib(ctx, ctx->Array.VAO,
+                                                     index, pname,
+                                                     "glGetVertexAttribLdv");
+   }
+}
 
 void GLAPIENTRY
-_mesa_GetVertexAttribivARB(GLuint index, GLenum pname, GLint *params)
+_mesa_GetVertexAttribiv(GLuint index, GLenum pname, GLint *params)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) {
       const GLfloat *v = get_current_attrib(ctx, index, "glGetVertexAttribiv");
@@ -669,7 +1011,8 @@ _mesa_GetVertexAttribivARB(GLuint index, GLenum pname, GLint *params)
       }
    }
    else {
-      params[0] = (GLint) get_vertex_array_attrib(ctx, index, pname,
+      params[0] = (GLint) get_vertex_array_attrib(ctx, ctx->Array.VAO,
+                                                  index, pname,
                                                   "glGetVertexAttribiv");
    }
 }
@@ -680,21 +1023,17 @@ void GLAPIENTRY
 _mesa_GetVertexAttribIiv(GLuint index, GLenum pname, GLint *params)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) {
-      const GLfloat *v =
+      const GLint *v = (const GLint *)
         get_current_attrib(ctx, index, "glGetVertexAttribIiv");
       if (v != NULL) {
-         /* XXX we don't have true integer-valued vertex attribs yet */
-         params[0] = (GLint) v[0];
-         params[1] = (GLint) v[1];
-         params[2] = (GLint) v[2];
-         params[3] = (GLint) v[3];
+         COPY_4V(params, v);
       }
    }
    else {
-      params[0] = (GLint) get_vertex_array_attrib(ctx, index, pname,
+      params[0] = (GLint) get_vertex_array_attrib(ctx, ctx->Array.VAO,
+                                                  index, pname,
                                                   "glGetVertexAttribIiv");
    }
 }
@@ -705,33 +1044,28 @@ void GLAPIENTRY
 _mesa_GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint *params)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) {
-      const GLfloat *v =
+      const GLuint *v = (const GLuint *)
         get_current_attrib(ctx, index, "glGetVertexAttribIuiv");
       if (v != NULL) {
-         /* XXX we don't have true integer-valued vertex attribs yet */
-         params[0] = (GLuint) v[0];
-         params[1] = (GLuint) v[1];
-         params[2] = (GLuint) v[2];
-         params[3] = (GLuint) v[3];
+         COPY_4V(params, v);
       }
    }
    else {
-      params[0] = get_vertex_array_attrib(ctx, index, pname,
+      params[0] = get_vertex_array_attrib(ctx, ctx->Array.VAO,
+                                          index, pname,
                                           "glGetVertexAttribIuiv");
    }
 }
 
 
 void GLAPIENTRY
-_mesa_GetVertexAttribPointervARB(GLuint index, GLenum pname, GLvoid **pointer)
+_mesa_GetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid **pointer)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribPointerARB(index)");
       return;
    }
@@ -741,9 +1075,9 @@ _mesa_GetVertexAttribPointervARB(GLuint index, GLenum pname, GLvoid **pointer)
       return;
    }
 
-   ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib));
+   assert(VERT_ATTRIB_GENERIC(index) < ARRAY_SIZE(ctx->Array.VAO->VertexAttrib));
 
-   *pointer = (GLvoid *) ctx->Array.ArrayObj->VertexAttrib[index].Ptr;
+   *pointer = (GLvoid *) ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_GENERIC(index)].Ptr;
 }
 
 
@@ -812,7 +1146,7 @@ _mesa_InterleavedArrays(GLenum format, GLsizei stride, const GLvoid *pointer)
    GLint defstride;                /* default stride */
    GLint c, f;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    f = sizeof(GLfloat);
    c = f * ((4 * sizeof(GLubyte) + (f - 1)) / f);
@@ -982,7 +1316,8 @@ void GLAPIENTRY
 _mesa_LockArraysEXT(GLint first, GLsizei count)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   FLUSH_VERTICES(ctx, 0);
 
    if (MESA_VERBOSE & VERBOSE_API)
       _mesa_debug(ctx, "glLockArrays %d %d\n", first, count);
@@ -1004,7 +1339,6 @@ _mesa_LockArraysEXT(GLint first, GLsizei count)
    ctx->Array.LockCount = count;
 
    ctx->NewState |= _NEW_ARRAY;
-   ctx->Array.NewState |= _NEW_ARRAY_ALL;
 }
 
 
@@ -1012,7 +1346,8 @@ void GLAPIENTRY
 _mesa_UnlockArraysEXT( void )
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   FLUSH_VERTICES(ctx, 0);
 
    if (MESA_VERBOSE & VERBOSE_API)
       _mesa_debug(ctx, "glUnlockArrays\n");
@@ -1025,23 +1360,22 @@ _mesa_UnlockArraysEXT( void )
    ctx->Array.LockFirst = 0;
    ctx->Array.LockCount = 0;
    ctx->NewState |= _NEW_ARRAY;
-   ctx->Array.NewState |= _NEW_ARRAY_ALL;
 }
 
 
 /* GL_EXT_multi_draw_arrays */
 void GLAPIENTRY
-_mesa_MultiDrawArraysEXT( GLenum mode, const GLint *first,
+_mesa_MultiDrawArrays( GLenum mode, const GLint *first,
                           const GLsizei *count, GLsizei primcount )
 {
    GET_CURRENT_CONTEXT(ctx);
    GLint i;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    for (i = 0; i < primcount; i++) {
       if (count[i] > 0) {
-         CALL_DrawArrays(ctx->Exec, (mode, first[i], count[i]));
+         CALL_DrawArrays(ctx->CurrentDispatch, (mode, first[i], count[i]));
       }
    }
 }
@@ -1056,12 +1390,12 @@ _mesa_MultiModeDrawArraysIBM( const GLenum * mode, const GLint * first,
    GET_CURRENT_CONTEXT(ctx);
    GLint i;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    for ( i = 0 ; i < primcount ; i++ ) {
       if ( count[i] > 0 ) {
          GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride));
-        CALL_DrawArrays(ctx->Exec, ( m, first[i], count[i] ));
+        CALL_DrawArrays(ctx->CurrentDispatch, ( m, first[i], count[i] ));
       }
    }
 }
@@ -1076,14 +1410,15 @@ _mesa_MultiModeDrawElementsIBM( const GLenum * mode, const GLsizei * count,
    GET_CURRENT_CONTEXT(ctx);
    GLint i;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    /* XXX not sure about ARB_vertex_buffer_object handling here */
 
    for ( i = 0 ; i < primcount ; i++ ) {
       if ( count[i] > 0 ) {
          GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride));
-        CALL_DrawElements(ctx->Exec, ( m, count[i], type, indices[i] ));
+        CALL_DrawElements(ctx->CurrentDispatch, ( m, count[i], type,
+                                                   indices[i] ));
       }
    }
 }
@@ -1097,17 +1432,15 @@ _mesa_PrimitiveRestartIndex(GLuint index)
 {
    GET_CURRENT_CONTEXT(ctx);
 
-   if (!ctx->Extensions.NV_primitive_restart &&
-       ctx->VersionMajor * 10 + ctx->VersionMinor < 31) {
+   if (!ctx->Extensions.NV_primitive_restart && ctx->Version < 31) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glPrimitiveRestartIndexNV()");
       return;
    }
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   FLUSH_VERTICES(ctx, _NEW_TRANSFORM);
-
-   ctx->Array.RestartIndex = index;
+   if (ctx->Array.RestartIndex != index) {
+      FLUSH_VERTICES(ctx, _NEW_TRANSFORM);
+      ctx->Array.RestartIndex = index;
+   }
 }
 
 
@@ -1120,23 +1453,674 @@ void GLAPIENTRY
 _mesa_VertexAttribDivisor(GLuint index, GLuint divisor)
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   const GLuint genericIndex = VERT_ATTRIB_GENERIC(index);
+   struct gl_vertex_array_object * const vao = ctx->Array.VAO;
 
    if (!ctx->Extensions.ARB_instanced_arrays) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glVertexAttribDivisor()");
       return;
    }
 
-   if (index >= ctx->Const.VertexProgram.MaxAttribs) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glVertexAttribDivisor(index = %u)",
+   if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribDivisor(index = %u)",
                   index);
       return;
    }
 
-   ctx->Array.ArrayObj->VertexAttrib[index].InstanceDivisor = divisor;
+   assert(genericIndex < ARRAY_SIZE(vao->VertexAttrib));
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "The command
+    *
+    *       void VertexAttribDivisor(uint index, uint divisor);
+    *
+    *     is equivalent to (assuming no errors are generated):
+    *
+    *       VertexAttribBinding(index, index);
+    *       VertexBindingDivisor(index, divisor);"
+    */
+   vertex_attrib_binding(ctx, vao, genericIndex, genericIndex);
+   vertex_binding_divisor(ctx, vao, genericIndex, divisor);
+}
+
+
+unsigned
+_mesa_primitive_restart_index(const struct gl_context *ctx, GLenum ib_type)
+{
+   /* From the OpenGL 4.3 core specification, page 302:
+    * "If both PRIMITIVE_RESTART and PRIMITIVE_RESTART_FIXED_INDEX are
+    *  enabled, the index value determined by PRIMITIVE_RESTART_FIXED_INDEX
+    *  is used."
+    */
+   if (ctx->Array.PrimitiveRestartFixedIndex) {
+      switch (ib_type) {
+      case GL_UNSIGNED_BYTE:
+         return 0xff;
+      case GL_UNSIGNED_SHORT:
+         return 0xffff;
+      case GL_UNSIGNED_INT:
+         return 0xffffffff;
+      default:
+         assert(!"_mesa_primitive_restart_index: Invalid index buffer type.");
+      }
+   }
+
+   return ctx->Array.RestartIndex;
+}
+
+
+/**
+ * GL_ARB_vertex_attrib_binding
+ */
+static void
+vertex_array_vertex_buffer(struct gl_context *ctx, struct gl_vertex_array_object *vao,
+                           GLuint bindingIndex, GLuint buffer, GLintptr offset,
+                           GLsizei stride, const char *func)
+{
+   struct gl_buffer_object *vbo;
+
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_VALUE error is generated if <bindingindex> is greater than
+    *     the value of MAX_VERTEX_ATTRIB_BINDINGS."
+    */
+   if (bindingIndex >= ctx->Const.MaxVertexAttribBindings) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(bindingindex=%u > "
+                  "GL_MAX_VERTEX_ATTRIB_BINDINGS)",
+                  func, bindingIndex);
+      return;
+   }
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "The error INVALID_VALUE is generated if <stride> or <offset>
+    *     are negative."
+    */
+   if (offset < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(offset=%" PRId64 " < 0)",
+                  func, (int64_t) offset);
+      return;
+   }
+
+   if (stride < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(stride=%d < 0)", func, stride);
+      return;
+   }
+
+   if (ctx->API == API_OPENGL_CORE && ctx->Version >= 44 &&
+       stride > ctx->Const.MaxVertexAttribStride) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(stride=%d > "
+                  "GL_MAX_VERTEX_ATTRIB_STRIDE)", func, stride);
+      return;
+   }
+
+   if (buffer == vao->VertexBinding[VERT_ATTRIB_GENERIC(bindingIndex)].BufferObj->Name) {
+      vbo = vao->VertexBinding[VERT_ATTRIB_GENERIC(bindingIndex)].BufferObj;
+   } else if (buffer != 0) {
+      vbo = _mesa_lookup_bufferobj(ctx, buffer);
+
+      /* From the GL_ARB_vertex_attrib_array spec:
+       *
+       *   "[Core profile only:]
+       *    An INVALID_OPERATION error is generated if buffer is not zero or a
+       *    name returned from a previous call to GenBuffers, or if such a name
+       *    has since been deleted with DeleteBuffers.
+       *
+       * Otherwise, we fall back to the same compat profile behavior as other
+       * object references (automatically gen it).
+       */
+      if (!_mesa_handle_bind_buffer_gen(ctx, GL_ARRAY_BUFFER, buffer,
+                                        &vbo, func))
+         return;
+   } else {
+      /* The ARB_vertex_attrib_binding spec says:
+       *
+       *    "If <buffer> is zero, any buffer object attached to this
+       *     bindpoint is detached."
+       */
+      vbo = ctx->Shared->NullBufferObj;
+   }
+
+   bind_vertex_buffer(ctx, vao, VERT_ATTRIB_GENERIC(bindingIndex),
+                      vbo, offset, stride);
+}
+
+
+void GLAPIENTRY
+_mesa_BindVertexBuffer(GLuint bindingIndex, GLuint buffer, GLintptr offset,
+                       GLsizei stride)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_OPERATION error is generated if no vertex array object
+    *     is bound."
+    */
+   if (ctx->API == API_OPENGL_CORE &&
+       ctx->Array.VAO == ctx->Array.DefaultVAO) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glBindVertexBuffer(No array object bound)");
+      return;
+   }
+
+   vertex_array_vertex_buffer(ctx, ctx->Array.VAO, bindingIndex,
+                              buffer, offset, stride, "glBindVertexBuffer");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexArrayVertexBuffer(GLuint vaobj, GLuint bindingIndex, GLuint buffer,
+                              GLintptr offset, GLsizei stride)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_vertex_array_object *vao;
+
+   /* The ARB_direct_state_access specification says:
+    *
+    *   "An INVALID_OPERATION error is generated by VertexArrayVertexBuffer
+    *    if <vaobj> is not [compatibility profile: zero or] the name of an
+    *    existing vertex array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, "glVertexArrayVertexBuffer");
+   if (!vao)
+      return;
+
+   vertex_array_vertex_buffer(ctx, vao, bindingIndex,
+                              buffer, offset, stride,
+                              "glVertexArrayVertexBuffer");
+}
+
+
+static void
+vertex_array_vertex_buffers(struct gl_context *ctx,
+                            struct gl_vertex_array_object *vao,
+                            GLuint first, GLsizei count, const GLuint *buffers,
+                            const GLintptr *offsets, const GLsizei *strides,
+                            const char *func)
+{
+   GLuint i;
+
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   /* The ARB_multi_bind spec says:
+    *
+    *    "An INVALID_OPERATION error is generated if <first> + <count>
+    *     is greater than the value of MAX_VERTEX_ATTRIB_BINDINGS."
+    */
+   if (first + count > ctx->Const.MaxVertexAttribBindings) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(first=%u + count=%d > the value of "
+                  "GL_MAX_VERTEX_ATTRIB_BINDINGS=%u)",
+                  func, first, count, ctx->Const.MaxVertexAttribBindings);
+      return;
+   }
+
+   if (!buffers) {
+      /**
+       * The ARB_multi_bind spec says:
+       *
+       *    "If <buffers> is NULL, each affected vertex buffer binding point
+       *     from <first> through <first>+<count>-1 will be reset to have no
+       *     bound buffer object.  In this case, the offsets and strides
+       *     associated with the binding points are set to default values,
+       *     ignoring <offsets> and <strides>."
+       */
+      struct gl_buffer_object *vbo = ctx->Shared->NullBufferObj;
+
+      for (i = 0; i < count; i++)
+         bind_vertex_buffer(ctx, vao, VERT_ATTRIB_GENERIC(first + i),
+                            vbo, 0, 16);
+
+      return;
+   }
+
+   /* Note that the error semantics for multi-bind commands differ from
+    * those of other GL commands.
+    *
+    * The Issues section in the ARB_multi_bind spec says:
+    *
+    *    "(11) Typically, OpenGL specifies that if an error is generated by
+    *          a command, that command has no effect.  This is somewhat
+    *          unfortunate for multi-bind commands, because it would require
+    *          a first pass to scan the entire list of bound objects for
+    *          errors and then a second pass to actually perform the
+    *          bindings.  Should we have different error semantics?
+    *
+    *       RESOLVED:  Yes.  In this specification, when the parameters for
+    *       one of the <count> binding points are invalid, that binding
+    *       point is not updated and an error will be generated.  However,
+    *       other binding points in the same command will be updated if
+    *       their parameters are valid and no other error occurs."
+    */
+
+   _mesa_begin_bufferobj_lookups(ctx);
+
+   for (i = 0; i < count; i++) {
+      struct gl_buffer_object *vbo;
+
+      /* The ARB_multi_bind spec says:
+       *
+       *    "An INVALID_VALUE error is generated if any value in
+       *     <offsets> or <strides> is negative (per binding)."
+       */
+      if (offsets[i] < 0) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "%s(offsets[%u]=%" PRId64 " < 0)",
+                     func, i, (int64_t) offsets[i]);
+         continue;
+      }
+
+      if (strides[i] < 0) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "%s(strides[%u]=%d < 0)",
+                     func, i, strides[i]);
+         continue;
+      }
+
+      if (ctx->API == API_OPENGL_CORE && ctx->Version >= 44 &&
+          strides[i] > ctx->Const.MaxVertexAttribStride) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "%s(strides[%u]=%d > "
+                     "GL_MAX_VERTEX_ATTRIB_STRIDE)", func, i, strides[i]);
+         continue;
+      }
+
+      if (buffers[i]) {
+         struct gl_vertex_buffer_binding *binding =
+            &vao->VertexBinding[VERT_ATTRIB_GENERIC(first + i)];
+
+         if (buffers[i] == binding->BufferObj->Name)
+            vbo = binding->BufferObj;
+         else
+            vbo = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, func);
+
+         if (!vbo)
+            continue;
+      } else {
+         vbo = ctx->Shared->NullBufferObj;
+      }
+
+      bind_vertex_buffer(ctx, vao, VERT_ATTRIB_GENERIC(first + i),
+                         vbo, offsets[i], strides[i]);
+   }
+
+   _mesa_end_bufferobj_lookups(ctx);
+}
+
+
+void GLAPIENTRY
+_mesa_BindVertexBuffers(GLuint first, GLsizei count, const GLuint *buffers,
+                        const GLintptr *offsets, const GLsizei *strides)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_OPERATION error is generated if no
+    *     vertex array object is bound."
+    */
+   if (ctx->API == API_OPENGL_CORE &&
+       ctx->Array.VAO == ctx->Array.DefaultVAO) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glBindVertexBuffers(No array object bound)");
+      return;
+   }
+
+   vertex_array_vertex_buffers(ctx, ctx->Array.VAO, first, count,
+                               buffers, offsets, strides,
+                               "glBindVertexBuffers");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexArrayVertexBuffers(GLuint vaobj, GLuint first, GLsizei count,
+                               const GLuint *buffers,
+                               const GLintptr *offsets, const GLsizei *strides)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_vertex_array_object *vao;
+
+   /* The ARB_direct_state_access specification says:
+    *
+    *   "An INVALID_OPERATION error is generated by VertexArrayVertexBuffer
+    *    if <vaobj> is not [compatibility profile: zero or] the name of an
+    *    existing vertex array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, "glVertexArrayVertexBuffers");
+   if (!vao)
+      return;
+
+   vertex_array_vertex_buffers(ctx, vao, first, count,
+                               buffers, offsets, strides,
+                               "glVertexArrayVertexBuffers");
+}
+
+
+static void
+vertex_attrib_format(GLuint attribIndex, GLint size, GLenum type,
+                     GLboolean normalized, GLboolean integer,
+                     GLboolean doubles, GLbitfield legalTypes,
+                     GLsizei maxSize, GLuint relativeOffset,
+                     const char *func)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_OPERATION error is generated under any of the following
+    *     conditions:
+    *     - if no vertex array object is currently bound (see section 2.10);
+    *     - ..."
+    *
+    * This error condition only applies to VertexAttribFormat and
+    * VertexAttribIFormat in the extension spec, but we assume that this
+    * is an oversight.  In the OpenGL 4.3 (Core Profile) spec, it applies
+    * to all three functions.
+    */
+   if (ctx->API == API_OPENGL_CORE &&
+       ctx->Array.VAO == ctx->Array.DefaultVAO) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(No array object bound)", func);
+      return;
+   }
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *   "The error INVALID_VALUE is generated if index is greater than or equal
+    *     to the value of MAX_VERTEX_ATTRIBS."
+    */
+   if (attribIndex >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(attribindex=%u > "
+                  "GL_MAX_VERTEX_ATTRIBS)",
+                  func, attribIndex);
+      return;
+   }
+
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array_format(ctx, func, ctx->Array.VAO,
+                       VERT_ATTRIB_GENERIC(attribIndex),
+                       legalTypes, 1, maxSize, size, type,
+                       normalized, integer, doubles, relativeOffset);
+}
+
+
+void GLAPIENTRY
+_mesa_VertexAttribFormat(GLuint attribIndex, GLint size, GLenum type,
+                         GLboolean normalized, GLuint relativeOffset)
+{
+   vertex_attrib_format(attribIndex, size, type, normalized,
+                        GL_FALSE, GL_FALSE, ATTRIB_FORMAT_TYPES_MASK,
+                        BGRA_OR_4, relativeOffset,
+                        "glVertexAttribFormat");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexAttribIFormat(GLuint attribIndex, GLint size, GLenum type,
+                          GLuint relativeOffset)
+{
+   vertex_attrib_format(attribIndex, size, type, GL_FALSE,
+                        GL_TRUE, GL_FALSE, ATTRIB_IFORMAT_TYPES_MASK, 4,
+                        relativeOffset, "glVertexAttribIFormat");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexAttribLFormat(GLuint attribIndex, GLint size, GLenum type,
+                          GLuint relativeOffset)
+{
+   vertex_attrib_format(attribIndex, size, type, GL_FALSE, GL_FALSE,
+                        GL_TRUE, ATTRIB_LFORMAT_TYPES_MASK, 4,
+                        relativeOffset, "glVertexAttribLFormat");
+}
+
+
+static void
+vertex_array_attrib_format(GLuint vaobj, GLuint attribIndex, GLint size,
+                           GLenum type, GLboolean normalized,
+                           GLboolean integer, GLboolean doubles,
+                           GLbitfield legalTypes, GLsizei maxSize,
+                           GLuint relativeOffset, const char *func)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_vertex_array_object *vao;
+
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   /* The ARB_direct_state_access spec says:
+    *
+    *   "An INVALID_OPERATION error is generated by VertexArrayAttrib*Format
+    *    if <vaobj> is not [compatibility profile: zero or] the name of an
+    *    existing vertex array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, func);
+   if (!vao)
+      return;
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *   "The error INVALID_VALUE is generated if index is greater than or equal
+    *    to the value of MAX_VERTEX_ATTRIBS."
+    */
+   if (attribIndex >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(attribindex=%u > GL_MAX_VERTEX_ATTRIBS)",
+                  func, attribIndex);
+      return;
+   }
+
+   FLUSH_VERTICES(ctx, 0);
+
+   update_array_format(ctx, func, vao,
+                       VERT_ATTRIB_GENERIC(attribIndex),
+                       legalTypes, 1, maxSize, size, type, normalized,
+                       integer, doubles, relativeOffset);
+}
+
+
+void GLAPIENTRY
+_mesa_VertexArrayAttribFormat(GLuint vaobj, GLuint attribIndex, GLint size,
+                              GLenum type, GLboolean normalized,
+                              GLuint relativeOffset)
+{
+   vertex_array_attrib_format(vaobj, attribIndex, size, type, normalized,
+                              GL_FALSE, GL_FALSE, ATTRIB_FORMAT_TYPES_MASK,
+                              BGRA_OR_4, relativeOffset,
+                              "glVertexArrayAttribFormat");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexArrayAttribIFormat(GLuint vaobj, GLuint attribIndex,
+                               GLint size, GLenum type,
+                               GLuint relativeOffset)
+{
+   vertex_array_attrib_format(vaobj, attribIndex, size, type, GL_FALSE,
+                              GL_TRUE, GL_FALSE, ATTRIB_IFORMAT_TYPES_MASK,
+                              4, relativeOffset,
+                              "glVertexArrayAttribIFormat");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexArrayAttribLFormat(GLuint vaobj, GLuint attribIndex,
+                               GLint size, GLenum type,
+                               GLuint relativeOffset)
+{
+   vertex_array_attrib_format(vaobj, attribIndex, size, type, GL_FALSE,
+                              GL_FALSE, GL_TRUE, ATTRIB_LFORMAT_TYPES_MASK,
+                              4, relativeOffset,
+                              "glVertexArrayAttribLFormat");
+}
+
+
+static void
+vertex_array_attrib_binding(struct gl_context *ctx,
+                            struct gl_vertex_array_object *vao,
+                            GLuint attribIndex, GLuint bindingIndex,
+                            const char *func)
+{
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "<attribindex> must be less than the value of MAX_VERTEX_ATTRIBS and
+    *     <bindingindex> must be less than the value of
+    *     MAX_VERTEX_ATTRIB_BINDINGS, otherwise the error INVALID_VALUE
+    *     is generated."
+    */
+   if (attribIndex >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(attribindex=%u >= "
+                  "GL_MAX_VERTEX_ATTRIBS)",
+                  func, attribIndex);
+      return;
+   }
+
+   if (bindingIndex >= ctx->Const.MaxVertexAttribBindings) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(bindingindex=%u >= "
+                  "GL_MAX_VERTEX_ATTRIB_BINDINGS)",
+                  func, bindingIndex);
+      return;
+   }
+
+   assert(VERT_ATTRIB_GENERIC(attribIndex) < ARRAY_SIZE(vao->VertexAttrib));
+
+   vertex_attrib_binding(ctx, vao,
+                         VERT_ATTRIB_GENERIC(attribIndex),
+                         VERT_ATTRIB_GENERIC(bindingIndex));
+}
+
+
+void GLAPIENTRY
+_mesa_VertexAttribBinding(GLuint attribIndex, GLuint bindingIndex)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_OPERATION error is generated if no vertex array object
+    *     is bound."
+    */
+   if (ctx->API == API_OPENGL_CORE &&
+       ctx->Array.VAO == ctx->Array.DefaultVAO) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glVertexAttribBinding(No array object bound)");
+      return;
+   }
+
+   vertex_array_attrib_binding(ctx, ctx->Array.VAO,
+                               attribIndex, bindingIndex,
+                               "glVertexAttribBinding");
+}
+
+
+void GLAPIENTRY
+_mesa_VertexArrayAttribBinding(GLuint vaobj, GLuint attribIndex, GLuint bindingIndex)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_vertex_array_object *vao;
+
+   /* The ARB_direct_state_access specification says:
+    *
+    *   "An INVALID_OPERATION error is generated by VertexArrayAttribBinding
+    *    if <vaobj> is not [compatibility profile: zero or] the name of an
+    *    existing vertex array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, "glVertexArrayAttribBinding");
+   if (!vao)
+      return;
+
+   vertex_array_attrib_binding(ctx, vao, attribIndex, bindingIndex,
+                               "glVertexArrayAttribBinding");
+}
+
+
+static void
+vertex_array_binding_divisor(struct gl_context *ctx,
+                             struct gl_vertex_array_object *vao,
+                             GLuint bindingIndex, GLuint divisor,
+                             const char *func)
+{
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (!ctx->Extensions.ARB_instanced_arrays) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s()", func);
+      return;
+   }
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_VALUE error is generated if <bindingindex> is greater
+    *     than or equal to the value of MAX_VERTEX_ATTRIB_BINDINGS."
+    */
+   if (bindingIndex >= ctx->Const.MaxVertexAttribBindings) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(bindingindex=%u > "
+                  "GL_MAX_VERTEX_ATTRIB_BINDINGS)",
+                  func, bindingIndex);
+      return;
+   }
+
+   vertex_binding_divisor(ctx, vao, VERT_ATTRIB_GENERIC(bindingIndex), divisor);
+}
+
+
+void GLAPIENTRY
+_mesa_VertexBindingDivisor(GLuint bindingIndex, GLuint divisor)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* The ARB_vertex_attrib_binding spec says:
+    *
+    *    "An INVALID_OPERATION error is generated if no vertex array object
+    *     is bound."
+    */
+   if (ctx->API == API_OPENGL_CORE &&
+       ctx->Array.VAO == ctx->Array.DefaultVAO) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glVertexBindingDivisor(No array object bound)");
+      return;
+   }
+
+   vertex_array_binding_divisor(ctx, ctx->Array.VAO,
+                                bindingIndex, divisor,
+                                "glVertexBindingDivisor");
 }
 
 
+void GLAPIENTRY
+_mesa_VertexArrayBindingDivisor(GLuint vaobj, GLuint bindingIndex, GLuint divisor)
+{
+   struct gl_vertex_array_object *vao;
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* The ARB_direct_state_access specification says:
+    *
+    *   "An INVALID_OPERATION error is generated by VertexArrayBindingDivisor
+    *    if <vaobj> is not [compatibility profile: zero or] the name of an
+    *    existing vertex array object."
+    */
+   vao = _mesa_lookup_vao_err(ctx, vaobj, "glVertexArrayBindingDivisor");
+   if (!vao)
+       return;
+
+   vertex_array_binding_divisor(ctx, vao, bindingIndex, divisor,
+                                "glVertexArrayBindingDivisor");
+}
+
 
 /**
  * Copy one client vertex array to another.
@@ -1155,13 +2139,43 @@ _mesa_copy_client_array(struct gl_context *ctx,
    dst->Enabled = src->Enabled;
    dst->Normalized = src->Normalized;
    dst->Integer = src->Integer;
+   dst->Doubles = src->Doubles;
    dst->InstanceDivisor = src->InstanceDivisor;
    dst->_ElementSize = src->_ElementSize;
    _mesa_reference_buffer_object(ctx, &dst->BufferObj, src->BufferObj);
-   dst->_MaxElement = src->_MaxElement;
 }
 
+void
+_mesa_copy_vertex_attrib_array(struct gl_context *ctx,
+                               struct gl_vertex_attrib_array *dst,
+                               const struct gl_vertex_attrib_array *src)
+{
+   dst->Size           = src->Size;
+   dst->Type           = src->Type;
+   dst->Format         = src->Format;
+   dst->VertexBinding  = src->VertexBinding;
+   dst->RelativeOffset = src->RelativeOffset;
+   dst->Format         = src->Format;
+   dst->Integer        = src->Integer;
+   dst->Doubles        = src->Doubles;
+   dst->Normalized     = src->Normalized;
+   dst->Ptr            = src->Ptr;
+   dst->Enabled        = src->Enabled;
+   dst->_ElementSize   = src->_ElementSize;
+}
+
+void
+_mesa_copy_vertex_buffer_binding(struct gl_context *ctx,
+                                 struct gl_vertex_buffer_binding *dst,
+                                 const struct gl_vertex_buffer_binding *src)
+{
+   dst->Offset          = src->Offset;
+   dst->Stride          = src->Stride;
+   dst->InstanceDivisor = src->InstanceDivisor;
+   dst->_BoundArrays    = src->_BoundArrays;
 
+   _mesa_reference_buffer_object(ctx, &dst->BufferObj, src->BufferObj);
+}
 
 /**
  * Print vertex array's fields.
@@ -1170,14 +2184,13 @@ static void
 print_array(const char *name, GLint index, const struct gl_client_array *array)
 {
    if (index >= 0)
-      printf("  %s[%d]: ", name, index);
+      fprintf(stderr, "  %s[%d]: ", name, index);
    else
-      printf("  %s: ", name);
-   printf("Ptr=%p, Type=0x%x, Size=%d, ElemSize=%u, Stride=%d, Buffer=%u(Size %lu), MaxElem=%u\n",
+      fprintf(stderr, "  %s: ", name);
+   fprintf(stderr, "Ptr=%p, Type=0x%x, Size=%d, ElemSize=%u, Stride=%d, Buffer=%u(Size %lu)\n",
          array->Ptr, array->Type, array->Size,
          array->_ElementSize, array->StrideB,
-         array->BufferObj->Name, (unsigned long) array->BufferObj->Size,
-         array->_MaxElement);
+         array->BufferObj->Name, (unsigned long) array->BufferObj->Size);
 }
 
 
@@ -1187,25 +2200,22 @@ print_array(const char *name, GLint index, const struct gl_client_array *array)
 void
 _mesa_print_arrays(struct gl_context *ctx)
 {
-   struct gl_array_object *arrayObj = ctx->Array.ArrayObj;
+   struct gl_vertex_array_object *vao = ctx->Array.VAO;
    GLuint i;
 
-   _mesa_update_array_object_max_element(ctx, arrayObj);
-
-   printf("Array Object %u\n", arrayObj->Name);
-   if (arrayObj->Vertex.Enabled)
-      print_array("Vertex", -1, &arrayObj->Vertex);
-   if (arrayObj->Normal.Enabled)
-      print_array("Normal", -1, &arrayObj->Normal);
-   if (arrayObj->Color.Enabled)
-      print_array("Color", -1, &arrayObj->Color);
-   for (i = 0; i < Elements(arrayObj->TexCoord); i++)
-      if (arrayObj->TexCoord[i].Enabled)
-         print_array("TexCoord", i, &arrayObj->TexCoord[i]);
-   for (i = 0; i < Elements(arrayObj->VertexAttrib); i++)
-      if (arrayObj->VertexAttrib[i].Enabled)
-         print_array("Attrib", i, &arrayObj->VertexAttrib[i]);
-   printf("  _MaxElement = %u\n", arrayObj->_MaxElement);
+   printf("Array Object %u\n", vao->Name);
+   if (vao->_VertexAttrib[VERT_ATTRIB_POS].Enabled)
+      print_array("Vertex", -1, &vao->_VertexAttrib[VERT_ATTRIB_POS]);
+   if (vao->_VertexAttrib[VERT_ATTRIB_NORMAL].Enabled)
+      print_array("Normal", -1, &vao->_VertexAttrib[VERT_ATTRIB_NORMAL]);
+   if (vao->_VertexAttrib[VERT_ATTRIB_COLOR0].Enabled)
+      print_array("Color", -1, &vao->_VertexAttrib[VERT_ATTRIB_COLOR0]);
+   for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++)
+      if (vao->_VertexAttrib[VERT_ATTRIB_TEX(i)].Enabled)
+         print_array("TexCoord", i, &vao->_VertexAttrib[VERT_ATTRIB_TEX(i)]);
+   for (i = 0; i < VERT_ATTRIB_GENERIC_MAX; i++)
+      if (vao->_VertexAttrib[VERT_ATTRIB_GENERIC(i)].Enabled)
+         print_array("Attrib", i, &vao->_VertexAttrib[VERT_ATTRIB_GENERIC(i)]);
 }
 
 
@@ -1215,9 +2225,8 @@ _mesa_print_arrays(struct gl_context *ctx)
 void 
 _mesa_init_varray(struct gl_context *ctx)
 {
-   ctx->Array.DefaultArrayObj = _mesa_new_array_object(ctx, 0);
-   _mesa_reference_array_object(ctx, &ctx->Array.ArrayObj,
-                                ctx->Array.DefaultArrayObj);
+   ctx->Array.DefaultVAO = ctx->Driver.NewArrayObject(ctx, 0);
+   _mesa_reference_vao(ctx, &ctx->Array.VAO, ctx->Array.DefaultVAO);
    ctx->Array.ActiveTexture = 0;   /* GL_ARB_multitexture */
 
    ctx->Array.Objects = _mesa_NewHashTable();
@@ -1230,9 +2239,9 @@ _mesa_init_varray(struct gl_context *ctx)
 static void
 delete_arrayobj_cb(GLuint id, void *data, void *userData)
 {
-   struct gl_array_object *arrayObj = (struct gl_array_object *) data;
+   struct gl_vertex_array_object *vao = (struct gl_vertex_array_object *) data;
    struct gl_context *ctx = (struct gl_context *) userData;
-   _mesa_delete_array_object(ctx, arrayObj);
+   _mesa_delete_vao(ctx, vao);
 }