+ /* Limit used for common binding scanning below. */
+ const GLsizeiptr MaxRelativeOffset =
+ ctx->Const.MaxVertexAttribRelativeOffset;
+
+ /* The gl_vertex_array_object::_AttributeMapMode denotes the way
+ * VERT_ATTRIB_{POS,GENERIC0} mapping is done.
+ *
+ * This mapping is used to map between the OpenGL api visible
+ * VERT_ATTRIB_* arrays to mesa driver arrayinputs or shader inputs.
+ * The mapping only depends on the enabled bits of the
+ * VERT_ATTRIB_{POS,GENERIC0} arrays and is tracked in the VAO.
+ *
+ * This map needs to be applied when finally translating to the bitmasks
+ * as consumed by the driver backends. The duplicate scanning is here
+ * can as well be done in the OpenGL API numbering without this map.
+ */
+ const gl_attribute_map_mode mode = vao->_AttributeMapMode;
+ /* Enabled array bits. */
+ const GLbitfield enabled = vao->_Enabled;
+ /* VBO array bits. */
+ const GLbitfield vbos = vao->VertexAttribBufferMask;
+
+ /* Compute and store effectively enabled and mapped vbo arrays */
+ vao->_EffEnabledVBO = _mesa_vao_enable_to_vp_inputs(mode, enabled & vbos);
+ /* Walk those enabled arrays that have a real vbo attached */
+ GLbitfield mask = enabled;
+ while (mask) {
+ /* Do not use u_bit_scan as we can walk multiple attrib arrays at once */
+ const int i = ffs(mask) - 1;
+ /* The binding from the first to be processed attribute. */
+ const GLuint bindex = vao->VertexAttrib[i].BufferBindingIndex;
+ struct gl_vertex_buffer_binding *binding = &vao->BufferBinding[bindex];
+
+ /* The scan goes different for user space arrays than vbos */
+ if (_mesa_is_bufferobj(binding->BufferObj)) {
+ /* The bound arrays. */
+ const GLbitfield bound = enabled & binding->_BoundArrays;
+
+ /* Start this current effective binding with the actual bound arrays */
+ GLbitfield eff_bound_arrays = bound;
+
+ /*
+ * If there is nothing left to scan just update the effective binding
+ * information. If the VAO is already only using a single binding point
+ * we end up here. So the overhead of this scan for an application
+ * carefully preparing the VAO for draw is low.
+ */
+
+ GLbitfield scanmask = mask & vbos & ~bound;
+ /* Is there something left to scan? */
+ if (scanmask == 0) {
+ /* Just update the back reference from the attrib to the binding and
+ * the effective offset.
+ */
+ GLbitfield attrmask = eff_bound_arrays;
+ while (attrmask) {
+ const int j = u_bit_scan(&attrmask);
+ struct gl_array_attributes *attrib2 = &vao->VertexAttrib[j];
+
+ /* Update the index into the common binding point and offset */
+ attrib2->_EffBufferBindingIndex = bindex;
+ attrib2->_EffRelativeOffset = attrib2->RelativeOffset;
+ assert(attrib2->_EffRelativeOffset <= MaxRelativeOffset);
+
+ /* Only enabled arrays shall appear in the unique bindings */
+ assert(attrib2->Enabled);
+ }
+ /* Finally this is the set of effectively bound arrays with the
+ * original binding offset.
+ */
+ binding->_EffOffset = binding->Offset;
+ /* The bound arrays past the VERT_ATTRIB_{POS,GENERIC0} mapping. */
+ binding->_EffBoundArrays =
+ _mesa_vao_enable_to_vp_inputs(mode, eff_bound_arrays);
+
+ } else {
+ /* In the VBO case, scan for attribute/binding
+ * combinations with relative bindings in the range of
+ * [0, ctx->Const.MaxVertexAttribRelativeOffset].
+ * Note that this does also go beyond just interleaved arrays
+ * as long as they use the same VBO, binding parameters and the
+ * offsets stay within bounds that the backend still can handle.
+ */
+
+ GLsizeiptr min_offset, max_offset;
+ compute_vbo_offset_range(vao, binding, &min_offset, &max_offset);
+ assert(max_offset <= min_offset + MaxRelativeOffset);
+
+ /* Now scan. */
+ while (scanmask) {
+ /* Do not use u_bit_scan as we can walk multiple
+ * attrib arrays at once
+ */
+ const int j = ffs(scanmask) - 1;
+ const struct gl_array_attributes *attrib2 =
+ &vao->VertexAttrib[j];
+ const struct gl_vertex_buffer_binding *binding2 =
+ &vao->BufferBinding[attrib2->BufferBindingIndex];
+
+ /* Remove those attrib bits from the mask that are bound to the
+ * same effective binding point.
+ */
+ const GLbitfield bound2 = enabled & binding2->_BoundArrays;
+ scanmask &= ~bound2;
+
+ /* Check if we have an identical binding */
+ if (binding->Stride != binding2->Stride)
+ continue;
+ if (binding->InstanceDivisor != binding2->InstanceDivisor)
+ continue;
+ if (binding->BufferObj != binding2->BufferObj)
+ continue;
+ /* Check if we can fold both bindings into a common binding */
+ GLsizeiptr min_offset2, max_offset2;
+ compute_vbo_offset_range(vao, binding2,
+ &min_offset2, &max_offset2);
+ /* If the relative offset is within the limits ... */
+ if (min_offset + MaxRelativeOffset < max_offset2)
+ continue;
+ if (min_offset2 + MaxRelativeOffset < max_offset)
+ continue;
+ /* ... add this array to the effective binding */
+ eff_bound_arrays |= bound2;
+ min_offset = MIN2(min_offset, min_offset2);
+ max_offset = MAX2(max_offset, max_offset2);
+ assert(max_offset <= min_offset + MaxRelativeOffset);
+ }
+
+ /* Update the back reference from the attrib to the binding */
+ GLbitfield attrmask = eff_bound_arrays;
+ while (attrmask) {
+ const int j = u_bit_scan(&attrmask);
+ struct gl_array_attributes *attrib2 = &vao->VertexAttrib[j];
+ const struct gl_vertex_buffer_binding *binding2 =
+ &vao->BufferBinding[attrib2->BufferBindingIndex];
+
+ /* Update the index into the common binding point and offset */
+ attrib2->_EffBufferBindingIndex = bindex;
+ attrib2->_EffRelativeOffset =
+ binding2->Offset + attrib2->RelativeOffset - min_offset;
+ assert(attrib2->_EffRelativeOffset <= MaxRelativeOffset);
+
+ /* Only enabled arrays shall appear in the unique bindings */
+ assert(attrib2->Enabled);
+ }
+ /* Finally this is the set of effectively bound arrays */
+ binding->_EffOffset = min_offset;
+ /* The bound arrays past the VERT_ATTRIB_{POS,GENERIC0} mapping. */
+ binding->_EffBoundArrays =
+ _mesa_vao_enable_to_vp_inputs(mode, eff_bound_arrays);
+ }
+
+ /* Mark all the effective bound arrays as processed. */
+ mask &= ~eff_bound_arrays;
+
+ } else {
+ /* Scanning of common bindings for user space arrays.
+ */
+
+ const struct gl_array_attributes *attrib = &vao->VertexAttrib[i];
+ const GLbitfield bound = VERT_BIT(i);
+
+ /* Note that user space array pointers can only happen using a one
+ * to one binding point to array mapping.
+ * The OpenGL 4.x/ARB_vertex_attrib_binding api does not support
+ * user space arrays collected at multiple binding points.
+ * The only provider of user space interleaved arrays with a single
+ * binding point is the mesa internal vbo module. But that one
+ * provides a perfect interleaved set of arrays.
+ *
+ * If this would not be true we would potentially get attribute arrays
+ * with user space pointers that may not lie within the
+ * MaxRelativeOffset range but still attached to a single binding.
+ * Then we would need to store the effective attribute and binding
+ * grouping information in a seperate array beside
+ * gl_array_attributes/gl_vertex_buffer_binding.
+ */
+ assert(util_bitcount(binding->_BoundArrays & vao->_Enabled) == 1
+ || (vao->_Enabled & ~binding->_BoundArrays) == 0);