glthread: handle ARB_vertex_attrib_binding
authorMarek Olšák <marek.olsak@amd.com>
Sat, 30 May 2020 21:42:46 +0000 (17:42 -0400)
committerMarge Bot <eric+marge@anholt.net>
Tue, 23 Jun 2020 06:41:37 +0000 (06:41 +0000)
This handles ARB_vertex_attrib_binding for vertex uploads correctly.
Before this, the extension might have led to crashes if non-VBO vertex
attribs were present.

Acked-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/5303>

src/mapi/glapi/gen/ARB_direct_state_access.xml
src/mapi/glapi/gen/ARB_vertex_attrib_binding.xml
src/mesa/main/glthread.h
src/mesa/main/glthread_draw.c
src/mesa/main/glthread_marshal.h
src/mesa/main/glthread_varray.c

index cd0f51c4327b46de7a729b418ab9a12136cb0af7..f06adc4cfc2367318091716d321aec62d2479e2b 100644 (file)
       <param name="index" type="GLuint" />
    </function>
 
-   <function name="VertexArrayElementBuffer" no_error="true">
+   <function name="VertexArrayElementBuffer" no_error="true"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAElementBuffer(ctx, vaobj, buffer);">
       <param name="vaobj" type="GLuint" />
       <param name="buffer" type="GLuint" />
    </function>
 
-   <function name="VertexArrayVertexBuffer" no_error="true">
+   <function name="VertexArrayVertexBuffer" no_error="true"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAVertexBuffer(ctx, vaobj, bindingindex, buffer, offset, stride);">
       <param name="vaobj" type="GLuint" />
       <param name="bindingindex" type="GLuint" />
       <param name="buffer" type="GLuint" />
       <param name="stride" type="GLsizei" />
    </function>
 
-   <function name="VertexArrayVertexBuffers" no_error="true">
+   <function name="VertexArrayVertexBuffers" no_error="true"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAVertexBuffers(ctx, vaobj, first, count, buffers, offsets, strides);">
       <param name="vaobj" type="GLuint" />
       <param name="first" type="GLuint" />
       <param name="count" type="GLsizei" />
       <param name="strides" type="const GLsizei *" count="count"/>
    </function>
 
-   <function name="VertexArrayAttribFormat">
+   <function name="VertexArrayAttribFormat"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribFormat(ctx, vaobj, attribindex, size, type, relativeoffset);">
       <param name="vaobj" type="GLuint" />
       <param name="attribindex" type="GLuint" />
       <param name="size" type="GLint" />
       <param name="relativeoffset" type="GLuint" />
    </function>
 
-   <function name="VertexArrayAttribIFormat">
+   <function name="VertexArrayAttribIFormat"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribFormat(ctx, vaobj, attribindex, size, type, relativeoffset);">
       <param name="vaobj" type="GLuint" />
       <param name="attribindex" type="GLuint" />
       <param name="size" type="GLint" />
       <param name="relativeoffset" type="GLuint" />
    </function>
 
-   <function name="VertexArrayAttribLFormat">
+   <function name="VertexArrayAttribLFormat"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribFormat(ctx, vaobj, attribindex, size, type, relativeoffset);">
       <param name="vaobj" type="GLuint" />
       <param name="attribindex" type="GLuint" />
       <param name="size" type="GLint" />
       <param name="relativeoffset" type="GLuint" />
    </function>
 
-   <function name="VertexArrayAttribBinding" no_error="true">
+   <function name="VertexArrayAttribBinding" no_error="true"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribBinding(ctx, vaobj, attribindex, bindingindex);">
       <param name="vaobj" type="GLuint" />
       <param name="attribindex" type="GLuint" />
       <param name="bindingindex" type="GLuint" />
    </function>
 
-   <function name="VertexArrayBindingDivisor" no_error="true">
+   <function name="VertexArrayBindingDivisor" no_error="true"
+             marshal_call_after="if (COMPAT) _mesa_glthread_DSABindingDivisor(ctx, vaobj, bindingindex, divisor);">
       <param name="vaobj" type="GLuint" />
       <param name="bindingindex" type="GLuint" />
       <param name="divisor" type="GLuint" />
index ca9dd5a75c25c2b41ce5b476db6bfd7e6cb5307f..056cc34543019107cb4427e3b9d28bee2cc56242 100644 (file)
@@ -7,14 +7,16 @@
 
 <category name="GL_ARB_vertex_attrib_binding" number="125">
 
