mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-24 19:40:10 +01:00
Reviewed-by: Mathias Fröhlich <mathias.froehlich@web.de> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/merge_requests/3829>
311 lines
12 KiB
C
311 lines
12 KiB
C
|
|
/**************************************************************************
|
|
*
|
|
* Copyright 2007 VMware, Inc.
|
|
* Copyright 2012 Marek Olšák <maraeo@gmail.com>
|
|
* 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 <keithw@vmware.com>
|
|
* Marek Olšák <maraeo@gmail.com>
|
|
*/
|
|
|
|
#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 init_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_lowered(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;
|
|
|
|
if (vformat->Doubles) {
|
|
int lower_format;
|
|
|
|
if (nr_components < 2)
|
|
lower_format = PIPE_FORMAT_R32G32_UINT;
|
|
else
|
|
lower_format = PIPE_FORMAT_R32G32B32A32_UINT;
|
|
|
|
init_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;
|
|
|
|
init_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.
|
|
*/
|
|
init_velement(&velements[idx], src_offset, PIPE_FORMAT_R32G32_UINT,
|
|
instance_divisor, vbo_index);
|
|
}
|
|
}
|
|
} else {
|
|
init_velement(&velements[idx], src_offset, vformat->_PipeFormat,
|
|
instance_divisor, vbo_index);
|
|
}
|
|
}
|
|
|
|
/* ALWAYS_INLINE helps the compiler realize that most of the parameters are
|
|
* on the stack.
|
|
*/
|
|
void ALWAYS_INLINE
|
|
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;
|
|
bool uses_user_vertex_buffers = false;
|
|
|
|
/* Process attribute array data. */
|
|
GLbitfield mask = inputs_read & _mesa_draw_array_bits(ctx);
|
|
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;
|
|
|
|
uses_user_vertex_buffers = true;
|
|
if (!binding->InstanceDivisor)
|
|
st->draw_needs_minmax_index = true;
|
|
}
|
|
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 */
|
|
while (attrmask) {
|
|
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_lowered(vp, velements, &attrib->Format, off,
|
|
binding->InstanceDivisor, bufidx,
|
|
input_to_index[attr]);
|
|
}
|
|
}
|
|
*has_user_vertex_buffers = uses_user_vertex_buffers;
|
|
}
|
|
|
|
/* ALWAYS_INLINE helps the compiler realize that most of the parameters are
|
|
* on the stack.
|
|
*/
|
|
void 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;
|
|
|
|
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 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_lowered(vp, velements, &attrib->Format, cursor - data, 0,
|
|
bufidx, input_to_index[attr]);
|
|
|
|
cursor += alignment;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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_lowered(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, first_upload_vbuffer;
|
|
struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS];
|
|
unsigned num_velements;
|
|
bool uses_user_vertex_buffers;
|
|
|
|
st->draw_needs_minmax_index = false;
|
|
|
|
/* 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 current uploads */
|
|
first_upload_vbuffer = num_vbuffers;
|
|
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 buffer resources. */
|
|
for (unsigned i = first_upload_vbuffer; i < num_vbuffers; ++i) {
|
|
pipe_resource_reference(&vbuffer[i].buffer.resource, NULL);
|
|
}
|
|
}
|