/************************************************************************** * * Copyright 2007 VMware, Inc. * Copyright 2012 Marek Olšák * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ /* * This converts the VBO's vertex attribute/array information into * Gallium vertex state and binds it. * * Authors: * Keith Whitwell * Marek Olšák */ #include "st_context.h" #include "st_atom.h" #include "st_cb_bufferobjects.h" #include "st_draw.h" #include "st_program.h" #include "cso_cache/cso_context.h" #include "util/u_math.h" #include "util/u_upload_mgr.h" #include "main/bufferobj.h" #include "main/glformats.h" #include "main/varray.h" #include "main/arrayobj.h" static void set_velement(struct pipe_vertex_element *velement, int src_offset, int format, int instance_divisor, int vbo_index) { velement->src_offset = src_offset; velement->src_format = format; velement->instance_divisor = instance_divisor; velement->vertex_buffer_index = vbo_index; assert(velement->src_format); } static void init_velement_64bit(const struct st_vertex_program *vp, struct pipe_vertex_element *velements, const struct gl_vertex_format *vformat, int src_offset, int instance_divisor, int vbo_index, int idx) { const GLubyte nr_components = vformat->Size; int lower_format; if (nr_components < 2) lower_format = PIPE_FORMAT_R32G32_UINT; else lower_format = PIPE_FORMAT_R32G32B32A32_UINT; set_velement(&velements[idx], src_offset, lower_format, instance_divisor, vbo_index); idx++; if (idx < vp->num_inputs && vp->index_to_input[idx] == ST_DOUBLE_ATTRIB_PLACEHOLDER) { if (nr_components >= 3) { if (nr_components == 3) lower_format = PIPE_FORMAT_R32G32_UINT; else lower_format = PIPE_FORMAT_R32G32B32A32_UINT; set_velement(&velements[idx], src_offset + 4 * sizeof(float), lower_format, instance_divisor, vbo_index); } else { /* The values here are undefined. Fill in some conservative * dummy values. */ set_velement(&velements[idx], src_offset, PIPE_FORMAT_R32G32_UINT, instance_divisor, vbo_index); } } } /* Always inline the non-64bit element code, so that the compiler can see * that velements is on the stack. */ static void ALWAYS_INLINE init_velement(const struct st_vertex_program *vp, struct pipe_vertex_element *velements, const struct gl_vertex_format *vformat, int src_offset, int instance_divisor, int vbo_index, int idx) { if (!vformat->Doubles) { velements[idx].src_offset = src_offset; velements[idx].src_format = vformat->_PipeFormat; velements[idx].instance_divisor = instance_divisor; velements[idx].vertex_buffer_index = vbo_index; assert(velements[idx].src_format); return; } init_velement_64bit(vp, velements, vformat, src_offset, instance_divisor, vbo_index, idx); } /* ALWAYS_INLINE helps the compiler realize that most of the parameters are * on the stack. */ void #ifndef _MSC_VER /* MSVC doesn't like inlining public functions */ ALWAYS_INLINE #endif st_setup_arrays(struct st_context *st, const struct st_vertex_program *vp, const struct st_common_variant *vp_variant, struct pipe_vertex_element *velements, struct pipe_vertex_buffer *vbuffer, unsigned *num_vbuffers, bool *has_user_vertex_buffers) { struct gl_context *ctx = st->ctx; const struct gl_vertex_array_object *vao = ctx->Array._DrawVAO; const GLbitfield inputs_read = vp_variant->vert_attrib_mask; const ubyte *input_to_index = vp->input_to_index; /* Process attribute array data. */ GLbitfield mask = inputs_read & _mesa_draw_array_bits(ctx); GLbitfield userbuf_attribs = inputs_read & _mesa_draw_user_array_bits(ctx); *has_user_vertex_buffers = userbuf_attribs != 0; st->draw_needs_minmax_index = (userbuf_attribs & ~_mesa_draw_nonzero_divisor_bits(ctx)) != 0; while (mask) { /* The attribute index to start pulling a binding */ const gl_vert_attrib i = ffs(mask) - 1; const struct gl_vertex_buffer_binding *const binding = _mesa_draw_buffer_binding(vao, i); const unsigned bufidx = (*num_vbuffers)++; if (_mesa_is_bufferobj(binding->BufferObj)) { /* Set the binding */ struct st_buffer_object *stobj = st_buffer_object(binding->BufferObj); vbuffer[bufidx].buffer.resource = stobj ? stobj->buffer : NULL; vbuffer[bufidx].is_user_buffer = false; vbuffer[bufidx].buffer_offset = _mesa_draw_binding_offset(binding); } else { /* Set the binding */ const void *ptr = (const void *)_mesa_draw_binding_offset(binding); vbuffer[bufidx].buffer.user = ptr; vbuffer[bufidx].is_user_buffer = true; vbuffer[bufidx].buffer_offset = 0; } vbuffer[bufidx].stride = binding->Stride; /* in bytes */ const GLbitfield boundmask = _mesa_draw_bound_attrib_bits(binding); GLbitfield attrmask = mask & boundmask; /* Mark the those attributes as processed */ mask &= ~boundmask; /* We can assume that we have array for the binding */ assert(attrmask); /* Walk attributes belonging to the binding */ do { const gl_vert_attrib attr = u_bit_scan(&attrmask); const struct gl_array_attributes *const attrib = _mesa_draw_array_attrib(vao, attr); const GLuint off = _mesa_draw_attributes_relative_offset(attrib); init_velement(vp, velements, &attrib->Format, off, binding->InstanceDivisor, bufidx, input_to_index[attr]); } while (attrmask); } } /* ALWAYS_INLINE helps the compiler realize that most of the parameters are * on the stack. * * Return the index of the vertex buffer where current attribs have been * uploaded. */ static int ALWAYS_INLINE st_setup_current(struct st_context *st, const struct st_vertex_program *vp, const struct st_common_variant *vp_variant, struct pipe_vertex_element *velements, struct pipe_vertex_buffer *vbuffer, unsigned *num_vbuffers) { struct gl_context *ctx = st->ctx; const GLbitfield inputs_read = vp_variant->vert_attrib_mask; /* Process values that should have better been uniforms in the application */ GLbitfield curmask = inputs_read & _mesa_draw_current_bits(ctx); if (curmask) { const ubyte *input_to_index = vp->input_to_index; /* For each attribute, upload the maximum possible size. */ GLubyte data[VERT_ATTRIB_MAX * sizeof(GLdouble) * 4]; GLubyte *cursor = data; const unsigned bufidx = (*num_vbuffers)++; unsigned max_alignment = 1; do { const gl_vert_attrib attr = u_bit_scan(&curmask); const struct gl_array_attributes *const attrib = _mesa_draw_current_attrib(ctx, attr); const unsigned size = attrib->Format._ElementSize; const unsigned alignment = util_next_power_of_two(size); max_alignment = MAX2(max_alignment, alignment); memcpy(cursor, attrib->Ptr, size); if (alignment != size) memset(cursor + size, 0, alignment - size); init_velement(vp, velements, &attrib->Format, cursor - data, 0, bufidx, input_to_index[attr]); cursor += alignment; } while (curmask); vbuffer[bufidx].is_user_buffer = false; vbuffer[bufidx].buffer.resource = NULL; /* vbuffer[bufidx].buffer_offset is set below */ vbuffer[bufidx].stride = 0; /* Use const_uploader for zero-stride vertex attributes, because * it may use a better memory placement than stream_uploader. * The reason is that zero-stride attributes can be fetched many * times (thousands of times), so a better placement is going to * perform better. */ struct u_upload_mgr *uploader = st->can_bind_const_buffer_as_vertex ? st->pipe->const_uploader : st->pipe->stream_uploader; u_upload_data(uploader, 0, cursor - data, max_alignment, data, &vbuffer[bufidx].buffer_offset, &vbuffer[bufidx].buffer.resource); /* Always unmap. The uploader might use explicit flushes. */ u_upload_unmap(uploader); return bufidx; } return -1; } void st_setup_current_user(struct st_context *st, const struct st_vertex_program *vp, const struct st_common_variant *vp_variant, struct pipe_vertex_element *velements, struct pipe_vertex_buffer *vbuffer, unsigned *num_vbuffers) { struct gl_context *ctx = st->ctx; const GLbitfield inputs_read = vp_variant->vert_attrib_mask; const ubyte *input_to_index = vp->input_to_index; /* Process values that should have better been uniforms in the application */ GLbitfield curmask = inputs_read & _mesa_draw_current_bits(ctx); /* For each attribute, make an own user buffer binding. */ while (curmask) { const gl_vert_attrib attr = u_bit_scan(&curmask); const struct gl_array_attributes *const attrib = _mesa_draw_current_attrib(ctx, attr); const unsigned bufidx = (*num_vbuffers)++; init_velement(vp, velements, &attrib->Format, 0, 0, bufidx, input_to_index[attr]); vbuffer[bufidx].is_user_buffer = true; vbuffer[bufidx].buffer.user = attrib->Ptr; vbuffer[bufidx].buffer_offset = 0; vbuffer[bufidx].stride = 0; } } void st_update_array(struct st_context *st) { /* vertex program validation must be done before this */ /* _NEW_PROGRAM, ST_NEW_VS_STATE */ const struct st_vertex_program *vp = (struct st_vertex_program *)st->vp; const struct st_common_variant *vp_variant = st->vp_variant; struct pipe_vertex_buffer vbuffer[PIPE_MAX_ATTRIBS]; unsigned num_vbuffers = 0; struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS]; unsigned num_velements; bool uses_user_vertex_buffers; /* ST_NEW_VERTEX_ARRAYS alias ctx->DriverFlags.NewArray */ /* Setup arrays */ st_setup_arrays(st, vp, vp_variant, velements, vbuffer, &num_vbuffers, &uses_user_vertex_buffers); /* _NEW_CURRENT_ATTRIB */ /* Setup zero-stride attribs. */ int current_attrib_buffer = st_setup_current(st, vp, vp_variant, velements, vbuffer, &num_vbuffers); /* Set the array into cso */ num_velements = vp->num_inputs + vp_variant->key.passthrough_edgeflags; /* Set vertex buffers and elements. */ struct cso_context *cso = st->cso_context; unsigned unbind_trailing_vbuffers = st->last_num_vbuffers > num_vbuffers ? st->last_num_vbuffers - num_vbuffers : 0; cso_set_vertex_buffers_and_elements(cso, num_velements, velements, num_vbuffers, unbind_trailing_vbuffers, vbuffer, uses_user_vertex_buffers); st->last_num_vbuffers = num_vbuffers; /* Unreference uploaded current attrib buffer. */ if (current_attrib_buffer >= 0) pipe_resource_reference(&vbuffer[current_attrib_buffer].buffer.resource, NULL); }