-    <function name="BindVertexBuffer" es2="3.1" no_error="true">
+    <function name="BindVertexBuffer" es2="3.1" no_error="true"
+              marshal_call_after="if (COMPAT) _mesa_glthread_VertexBuffer(ctx, bindingindex, buffer, offset, stride);">
         <param name="bindingindex" type="GLuint"/>
         <param name="buffer" type="GLuint"/>
         <param name="offset" type="GLintptr"/>
         <param name="stride" type="GLsizei"/>
     </function>
 
-    <function name="VertexAttribFormat" es2="3.1">
+    <function name="VertexAttribFormat" es2="3.1"
+              marshal_call_after="if (COMPAT) _mesa_glthread_AttribFormat(ctx, attribindex, size, type, relativeoffset);">
         <param name="attribindex" type="GLuint"/>
         <param name="size" type="GLint"/>
         <param name="type" type="GLenum"/>
         <param name="relativeoffset" type="GLuint"/>
     </function>
 
-    <function name="VertexAttribIFormat" es2="3.1">
+    <function name="VertexAttribIFormat" es2="3.1"
+              marshal_call_after="if (COMPAT) _mesa_glthread_AttribFormat(ctx, attribindex, size, type, relativeoffset);">
         <param name="attribindex" type="GLuint"/>
         <param name="size" type="GLint"/>
         <param name="type" type="GLenum"/>
         <param name="relativeoffset" type="GLuint"/>
     </function>
 
-    <function name="VertexAttribLFormat">
+    <function name="VertexAttribLFormat"
+              marshal_call_after="if (COMPAT) _mesa_glthread_AttribFormat(ctx, attribindex, size, type, relativeoffset);">
         <param name="attribindex" type="GLuint"/>
         <param name="size" type="GLint"/>
         <param name="type" type="GLenum"/>
         <param name="relativeoffset" type="GLuint"/>
     </function>
 
-    <function name="VertexAttribBinding" es2="3.1" no_error="true">
+    <function name="VertexAttribBinding" es2="3.1" no_error="true"
+              marshal_call_after="if (COMPAT) _mesa_glthread_AttribBinding(ctx, attribindex, bindingindex);">
         <param name="attribindex" type="GLuint"/>
         <param name="bindingindex" type="GLuint"/>
     </function>
 
-    <function name="VertexBindingDivisor" es2="3.1" no_error="true">
+    <function name="VertexBindingDivisor" es2="3.1" no_error="true"
+              marshal_call_after="if (COMPAT) _mesa_glthread_BindingDivisor(ctx, bindingindex, divisor);">
         <param name="bindingindex" type="GLuint"/>
         <param name="divisor" type="GLuint"/>
     </function>
 
-    <function name="VertexArrayBindVertexBufferEXT">
+    <function name="VertexArrayBindVertexBufferEXT"
+              marshal_call_after="if (COMPAT) _mesa_glthread_DSAVertexBuffer(ctx, vaobj, bindingindex, buffer, offset, stride);">
         <param name="vaobj" type="GLuint"/>
         <param name="bindingindex" type="GLuint"/>
         <param name="buffer" type="GLuint"/>
@@ -54,7 +61,8 @@
         <param name="stride" type="GLsizei"/>
     </function>
 
-    <function name="VertexArrayVertexAttribFormatEXT">
+    <function name="VertexArrayVertexAttribFormatEXT"
+              marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribFormat(ctx, vaobj, attribindex, size, type, relativeoffset);">
         <param name="vaobj" type="GLuint"/>
         <param name="attribindex" type="GLuint"/>
         <param name="size" type="GLint"/>
@@ -63,7 +71,8 @@
         <param name="relativeoffset" type="GLuint"/>
     </function>
 
-    <function name="VertexArrayVertexAttribIFormatEXT">
+    <function name="VertexArrayVertexAttribIFormatEXT"
+              marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribFormat(ctx, vaobj, attribindex, size, type, relativeoffset);">
         <param name="vaobj" type="GLuint"/>
         <param name="attribindex" type="GLuint"/>
         <param name="size" type="GLint"/>
@@ -71,7 +80,8 @@
         <param name="relativeoffset" type="GLuint"/>
     </function>
 
-    <function name="VertexArrayVertexAttribLFormatEXT">
+    <function name="VertexArrayVertexAttribLFormatEXT"
+              marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribFormat(ctx, vaobj, attribindex, size, type, relativeoffset);">
         <param name="vaobj" type="GLuint"/>
         <param name="attribindex" type="GLuint"/>
         <param name="size" type="GLint"/>
         <param name="relativeoffset" type="GLuint"/>
     </function>
 
-    <function name="VertexArrayVertexAttribBindingEXT">
+    <function name="VertexArrayVertexAttribBindingEXT"
+              marshal_call_after="if (COMPAT) _mesa_glthread_DSAAttribBinding(ctx, vaobj, attribindex, bindingindex);">
         <param name="vaobj" type="GLuint"/>
         <param name="attribindex" type="GLuint"/>
         <param name="bindingindex" type="GLuint"/>
     </function>
 
-    <function name="VertexArrayVertexBindingDivisorEXT">
+    <function name="VertexArrayVertexBindingDivisorEXT"
+              marshal_call_after="if (COMPAT) _mesa_glthread_DSABindingDivisor(ctx, vaobj, bindingindex, divisor);">
         <param name="vaobj" type="GLuint"/>
         <param name="bindingindex" type="GLuint"/>
         <param name="divisor" type="GLuint"/>
index 34a5443a6daff75f3affb53b0fe1cd3835461df3..b07169e334c619af0780e66503a196e38ce84542 100644 (file)
@@ -66,15 +66,23 @@ struct glthread_attrib_binding {
 struct glthread_vao {
    GLuint Name;
    GLuint CurrentElementBufferName;
-   GLbitfield UserEnabled; /**< Vertex attrib arrays enabled by the user. */
+   GLbitfield UserEnabled; /**< Vertex attribs enabled by the user. */
    GLbitfield Enabled; /**< UserEnabled with POS vs GENERIC0 aliasing resolved. */
-   GLbitfield UserPointerMask;
-   GLbitfield NonZeroDivisorMask;
+   GLbitfield BufferEnabled; /**< "Enabled" converted to buffer bindings. */
+   GLbitfield BufferInterleaved; /**< Bitmask of buffers used by multiple attribs. */
+   GLbitfield UserPointerMask; /**< Bitmask of buffer bindings. */
+   GLbitfield NonZeroDivisorMask; /**< Bitmask of buffer bindings. */
 
    struct {
+      /* Per attrib: */
       GLuint ElementSize;
+      GLuint RelativeOffset;
+      GLuint BufferIndex; /**< Referring to Attrib[BufferIndex]. */
+
+      /* Per buffer binding: */
       GLsizei Stride;
       GLuint Divisor;
+      int EnabledAttribCount; /**< Number of enabled attribs using this buffer. */
       const void *Pointer;
    } Attrib[VERT_ATTRIB_MAX];
 };
@@ -207,6 +215,31 @@ void _mesa_glthread_DSAAttribPointer(struct gl_context *ctx, GLuint vao,
                                      GLuint buffer, gl_vert_attrib attrib,
                                      GLint size, GLenum type, GLsizei stride,
                                      GLintptr offset);
+void _mesa_glthread_AttribFormat(struct gl_context *ctx, GLuint attribindex,
+                                 GLint size, GLenum type,  GLuint relativeoffset);
+void _mesa_glthread_DSAAttribFormat(struct gl_context *ctx, GLuint vaobj,
+                                    GLuint attribindex, GLint size, GLenum type,
+                                    GLuint relativeoffset);
+void _mesa_glthread_VertexBuffer(struct gl_context *ctx, GLuint bindingindex,
+                                 GLuint buffer, GLintptr offset, GLsizei stride);
+void _mesa_glthread_DSAVertexBuffer(struct gl_context *ctx, GLuint vaobj,
+                                    GLuint bindingindex, GLuint buffer,
+                                    GLintptr offset, GLsizei stride);
+void _mesa_glthread_DSAVertexBuffers(struct gl_context *ctx, GLuint vaobj,
+                                     GLuint first, GLsizei count,
+                                     const GLuint *buffers,
+                                     const GLintptr *offsets,
+                                     const GLsizei *strides);
+void _mesa_glthread_BindingDivisor(struct gl_context *ctx, GLuint bindingindex,
+                                   GLuint divisor);
+void _mesa_glthread_DSABindingDivisor(struct gl_context *ctx, GLuint vaobj,
+                                      GLuint bindingindex, GLuint divisor);
+void _mesa_glthread_AttribBinding(struct gl_context *ctx, GLuint attribindex,
+                                  GLuint bindingindex);
+void _mesa_glthread_DSAAttribBinding(struct gl_context *ctx, GLuint vaobj,
+                                     GLuint attribindex, GLuint bindingindex);
+void _mesa_glthread_DSAElementBuffer(struct gl_context *ctx, GLuint vaobj,
+                                     GLuint buffer);
 void _mesa_glthread_PushClientAttrib(struct gl_context *ctx, GLbitfield mask,
                                      bool set_default);
 void _mesa_glthread_PopClientAttrib(struct gl_context *ctx);
index ed27cb14bff3f03a1c7032701342bfbd446a6a0a..1fd6828e1f688185f7e3ebc6307994adb2814876 100644 (file)
@@ -113,20 +113,111 @@ upload_vertices(struct gl_context *ctx, unsigned user_buffer_mask,
                 struct glthread_attrib_binding *buffers)
 {
    struct glthread_vao *vao = ctx->GLThread.CurrentVAO;
-   unsigned attrib_mask_iter = user_buffer_mask;
+   unsigned attrib_mask_iter = vao->Enabled;
    unsigned num_buffers = 0;
 
    assert((num_vertices || !(user_buffer_mask & ~vao->NonZeroDivisorMask)) &&
           (num_instances || !(user_buffer_mask & vao->NonZeroDivisorMask)));
 
+   if (unlikely(vao->BufferInterleaved & user_buffer_mask)) {
+      /* Slower upload path where some buffers reference multiple attribs,
+       * so we have to use 2 while loops instead of 1.
+       */
+      unsigned start_offset[VERT_ATTRIB_MAX];
+      unsigned end_offset[VERT_ATTRIB_MAX];
+      uint32_t buffer_mask = 0;
+
+      while (attrib_mask_iter) {
+         unsigned i = u_bit_scan(&attrib_mask_iter);
+         unsigned binding_index = vao->Attrib[i].BufferIndex;
+
+         if (!(user_buffer_mask & (1 << binding_index)))
+            continue;
+
+         unsigned stride = vao->Attrib[binding_index].Stride;
+         unsigned instance_div = vao->Attrib[binding_index].Divisor;
+         unsigned element_size = vao->Attrib[i].ElementSize;
+         unsigned offset = vao->Attrib[i].RelativeOffset;
+         unsigned size;
+
+         if (instance_div) {
+            /* Per-instance attrib. */
+
+            /* Figure out how many instances we'll render given instance_div.  We
+             * can't use the typical div_round_up() pattern because the CTS uses
+             * instance_div = ~0 for a test, which overflows div_round_up()'s
+             * addition.
+             */
+            unsigned count = num_instances / instance_div;
+            if (count * instance_div != num_instances)
+               count++;
+
+            offset += stride * start_instance;
+            size = stride * (count - 1) + element_size;
+         } else {
+            /* Per-vertex attrib. */
+            offset += stride * start_vertex;
+            size = stride * (num_vertices - 1) + element_size;
+         }
+
+         unsigned binding_index_bit = 1u << binding_index;
+
+         /* Update upload offsets. */
+         if (!(buffer_mask & binding_index_bit)) {
+            start_offset[binding_index] = offset;
+            end_offset[binding_index] = offset + size;
+         } else {
+            if (offset < start_offset[binding_index])
+               start_offset[binding_index] = offset;
+            if (offset + size > end_offset[binding_index])
+               end_offset[binding_index] = offset + size;
+         }
+
+         buffer_mask |= binding_index_bit;
+      }
+
+      /* Upload buffers. */
+      while (buffer_mask) {
+         struct gl_buffer_object *upload_buffer = NULL;
+         unsigned upload_offset = 0;
+         unsigned start, end;
+
+         unsigned binding_index = u_bit_scan(&buffer_mask);
+
+         start = start_offset[binding_index];
+         end = end_offset[binding_index];
+         assert(start < end);
+
+         const void *ptr = vao->Attrib[binding_index].Pointer;
+         _mesa_glthread_upload(ctx, (uint8_t*)ptr + start,
+                               end - start, &upload_offset,
+                               &upload_buffer, NULL);
+         assert(upload_buffer);
+
+         buffers[num_buffers].buffer = upload_buffer;
+         buffers[num_buffers].offset = upload_offset - start;
+         buffers[num_buffers].original_pointer = ptr;
+         num_buffers++;
+      }
+
+      return true;
+   }
+
+   /* Faster path where all attribs are separate. */
    while (attrib_mask_iter) {
       unsigned i = u_bit_scan(&attrib_mask_iter);
+      unsigned binding_index = vao->Attrib[i].BufferIndex;
+
+      if (!(user_buffer_mask & (1 << binding_index)))
+         continue;
+
       struct gl_buffer_object *upload_buffer = NULL;
       unsigned upload_offset = 0;
-      unsigned stride = vao->Attrib[i].Stride;
-      unsigned instance_div = vao->Attrib[i].Divisor;
+      unsigned stride = vao->Attrib[binding_index].Stride;
+      unsigned instance_div = vao->Attrib[binding_index].Divisor;
       unsigned element_size = vao->Attrib[i].ElementSize;
-      unsigned offset, size;
+      unsigned offset = vao->Attrib[i].RelativeOffset;
+      unsigned size;
 
       if (instance_div) {
          /* Per-instance attrib. */
@@ -140,15 +231,15 @@ upload_vertices(struct gl_context *ctx, unsigned user_buffer_mask,
          if (count * instance_div != num_instances)
             count++;
 
-         offset = stride * start_instance;
+         offset += stride * start_instance;
          size = stride * (count - 1) + element_size;
       } else {
          /* Per-vertex attrib. */
-         offset = stride * start_vertex;
+         offset += stride * start_vertex;
          size = stride * (num_vertices - 1) + element_size;
       }
 
-      const void *ptr = vao->Attrib[i].Pointer;
+      const void *ptr = vao->Attrib[binding_index].Pointer;
       _mesa_glthread_upload(ctx, (uint8_t*)ptr + offset,
                             size, &upload_offset, &upload_buffer, NULL);
       assert(upload_buffer);
@@ -158,6 +249,7 @@ upload_vertices(struct gl_context *ctx, unsigned user_buffer_mask,
       buffers[num_buffers].original_pointer = ptr;
       num_buffers++;
    }
+
    return true;
 }
 
@@ -233,7 +325,7 @@ draw_arrays(GLenum mode, GLint first, GLsizei count, GLsizei instance_count,
    GET_CURRENT_CONTEXT(ctx);
 
    struct glthread_vao *vao = ctx->GLThread.CurrentVAO;
-   unsigned user_buffer_mask = vao->UserPointerMask & vao->Enabled;
+   unsigned user_buffer_mask = vao->UserPointerMask & vao->BufferEnabled;
 
    if (compiled_into_dlist && ctx->GLThread.inside_dlist) {
       _mesa_glthread_finish_before(ctx, "DrawArrays");
@@ -347,7 +439,7 @@ _mesa_marshal_MultiDrawArrays(GLenum mode, const GLint *first,
    GET_CURRENT_CONTEXT(ctx);
 
    struct glthread_vao *vao = ctx->GLThread.CurrentVAO;
-   unsigned user_buffer_mask = vao->UserPointerMask & vao->Enabled;
+   unsigned user_buffer_mask = vao->UserPointerMask & vao->BufferEnabled;
 
    if (ctx->GLThread.inside_dlist)
       goto sync;
@@ -511,7 +603,7 @@ draw_elements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices,
    GET_CURRENT_CONTEXT(ctx);
 
    struct glthread_vao *vao = ctx->GLThread.CurrentVAO;
-   unsigned user_buffer_mask = vao->UserPointerMask & vao->Enabled;
+   unsigned user_buffer_mask = vao->UserPointerMask & vao->BufferEnabled;
    bool has_user_indices = vao->CurrentElementBufferName == 0;
 
    if (compiled_into_dlist && ctx->GLThread.inside_dlist)
@@ -724,7 +816,7 @@ _mesa_marshal_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count,
    GET_CURRENT_CONTEXT(ctx);
 
    struct glthread_vao *vao = ctx->GLThread.CurrentVAO;
-   unsigned user_buffer_mask = vao->UserPointerMask & vao->Enabled;
+   unsigned user_buffer_mask = vao->UserPointerMask & vao->BufferEnabled;
    bool has_user_indices = vao->CurrentElementBufferName == 0;
 
    if (ctx->GLThread.inside_dlist)
index c8cf733f9ce34af3a7c96310c1e81d29564f971b..5acc36716d9e9234318ac16b697c0b9a33d6881e 100644 (file)
@@ -85,7 +85,7 @@ _mesa_glthread_has_non_vbo_vertices_or_indices(const struct gl_context *ctx)
 
    return ctx->API != API_OPENGL_CORE &&
           (vao->CurrentElementBufferName == 0 ||
-           (vao->UserPointerMask & vao->Enabled));
+           (vao->UserPointerMask & vao->BufferEnabled));
 }
 
 static inline bool
@@ -95,7 +95,7 @@ _mesa_glthread_has_non_vbo_vertices(const struct gl_context *ctx)
    const struct glthread_vao *vao = glthread->CurrentVAO;
 
    return ctx->API != API_OPENGL_CORE &&
-          (vao->UserPointerMask & vao->Enabled);
+          (vao->UserPointerMask & vao->BufferEnabled);
 }
 
 static inline bool
@@ -106,7 +106,7 @@ _mesa_glthread_has_non_vbo_vertices_or_indirect(const struct gl_context *ctx)
 
    return ctx->API != API_OPENGL_CORE &&
           (glthread->CurrentDrawIndirectBufferName == 0 ||
-           (vao->UserPointerMask & vao->Enabled));
+           (vao->UserPointerMask & vao->BufferEnabled));
 }
 
 static inline bool
@@ -118,7 +118,7 @@ _mesa_glthread_has_non_vbo_vertices_or_indices_or_indirect(const struct gl_conte
    return ctx->API != API_OPENGL_CORE &&
           (glthread->CurrentDrawIndirectBufferName == 0 ||
            vao->CurrentElementBufferName == 0 ||
-           (vao->UserPointerMask & vao->Enabled));
+           (vao->UserPointerMask & vao->BufferEnabled));
 }
 
 
