From: Mathias Fröhlich <mathias.froehl...@web.de> Compute VAO buffer binding information past the position/generic0 mapping. Also scan for duplicate buffer bindings and collapse them into derived effective buffer binding index and effective attribute mask variables. Also provide a set of helper functions to access the distilled information in the VAO. All of them prefixed with _mesa_draw_... to indicate that they are meant to query draw information.
Signed-off-by: Mathias Fröhlich <mathias.froehl...@web.de> --- src/mesa/main/arrayobj.c | 292 ++++++++++++++++++++++++++++++++++++++++++++- src/mesa/main/arrayobj.h | 171 ++++++++++++++++++++++++++ src/mesa/main/attrib.c | 1 + src/mesa/main/mtypes.h | 38 ++++++ src/mesa/main/varray.c | 2 + src/mesa/vbo/vbo.h | 8 ++ src/mesa/vbo/vbo_context.c | 17 +++ 7 files changed, 523 insertions(+), 6 deletions(-) diff --git a/src/mesa/main/arrayobj.c b/src/mesa/main/arrayobj.c index 0d2f7a918a..4ef4046c4b 100644 --- a/src/mesa/main/arrayobj.c +++ b/src/mesa/main/arrayobj.c @@ -452,8 +452,69 @@ _mesa_initialize_vao(struct gl_context *ctx, /** - * Updates the derived gl_vertex_arrays when a gl_array_attributes - * or a gl_vertex_buffer_binding has changed. + * Update the unique binding and pos/generic0 map tracking in the vao. + * + * The idea is to build up information in the vao so that a consuming + * backend can execute the following to set up buffer and vertex element + * information: + * + * const GLbitfield inputs_read = VERT_BIT_ALL; // backend vp inputs + * + * // Attribute data is in a VBO. + * GLbitfield vbomask = inputs_read & _mesa_draw_vbo_array_bits(ctx); + * while (vbomask) { + * // The attribute index to start pulling a binding + * const gl_vert_attrib i = ffs(vbomask) - 1; + * const struct gl_vertex_buffer_binding *const binding + * = _mesa_draw_buffer_binding(vao, i); + * + * <insert code to handle the vertex buffer object at binding> + * + * const GLbitfield boundmask = _mesa_draw_bound_attrib_bits(binding); + * GLbitfield attrmask = vbomask & boundmask; + * assert(attrmask); + * // Walk attributes belonging to the binding + * while (attrmask) { + * const gl_vert_attrib attr = u_bit_scan(&attrmask); + * const struct gl_array_attributes *const attrib + * = _mesa_draw_array_attrib(vao, attr); + * + * <insert code to handle the vertex element refering to the binding> + * } + * vbomask &= ~boundmask; + * } + * + * // Process user space buffers + * GLbitfield usermask = inputs_read & _mesa_draw_user_array_bits(ctx); + * while (usermask) { + * // The attribute index to start pulling a binding + * const gl_vert_attrib i = ffs(usermask) - 1; + * const struct gl_vertex_buffer_binding *const binding + * = _mesa_draw_buffer_binding(vao, i); + * + * const GLbitfield boundmask = _mesa_draw_bound_attrib_bits(binding); + * GLbitfield attrmask = usermask & boundmask; + * assert(attrmask); + * // Walk attributes with a common stride and instance divisor + * while (attrmask) { + * const gl_vert_attrib attr = u_bit_scan(&attrmask); + * const struct gl_array_attributes *const attrib + * = _mesa_draw_array_attrib(vao, attr); + * + * <insert code to handle non vbo vertex arrays> + * } + * usermask &= ~boundmask; + * } + * + * // Process values that should have better been uniforms in the application + * GLbitfield curmask = inputs_read & _mesa_draw_current_bits(ctx); + * while (curmask) { + * const gl_vert_attrib attr = u_bit_scan(&curmask); + * const struct gl_array_attributes *const attrib + * = _mesa_draw_current_attrib(ctx, attr); + * + * <insert code to handle current values> + * } */ void _mesa_update_vao_derived_arrays(struct gl_context *ctx, @@ -462,11 +523,230 @@ _mesa_update_vao_derived_arrays(struct gl_context *ctx, /* Make sure we do not run into problems with shared objects */ assert(!vao->SharedAndImmutable || vao->NewArrays == 0); - /* - * Stay tuned, the next series scans for duplicate bindings in this - * function. So that drivers can easily know the minimum unique set - * of bindings. + /* Limit used for common binding scanning below. */ + const int MaxRelativeOffset = + (0 < ctx->Const.MaxVertexAttribRelativeOffset) ? + ctx->Const.MaxVertexAttribRelativeOffset : 2047; + + /* 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. */ + const gl_attribute_map_mode map_mode = vao->_AttributeMapMode; + const unsigned char *const map =_mesa_vao_attribute_map[map_mode]; + /* Enabled array bits past VERT_ATTRIB_{POS,GENERIC0} mapping. */ + const GLbitfield enabled = + _mesa_vao_enable_to_vp_inputs(map_mode, vao->_Enabled); + /* VBO array bits past VERT_ATTRIB_{POS,GENERIC0} mapping. */ + const GLbitfield vbos = + _mesa_vao_enable_to_vp_inputs(map_mode, vao->VertexAttribBufferMask); + + /* Compute and store effectively enabled and mapped vbo arrays */ + vao->_EffEnabledVBO = 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[map[i]].BufferBindingIndex; + struct gl_vertex_buffer_binding *binding = &vao->BufferBinding[bindex]; + /* The bound arrays past past VERT_ATTRIB_{POS,GENERIC0} mapping. */ + const GLbitfield bound = + _mesa_vao_enable_to_vp_inputs(map_mode, binding->_BoundArrays); + + /* Start this current effective binding with the actual bound arrays */ + GLbitfield eff_bound_arrays = enabled & bound; + + /* The scan goes different for user space arrays than vbos */ + if (_mesa_is_bufferobj(binding->BufferObj)) { + /* + * 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 relatively 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[map[j]]; + + /* Update the index into the common binding point and offset */ + attrib2->_EffBufferBindingIndex = bindex; + attrib2->_EffRelativeOffset = attrib2->RelativeOffset; + + /* 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->_EffBoundArrays = eff_bound_arrays; + binding->_EffOffset = binding->Offset; + + } else { + /* In the VBO case, scan for attribute/binding + * combinations with relative bindings in the range of + * ctx->Const.MaxVertexAttribRelativeOffset. + */ + + /* Start with the range of relative offsets of *binding. */ + GLintptr min_offset = binding->BufferObj->Size; + GLintptr max_offset = 0; + GLbitfield offsetmask = bound & enabled; + while (offsetmask) { + const int k = u_bit_scan(&offsetmask); + const struct gl_array_attributes *attrib + = &vao->VertexAttrib[map[k]]; + const GLintptr off = binding->Offset + attrib->RelativeOffset; + min_offset = MIN2(off, min_offset); + max_offset = MAX2(off, max_offset); + } + + /* 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[map[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 = + _mesa_vao_enable_to_vp_inputs(map_mode, 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; + /* Easy case, just identical binding a second time. */ + if (binding->Offset == binding2->Offset) { + /* Add the bindings from binding2 to the binding mask. */ + eff_bound_arrays |= enabled & bound2; + /* Update the offset range. */ + GLbitfield attrmask = bound2 & enabled; + while (attrmask) { + const int k = u_bit_scan(&attrmask); + const struct gl_array_attributes *attrib2 + = &vao->VertexAttrib[map[k]]; + const GLintptr offset2 = + binding2->Offset + attrib2->RelativeOffset; + min_offset = MIN2(offset2, min_offset); + max_offset = MAX2(offset2, max_offset); + } + } else { + /* Check if we can fold both bindings into a common binding */ + GLbitfield attrmask = bound2 & enabled; + while (attrmask) { + const int k = u_bit_scan(&attrmask); + const struct gl_array_attributes *attrib2 + = &vao->VertexAttrib[map[k]]; + const GLintptr offset2 = + binding2->Offset + attrib2->RelativeOffset; + /* If the relative offset is within the limits ... */ + if (offset2 + MaxRelativeOffset < max_offset) + continue; + if (min_offset + MaxRelativeOffset < offset2) + continue; + /* ... add this array to the effective binding */ + eff_bound_arrays |= 1u << k; + min_offset = MIN2(offset2, min_offset); + max_offset = MAX2(offset2, max_offset); + } + } + } + + /* 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[map[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; + + /* Only enabled arrays shall appear in the unique bindings */ + assert(attrib2->Enabled); + } + /* Finally this is the set of effectively bound arrays */ + binding->_EffBoundArrays = eff_bound_arrays; + binding->_EffOffset = min_offset; + } + + } else { + /* Scanning of common bindings for user space arrays. + * This only checks for common Stride and InstanceDivisor. + */ + GLbitfield scanmask = mask & ~vbos & ~bound; + 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[map[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 = + _mesa_vao_enable_to_vp_inputs(map_mode, binding2->_BoundArrays); + scanmask &= ~bound2; + + /* Check if we have an identical binding */ + if (binding->Stride != binding2->Stride) + continue; + if (binding->InstanceDivisor != binding2->InstanceDivisor) + continue; + /* User space buffer object */ + assert(binding->BufferObj == binding2->BufferObj); + } + + /* 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[map[j]]; + + /* Update the index into the common binding point and the offset */ + attrib2->_EffBufferBindingIndex = bindex; + + /* Only enabled arrays shall appear in the unique bindings */ + assert(attrib2->Enabled); + } + /* Finally this is the set of effectively bound arrays */ + binding->_EffBoundArrays = eff_bound_arrays; + } + + /* Mark all the effective bound arrays as processed. */ + mask &= ~eff_bound_arrays; + } } diff --git a/src/mesa/main/arrayobj.h b/src/mesa/main/arrayobj.h index 8da5c9ffe0..e8857ebc43 100644 --- a/src/mesa/main/arrayobj.h +++ b/src/mesa/main/arrayobj.h @@ -30,6 +30,7 @@ #include "glheader.h" #include "mtypes.h" #include "glformats.h" +#include "vbo/vbo.h" struct gl_context; @@ -146,6 +147,176 @@ _mesa_get_vao_vp_inputs(const struct gl_vertex_array_object *vao) } +/** + * Helper functions for consuming backends to walk the + * ctx->Array._DrawVAO for array setup. + * Note that mesa privides preprocessed minimal binding information + * in the VAO. See the _mesa_update_vao_derived_arrays documentation. + */ + +/** + * Return enabled vertex attribute bits for draw. + */ +static inline GLbitfield +_mesa_draw_array_bits(const struct gl_context *ctx) +{ + return ctx->Array._DrawVAOEnabledAttribs; +} + + +/** + * Return enabled buffer object vertex attribute bits for draw. + * + * Needs the a fully updated VAO ready for draw. + */ +static inline GLbitfield +_mesa_draw_vbo_array_bits(const struct gl_context *ctx) +{ + const struct gl_vertex_array_object *const vao = ctx->Array._DrawVAO; + assert(vao->NewArrays == 0); + return vao->_EffEnabledVBO & ctx->Array._DrawVAOEnabledAttribs; +} + + +/** + * Return enabled user space vertex attribute bits for draw. + * + * Needs the a fully updated VAO ready for draw. + */ +static inline GLbitfield +_mesa_draw_user_array_bits(const struct gl_context *ctx) +{ + const struct gl_vertex_array_object *const vao = ctx->Array._DrawVAO; + assert(vao->NewArrays == 0); + return ~vao->_EffEnabledVBO & ctx->Array._DrawVAOEnabledAttribs; +} + + +/** + * Return enabled current values attribute bits for draw. + */ +static inline GLbitfield +_mesa_draw_current_bits(const struct gl_context *ctx) +{ + return ~ctx->Array._DrawVAOEnabledAttribs & VERT_BIT_ALL; +} + + +/** + * Return vertex buffer binding provided the attribute struct. + * + * Needs the a fully updated VAO ready for draw. + */ +static inline const struct gl_vertex_buffer_binding* +_mesa_draw_buffer_binding_from_attrib(const struct gl_vertex_array_object *vao, + const struct gl_array_attributes *attrib) +{ + assert(vao->NewArrays == 0); + return &vao->BufferBinding[attrib->_EffBufferBindingIndex]; +} + + +/** + * Return vertex array attribute provided the attribute number. + */ +static inline const struct gl_array_attributes* +_mesa_draw_array_attrib(const struct gl_vertex_array_object *vao, + gl_vert_attrib attr) +{ + assert(vao->NewArrays == 0); + const gl_attribute_map_mode map_mode = vao->_AttributeMapMode; + return &vao->VertexAttrib[_mesa_vao_attribute_map[map_mode][attr]]; +} + + +/** + * Return vertex buffer binding provided an attribute number. + */ +static inline const struct gl_vertex_buffer_binding* +_mesa_draw_buffer_binding(const struct gl_vertex_array_object *vao, + gl_vert_attrib attr) +{ + const struct gl_array_attributes *const attrib + = _mesa_draw_array_attrib(vao, attr); + return _mesa_draw_buffer_binding_from_attrib(vao, attrib); +} + + +/** + * Return vertex attribute bits bound at the provided binding. + * + * Needs the a fully updated VAO ready for draw. + */ +static inline GLbitfield +_mesa_draw_bound_attrib_bits(const struct gl_vertex_buffer_binding *binding) +{ + return binding->_EffBoundArrays; +} + + +/** + * Return the vertex offset bound at the provided binding. + * + * Needs the a fully updated VAO ready for draw. + */ +static inline GLintptr +_mesa_draw_binding_offset(const struct gl_vertex_buffer_binding *binding) +{ + return binding->_EffOffset; +} + + +/** + * Return the relative offset of the provided attrib. + * + * Needs the a fully updated VAO ready for draw. + */ +static inline GLushort +_mesa_draw_attributes_relative_offset(const struct gl_array_attributes *attrib) +{ + return attrib->_EffRelativeOffset; +} + + +/** + * Return a current value vertex array attribute provided the attribute number. + */ +static inline const struct gl_array_attributes* +_mesa_draw_current_attrib(const struct gl_context *ctx, gl_vert_attrib attr) +{ + return _vbo_current_attrib(ctx, attr); +} + + +/** + * Return true if we have the VERT_ATTRIB_EDGEFLAG array enabled. + */ +static inline bool +_mesa_draw_edge_flag_array_enabled(const struct gl_context *ctx) +{ + return ctx->Array._DrawVAOEnabledAttribs & VERT_BIT_EDGEFLAG; +} + + +/** + * Return the attrib, binding pair for the given attribute. + */ +static inline void +_mesa_draw_attrib_and_binding(const struct gl_context *ctx, gl_vert_attrib attr, + const struct gl_array_attributes **attrib, + const struct gl_vertex_buffer_binding **binding) +{ + if (ctx->Array._DrawVAOEnabledAttribs & VERT_BIT(attr)) { + const struct gl_vertex_array_object *vao = ctx->Array._DrawVAO; + *attrib = _mesa_draw_array_attrib(vao, attr); + *binding = _mesa_draw_buffer_binding_from_attrib(vao, *attrib); + } else { + *attrib = _vbo_current_attrib(ctx, attr); + *binding = _vbo_current_binding(ctx); + } +} + + /* * API functions */ diff --git a/src/mesa/main/attrib.c b/src/mesa/main/attrib.c index 9c632ffb51..3330e451ce 100644 --- a/src/mesa/main/attrib.c +++ b/src/mesa/main/attrib.c @@ -1513,6 +1513,7 @@ copy_array_object(struct gl_context *ctx, /* _Enabled must be the same than on push */ dest->_Enabled = src->_Enabled; + dest->_EffEnabledVBO = src->_EffEnabledVBO; /* The bitmask of bound VBOs needs to match the VertexBinding array */ dest->VertexAttribBufferMask = src->VertexAttribBufferMask; dest->_AttributeMapMode = src->_AttributeMapMode; diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index b7a7b34a09..ac05a2aa4f 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -1548,6 +1548,23 @@ struct gl_array_attributes unsigned _ElementSize:8; /**< Size of each element in bytes */ /** Index into gl_vertex_array_object::BufferBinding[] array */ unsigned BufferBindingIndex:6; + + /** + * Derived effective buffer binding index + * + * Index into the gl_vertex_buffer_binding array of the vao. + * Similar to BufferBindingIndex, but with the mapping of the + * position/generic0 attributes applied and with identical + * gl_vertex_buffer_binding entries collapsed to a single + * entry within the vao. + */ + unsigned _EffBufferBindingIndex:6; + /** + * Derived effective relative offset. + * + * Relative offset to the effective buffers offset. + */ + GLushort _EffRelativeOffset; }; @@ -1563,6 +1580,24 @@ struct gl_vertex_buffer_binding GLuint InstanceDivisor; /**< GL_ARB_instanced_arrays */ struct gl_buffer_object *BufferObj; /**< GL_ARB_vertex_buffer_object */ GLbitfield _BoundArrays; /**< Arrays bound to this binding point */ + + /** + * Derived effective bound arrays. + * + * The effective binding handles enabled arrays past the + * position/generic0 attribute mapping and reduces the + * refered gl_vertex_buffer_binding entries to a unique + * subset. That shall push scanning for duplicate bindings + * out of the draw loop of VAO's are used multiple times. + */ + GLbitfield _EffBoundArrays; + /** + * Derived offset. + * + * The absolute offset to that we can collapse some attributes + * to this effective binding. + */ + GLintptr _EffOffset; }; @@ -1617,6 +1652,9 @@ struct gl_vertex_array_object /** Mask of VERT_BIT_* values indicating which arrays are enabled */ GLbitfield _Enabled; + /** Mask of VERT_BIT_* enabled arrays past position/generic0 mapping */ + GLbitfield _EffEnabledVBO; + /** Denotes the way the position/generic0 attribute is mapped */ gl_attribute_map_mode _AttributeMapMode; diff --git a/src/mesa/main/varray.c b/src/mesa/main/varray.c index 5df38a14f0..7243d24e9c 100644 --- a/src/mesa/main/varray.c +++ b/src/mesa/main/varray.c @@ -2868,6 +2868,7 @@ _mesa_copy_vertex_attrib_array(struct gl_context *ctx, dst->Ptr = src->Ptr; dst->Enabled = src->Enabled; dst->_ElementSize = src->_ElementSize; + dst->_EffBufferBindingIndex = src->_EffBufferBindingIndex; } void @@ -2879,6 +2880,7 @@ _mesa_copy_vertex_buffer_binding(struct gl_context *ctx, dst->Stride = src->Stride; dst->InstanceDivisor = src->InstanceDivisor; dst->_BoundArrays = src->_BoundArrays; + dst->_EffBoundArrays = src->_EffBoundArrays; _mesa_reference_buffer_object(ctx, &dst->BufferObj, src->BufferObj); } diff --git a/src/mesa/vbo/vbo.h b/src/mesa/vbo/vbo.h index 9b15066291..ca46f9baa7 100644 --- a/src/mesa/vbo/vbo.h +++ b/src/mesa/vbo/vbo.h @@ -186,6 +186,14 @@ void _vbo_update_inputs(struct gl_context *ctx, struct vbo_inputs *inputs); +const struct gl_array_attributes* +_vbo_current_attrib(const struct gl_context *ctx, gl_vert_attrib attr); + + +const struct gl_vertex_buffer_binding* +_vbo_current_binding(const struct gl_context *ctx); + + void GLAPIENTRY _es_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a); diff --git a/src/mesa/vbo/vbo_context.c b/src/mesa/vbo/vbo_context.c index e50cee7a8c..20e180252c 100644 --- a/src/mesa/vbo/vbo_context.c +++ b/src/mesa/vbo/vbo_context.c @@ -233,6 +233,23 @@ _vbo_DestroyContext(struct gl_context *ctx) } +const struct gl_array_attributes* +_vbo_current_attrib(const struct gl_context *ctx, gl_vert_attrib attr) +{ + const struct vbo_context *vbo = vbo_context((struct gl_context *)ctx); + const gl_vertex_processing_mode vmp = ctx->VertexProgram._VPMode; + return &vbo->current[_vbo_attribute_alias_map[vmp][attr]]; +} + + +const struct gl_vertex_buffer_binding* +_vbo_current_binding(const struct gl_context *ctx) +{ + const struct vbo_context *vbo = vbo_context((struct gl_context *)ctx); + return &vbo->binding; +} + + /* * Helper function for _vbo_draw_indirect below that additionally takes a zero * initialized array of _mesa_prim scratch space memory as the last argument. -- 2.14.3 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev