+/**
+ * Copy gl_vertex_array_object from src to dest.
+ * 'dest' must be in an initialized state.
+ */
+static void
+copy_array_object(struct gl_context *ctx,
+ struct gl_vertex_array_object *dest,
+ struct gl_vertex_array_object *src)
+{
+ GLuint i;
+
+ /* skip Name */
+ /* skip RefCount */
+
+ /* In theory must be the same anyway, but on recreate make sure it matches */
+ dest->ARBsemantics = src->ARBsemantics;
+
+ for (i = 0; i < ARRAY_SIZE(src->VertexAttrib); i++) {
+ _mesa_copy_client_array(ctx, &dest->_VertexAttrib[i], &src->_VertexAttrib[i]);
+ _mesa_copy_vertex_attrib_array(ctx, &dest->VertexAttrib[i], &src->VertexAttrib[i]);
+ _mesa_copy_vertex_buffer_binding(ctx, &dest->VertexBinding[i], &src->VertexBinding[i]);
+ }
+
+ /* _Enabled must be the same than on push */
+ dest->_Enabled = src->_Enabled;
+ dest->NewArrays = src->NewArrays;
+}
+
+/**
+ * Copy gl_array_attrib from src to dest.
+ * 'dest' must be in an initialized state.
+ */
+static void
+copy_array_attrib(struct gl_context *ctx,
+ struct gl_array_attrib *dest,
+ struct gl_array_attrib *src,
+ bool vbo_deleted)
+{
+ /* skip ArrayObj */
+ /* skip DefaultArrayObj, Objects */
+ dest->ActiveTexture = src->ActiveTexture;
+ dest->LockFirst = src->LockFirst;
+ dest->LockCount = src->LockCount;
+ dest->PrimitiveRestart = src->PrimitiveRestart;
+ dest->PrimitiveRestartFixedIndex = src->PrimitiveRestartFixedIndex;
+ dest->_PrimitiveRestart = src->_PrimitiveRestart;
+ dest->RestartIndex = src->RestartIndex;
+ /* skip NewState */
+ /* skip RebindArrays */
+
+ if (!vbo_deleted)
+ copy_array_object(ctx, dest->VAO, src->VAO);
+
+ /* skip ArrayBufferObj */
+ /* skip IndexBufferObj */
+
+ /* Invalidate draw state. It will be updated during the next draw. */
+ dest->DrawMethod = DRAW_NONE;
+ dest->_DrawArrays = NULL;
+}
+
+/**
+ * Save the content of src to dest.
+ */
+static void
+save_array_attrib(struct gl_context *ctx,
+ struct gl_array_attrib *dest,
+ struct gl_array_attrib *src)
+{
+ /* Set the Name, needed for restore, but do never overwrite.
+ * Needs to match value in the object hash. */
+ dest->VAO->Name = src->VAO->Name;
+ /* And copy all of the rest. */
+ copy_array_attrib(ctx, dest, src, false);
+
+ /* Just reference them here */
+ _mesa_reference_buffer_object(ctx, &dest->ArrayBufferObj,
+ src->ArrayBufferObj);
+ _mesa_reference_buffer_object(ctx, &dest->VAO->IndexBufferObj,
+ src->VAO->IndexBufferObj);
+}
+
+/**
+ * Restore the content of src to dest.
+ */
+static void
+restore_array_attrib(struct gl_context *ctx,
+ struct gl_array_attrib *dest,
+ struct gl_array_attrib *src)
+{
+ /* The ARB_vertex_array_object spec says:
+ *
+ * "BindVertexArray fails and an INVALID_OPERATION error is generated
+ * if array is not a name returned from a previous call to
+ * GenVertexArrays, or if such a name has since been deleted with
+ * DeleteVertexArrays."
+ *
+ * Therefore popping a deleted VAO cannot magically recreate it.
+ *
+ * The semantics of objects created using APPLE_vertex_array_objects behave
+ * differently. These objects expect to be recreated by pop. Alas.
+ */
+ const bool arb_vao = (src->VAO->Name != 0
+ && src->VAO->ARBsemantics);
+
+ if (arb_vao && !_mesa_IsVertexArray(src->VAO->Name))
+ return;
+
+ _mesa_BindVertexArrayAPPLE(src->VAO->Name);
+
+ /* Restore or recreate the buffer objects by the names ... */
+ if (!arb_vao
+ || src->ArrayBufferObj->Name == 0
+ || _mesa_IsBuffer(src->ArrayBufferObj->Name)) {
+ /* ... and restore its content */
+ copy_array_attrib(ctx, dest, src, false);
+
+ _mesa_BindBuffer(GL_ARRAY_BUFFER_ARB,
+ src->ArrayBufferObj->Name);
+ } else {
+ copy_array_attrib(ctx, dest, src, true);
+ }
+
+ if (!arb_vao
+ || src->VAO->IndexBufferObj->Name == 0
+ || _mesa_IsBuffer(src->VAO->IndexBufferObj->Name))
+ _mesa_BindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,
+ src->VAO->IndexBufferObj->Name);
+}
+
+/**
+ * init/alloc the fields of 'attrib'.
+ * Needs to the init part matching free_array_attrib_data below.
+ */
+static bool
+init_array_attrib_data(struct gl_context *ctx,
+ struct gl_array_attrib *attrib)
+{
+ /* Get a non driver gl_vertex_array_object. */
+ attrib->VAO = CALLOC_STRUCT( gl_vertex_array_object );
+
+ if (attrib->VAO == NULL) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glPushClientAttrib");
+ return false;
+ }
+
+ _mesa_initialize_vao(ctx, attrib->VAO, 0);
+ return true;
+}
+
+/**
+ * Free/unreference the fields of 'attrib' but don't delete it (that's
+ * done later in the calling code).
+ * Needs to the cleanup part matching init_array_attrib_data above.
+ */
+static void
+free_array_attrib_data(struct gl_context *ctx,
+ struct gl_array_attrib *attrib)
+{
+ /* We use a non driver array object, so don't just unref since we would
+ * end up using the drivers DeleteArrayObject function for deletion. */
+ _mesa_delete_vao(ctx, attrib->VAO);
+ attrib->VAO = 0;
+ _mesa_reference_buffer_object(ctx, &attrib->ArrayBufferObj, NULL);
+}
+