index 8fae4af3123c39353c1cee8248434eca81a48c7f..664e464435125b2e82cd78db0b753e4c69952777 100644 (file)
@@ -33,9 +33,6 @@
 #include "main/dispatch.h"
 #include "main/varray.h"
 
-/* TODO:
- *   - Handle ARB_vertex_attrib_binding (incl. EXT_dsa and ARB_dsa)
- */
 
 void
 _mesa_glthread_reset_vao(struct glthread_vao *vao)
@@ -52,6 +49,7 @@ _mesa_glthread_reset_vao(struct glthread_vao *vao)
    vao->CurrentElementBufferName = 0;
    vao->UserEnabled = 0;
    vao->Enabled = 0;
+   vao->BufferEnabled = 0;
    vao->UserPointerMask = 0;
    vao->NonZeroDivisorMask = 0;
 
@@ -61,8 +59,11 @@ _mesa_glthread_reset_vao(struct glthread_vao *vao)
          elem_size = 16;
 
       vao->Attrib[i].ElementSize = elem_size;
+      vao->Attrib[i].RelativeOffset = 0;
+      vao->Attrib[i].BufferIndex = i;
       vao->Attrib[i].Stride = elem_size;
       vao->Attrib[i].Divisor = 0;
+      vao->Attrib[i].EnabledAttribCount = 0;
       vao->Attrib[i].Pointer = NULL;
    }
 }
