Uploaded non-VBO user data will be set via these functions.
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4314>
    bind_buffer_object(ctx, bindTarget, buffer);
 }
 
+void
+_mesa_InternalBindElementBuffer(struct gl_context *ctx,
+                                struct gl_buffer_object *buf)
+{
+   struct gl_buffer_object **bindTarget =
+      get_buffer_target(ctx, GL_ELEMENT_ARRAY_BUFFER);
+
+   /* Move the buffer reference from the parameter to the bind point. */
+   _mesa_reference_buffer_object(ctx, bindTarget, NULL);
+   if (buf)
+      *bindTarget = buf;
+}
+
 /**
  * Binds a buffer object to a binding point.
  *
 
 void GLAPIENTRY
 _mesa_BindBuffer(GLenum target, GLuint buffer);
 
+void
+_mesa_InternalBindElementBuffer(struct gl_context *ctx,
+                                struct gl_buffer_object *buf);
+
 void GLAPIENTRY
 _mesa_DeleteBuffers_no_error(GLsizei n, const GLuint * buffer);
 
 
 struct gl_context;
 struct _mesa_HashTable;
 
+struct glthread_attrib_binding {
+   struct gl_buffer_object *buffer; /**< where non-VBO data was uploaded */
+   int offset;                      /**< offset to uploaded non-VBO data */
+   const void *original_pointer;    /**< restore this pointer after the draw */
+};
+
 struct glthread_vao {
    GLuint Name;
    GLuint CurrentElementBufferName;
 
 }
 
 
+void
+_mesa_InternalBindVertexBuffers(struct gl_context *ctx,
+                                const struct glthread_attrib_binding *attribs,
+                                GLbitfield attrib_mask,
+                                GLboolean restore_pointers)
+{
+   struct gl_vertex_array_object *vao = ctx->Array.VAO;
+   unsigned param_index = 0;
+
+   if (restore_pointers) {
+      while (attrib_mask) {
+         unsigned i = u_bit_scan(&attrib_mask);
+
+         _mesa_bind_vertex_buffer(ctx, vao, i, NULL,
+                                  (GLintptr)attribs[param_index].original_pointer,
+                                  vao->BufferBinding[i].Stride, false, false);
+         param_index++;
+      }
+      return;
+   }
+
+   while (attrib_mask) {
+      unsigned i = u_bit_scan(&attrib_mask);
+      struct gl_buffer_object *buf = attribs[param_index].buffer;
+
+      /* The buffer reference is passed to _mesa_bind_vertex_buffer. */
+      _mesa_bind_vertex_buffer(ctx, vao, i, buf, attribs[param_index].offset,
+                               vao->BufferBinding[i].Stride, true, true);
+      param_index++;
+   }
+}
+
+
 void GLAPIENTRY
 _mesa_VertexArrayVertexBuffers_no_error(GLuint vaobj, GLuint first,
                                         GLsizei count, const GLuint *buffers,
 
 _mesa_BindVertexBuffers(GLuint first, GLsizei count, const GLuint *buffers,
                         const GLintptr *offsets, const GLsizei *strides);
 
+void
+_mesa_InternalBindVertexBuffers(struct gl_context *ctx,
+                                const struct glthread_attrib_binding *attribs,
+                                GLbitfield attrib_mask,
+                                GLboolean restore_pointers);
+
 void GLAPIENTRY
 _mesa_VertexArrayVertexBuffers_no_error(GLuint vaobj, GLuint first,
                                         GLsizei count, const GLuint *buffers,