@@ -212,6 +213,30 @@ _mesa_glthread_PrimitiveRestartIndex(struct gl_context *ctx, GLuint index)
    update_primitive_restart(ctx);
 }
 
+static inline void
+enable_buffer(struct glthread_vao *vao, unsigned binding_index)
+{
+   int attrib_count = ++vao->Attrib[binding_index].EnabledAttribCount;
+
+   if (attrib_count == 1)
+      vao->BufferEnabled |= 1 << binding_index;
+   else if (attrib_count == 2)
+      vao->BufferInterleaved |= 1 << binding_index;
+}
+
+static inline void
+disable_buffer(struct glthread_vao *vao, unsigned binding_index)
+{
+   int attrib_count = --vao->Attrib[binding_index].EnabledAttribCount;
+
+   if (attrib_count == 0)
+      vao->BufferEnabled &= ~(1 << binding_index);
+   else if (attrib_count == 1)
+      vao->BufferInterleaved &= ~(1 << binding_index);
+   else
+      assert(attrib_count >= 0);
+}
+
 void
 _mesa_glthread_ClientState(struct gl_context *ctx, GLuint *vaobj,
                            gl_vert_attrib attrib, bool enable)
@@ -230,17 +255,63 @@ _mesa_glthread_ClientState(struct gl_context *ctx, GLuint *vaobj,
    if (!vao)
       return;
 
-   if (enable)
-      vao->UserEnabled |= 1u << attrib;
-   else
-      vao->UserEnabled &= ~(1u << attrib);
+   const unsigned attrib_bit = 1u << attrib;
+
+   if (enable && !(vao->UserEnabled & attrib_bit)) {
+      vao->UserEnabled |= attrib_bit;
+
+      /* The generic0 attribute supersedes the position attribute. We need to
+       * update BufferBindingEnabled accordingly.
+       */
+      if (attrib == VERT_ATTRIB_POS) {
+         if (!(vao->UserEnabled & VERT_BIT_GENERIC0))
+            enable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
+      } else {
+         enable_buffer(vao, vao->Attrib[attrib].BufferIndex);
+
+         if (attrib == VERT_ATTRIB_GENERIC0 && vao->UserEnabled & VERT_BIT_POS)
+            disable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
+      }
+   } else if (!enable && (vao->UserEnabled & attrib_bit)) {
+      vao->UserEnabled &= ~attrib_bit;
+
+      /* The generic0 attribute supersedes the position attribute. We need to
+       * update BufferBindingEnabled accordingly.
+       */
+      if (attrib == VERT_ATTRIB_POS) {
+         if (!(vao->UserEnabled & VERT_BIT_GENERIC0))
+            disable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
+      } else {
+         disable_buffer(vao, vao->Attrib[attrib].BufferIndex);
+
+         if (attrib == VERT_ATTRIB_GENERIC0 && vao->UserEnabled & VERT_BIT_POS)
+            enable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
+      }
+   }
 
-   /* The generic0 attribute superseeds the position attribute */
+   /* The generic0 attribute supersedes the position attribute. */
    vao->Enabled = vao->UserEnabled;
    if (vao->Enabled & VERT_BIT_GENERIC0)
       vao->Enabled &= ~VERT_BIT_POS;
 }
 
+static void
+set_attrib_binding(struct glthread_state *glthread, struct glthread_vao *vao,
+                   gl_vert_attrib attrib, unsigned new_binding_index)
+{
+   unsigned old_binding_index = vao->Attrib[attrib].BufferIndex;
+
+   if (old_binding_index != new_binding_index) {
+      vao->Attrib[attrib].BufferIndex = new_binding_index;
+
+      if (vao->Enabled & (1u << attrib)) {
+         /* Update BufferBindingEnabled. */
+         enable_buffer(vao, new_binding_index);
+         disable_buffer(vao, old_binding_index);
+      }
+   }
+}
+
 void _mesa_glthread_AttribDivisor(struct gl_context *ctx, const GLuint *vaobj,
                                   gl_vert_attrib attrib, GLuint divisor)
 {
@@ -253,6 +324,8 @@ void _mesa_glthread_AttribDivisor(struct gl_context *ctx, const GLuint *vaobj,
 
    vao->Attrib[attrib].Divisor = divisor;
 
+   set_attrib_binding(&ctx->GLThread, vao, attrib, attrib);
+
    if (divisor)
       vao->NonZeroDivisorMask |= 1u << attrib;
    else
@@ -273,6 +346,9 @@ attrib_pointer(struct glthread_state *glthread, struct glthread_vao *vao,
    vao->Attrib[attrib].ElementSize = elem_size;
    vao->Attrib[attrib].Stride = stride ? stride : elem_size;
    vao->Attrib[attrib].Pointer = pointer;
+   vao->Attrib[attrib].RelativeOffset = 0;
+
+   set_attrib_binding(glthread, vao, attrib, attrib);
 
    if (buffer != 0)
       vao->UserPointerMask &= ~(1u << attrib);
@@ -309,6 +385,182 @@ _mesa_glthread_DSAAttribPointer(struct gl_context *ctx, GLuint vaobj,
                   (const void*)offset);
 }
 
+static void
+attrib_format(struct glthread_state *glthread, struct glthread_vao *vao,
+              GLuint attribindex, GLint size, GLenum type,
+              GLuint relativeoffset)
+{
+   if (attribindex >= VERT_ATTRIB_GENERIC_MAX)
+      return;
+
+   unsigned elem_size = _mesa_bytes_per_vertex_attrib(size, type);
+
+   unsigned i = VERT_ATTRIB_GENERIC(attribindex);
+   vao->Attrib[i].ElementSize = elem_size;
+   vao->Attrib[i].RelativeOffset = relativeoffset;
+}
+
+void
+_mesa_glthread_AttribFormat(struct gl_context *ctx, GLuint attribindex,
+                            GLint size, GLenum type, GLuint relativeoffset)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+
+   attrib_format(glthread, glthread->CurrentVAO, attribindex, size, type,
+                 relativeoffset);
+}
+
+void
+_mesa_glthread_DSAAttribFormat(struct gl_context *ctx, GLuint vaobj,
+                               GLuint attribindex, GLint size, GLenum type,
+                               GLuint relativeoffset)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
+
+   if (vao)
+      attrib_format(glthread, vao, attribindex, size, type, relativeoffset);
+}
+
+static void
+bind_vertex_buffer(struct glthread_state *glthread, struct glthread_vao *vao,
+                   GLuint bindingindex, GLuint buffer, GLintptr offset,
+                   GLsizei stride)
+{
+   if (bindingindex >= VERT_ATTRIB_GENERIC_MAX)
+      return;
+
+   unsigned i = VERT_ATTRIB_GENERIC(bindingindex);
+   vao->Attrib[i].Pointer = (const void*)offset;
+   vao->Attrib[i].Stride = stride;
+
+   if (buffer != 0)
+      vao->UserPointerMask &= ~(1u << i);
+   else
+      vao->UserPointerMask |= 1u << i;
+}
+
+void
+_mesa_glthread_VertexBuffer(struct gl_context *ctx, GLuint bindingindex,
+                            GLuint buffer, GLintptr offset, GLsizei stride)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+
+   bind_vertex_buffer(glthread, glthread->CurrentVAO, bindingindex, buffer,
+                      offset, stride);
+}
+
+void
+_mesa_glthread_DSAVertexBuffer(struct gl_context *ctx, GLuint vaobj,
+                               GLuint bindingindex, GLuint buffer,
+                               GLintptr offset, GLsizei stride)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
+
+   if (vao)
+      bind_vertex_buffer(glthread, vao, bindingindex, buffer, offset, stride);
+}
+
+void
+_mesa_glthread_DSAVertexBuffers(struct gl_context *ctx, GLuint vaobj,
+                                GLuint first, GLsizei count,
+                                const GLuint *buffers,
+                                const GLintptr *offsets,
+                                const GLsizei *strides)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+   struct glthread_vao *vao;
+
+   vao = lookup_vao(ctx, vaobj);
+   if (!vao)
+      return;
+
+   for (unsigned i = 0; i < count; i++) {
+      bind_vertex_buffer(glthread, vao, first + i, buffers[i], offsets[i],
+                         strides[i]);
+   }
+}
+
+static void
+binding_divisor(struct glthread_state *glthread, struct glthread_vao *vao,
+                GLuint bindingindex, GLuint divisor)
+{
+   if (bindingindex >= VERT_ATTRIB_GENERIC_MAX)
+      return;
+
+   unsigned i = VERT_ATTRIB_GENERIC(bindingindex);
+   vao->Attrib[i].Divisor = divisor;
+
+   if (divisor)
+      vao->NonZeroDivisorMask |= 1u << i;
+   else
+      vao->NonZeroDivisorMask &= ~(1u << i);
+}
+
+void
+_mesa_glthread_BindingDivisor(struct gl_context *ctx, GLuint bindingindex,
+                              GLuint divisor)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+
+   binding_divisor(glthread, glthread->CurrentVAO, bindingindex, divisor);
+}
+
+void
+_mesa_glthread_DSABindingDivisor(struct gl_context *ctx, GLuint vaobj,
+                                 GLuint bindingindex, GLuint divisor)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
+
+   if (vao)
+      binding_divisor(glthread, vao, bindingindex, divisor);
+}
+
+void
+_mesa_glthread_AttribBinding(struct gl_context *ctx, GLuint attribindex,
+                             GLuint bindingindex)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+
+   if (attribindex >= VERT_ATTRIB_GENERIC_MAX ||
+       bindingindex >= VERT_ATTRIB_GENERIC_MAX)
+      return;
+
+   set_attrib_binding(glthread, glthread->CurrentVAO,
+                      VERT_ATTRIB_GENERIC(attribindex),
+                      VERT_ATTRIB_GENERIC(bindingindex));
+}
+
+void
+_mesa_glthread_DSAAttribBinding(struct gl_context *ctx, GLuint vaobj,
+                                GLuint attribindex, GLuint bindingindex)
+{
+   struct glthread_state *glthread = &ctx->GLThread;
+
+   if (attribindex >= VERT_ATTRIB_GENERIC_MAX ||
+       bindingindex >= VERT_ATTRIB_GENERIC_MAX)
+      return;
+
+   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
+   if (vao) {
+      set_attrib_binding(glthread, vao,
+                         VERT_ATTRIB_GENERIC(attribindex),
+                         VERT_ATTRIB_GENERIC(bindingindex));
+   }
+}
+
+void
+_mesa_glthread_DSAElementBuffer(struct gl_context *ctx, GLuint vaobj,
+                                GLuint buffer)
+{
+   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
+
+   if (vao)
+      vao->CurrentElementBufferName = buffer;
+}
+
 void
 _mesa_glthread_PushClientAttrib(struct gl_context *ctx, GLbitfield mask,
                                 bool set_default)