X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fstate_tracker%2Fst_draw.c;h=a8c20f45acda0f58e9a20347a5a85b84eefb2058;hb=bb4c5d72d7c7cb1d9e7016e2c07c36875f30011a;hp=318e08886c7e6cb73af2a37b771f7a5bd67b75b6;hpb=1218430e1200a08cd64b6555d3fd1fd0274ad9e5;p=mesa.git diff --git a/src/mesa/state_tracker/st_draw.c b/src/mesa/state_tracker/st_draw.c index 318e08886c7..a8c20f45acd 100644 --- a/src/mesa/state_tracker/st_draw.c +++ b/src/mesa/state_tracker/st_draw.c @@ -33,7 +33,7 @@ * * We basically convert the VBO's vertex attribute/array information into * Gallium vertex state, bind the vertex buffer objects and call - * pipe->draw_elements(), pipe->draw_range_elements() or pipe->draw_arrays(). + * pipe->draw_vbo(). * * Authors: * Keith Whitwell @@ -42,14 +42,16 @@ #include "main/imports.h" #include "main/image.h" +#include "main/bufferobj.h" #include "main/macros.h" -#include "program/prog_uniform.h" +#include "main/mfeatures.h" #include "vbo/vbo.h" #include "st_context.h" #include "st_atom.h" #include "st_cb_bufferobjects.h" +#include "st_cb_xformfb.h" #include "st_draw.h" #include "st_program.h" @@ -59,9 +61,12 @@ #include "util/u_format.h" #include "util/u_prim.h" #include "util/u_draw_quad.h" +#include "util/u_upload_mgr.h" #include "draw/draw_context.h" #include "cso_cache/cso_context.h" +#include "../glsl/ir_uniform.h" + static GLuint double_types[4] = { PIPE_FORMAT_R64_FLOAT, @@ -98,6 +103,13 @@ static GLuint uint_types_scale[4] = { PIPE_FORMAT_R32G32B32A32_USCALED }; +static GLuint uint_types_int[4] = { + PIPE_FORMAT_R32_UINT, + PIPE_FORMAT_R32G32_UINT, + PIPE_FORMAT_R32G32B32_UINT, + PIPE_FORMAT_R32G32B32A32_UINT +}; + static GLuint int_types_norm[4] = { PIPE_FORMAT_R32_SNORM, PIPE_FORMAT_R32G32_SNORM, @@ -112,6 +124,13 @@ static GLuint int_types_scale[4] = { PIPE_FORMAT_R32G32B32A32_SSCALED }; +static GLuint int_types_int[4] = { + PIPE_FORMAT_R32_SINT, + PIPE_FORMAT_R32G32_SINT, + PIPE_FORMAT_R32G32B32_SINT, + PIPE_FORMAT_R32G32B32A32_SINT +}; + static GLuint ushort_types_norm[4] = { PIPE_FORMAT_R16_UNORM, PIPE_FORMAT_R16G16_UNORM, @@ -126,6 +145,13 @@ static GLuint ushort_types_scale[4] = { PIPE_FORMAT_R16G16B16A16_USCALED }; +static GLuint ushort_types_int[4] = { + PIPE_FORMAT_R16_UINT, + PIPE_FORMAT_R16G16_UINT, + PIPE_FORMAT_R16G16B16_UINT, + PIPE_FORMAT_R16G16B16A16_UINT +}; + static GLuint short_types_norm[4] = { PIPE_FORMAT_R16_SNORM, PIPE_FORMAT_R16G16_SNORM, @@ -140,6 +166,13 @@ static GLuint short_types_scale[4] = { PIPE_FORMAT_R16G16B16A16_SSCALED }; +static GLuint short_types_int[4] = { + PIPE_FORMAT_R16_SINT, + PIPE_FORMAT_R16G16_SINT, + PIPE_FORMAT_R16G16B16_SINT, + PIPE_FORMAT_R16G16B16A16_SINT +}; + static GLuint ubyte_types_norm[4] = { PIPE_FORMAT_R8_UNORM, PIPE_FORMAT_R8G8_UNORM, @@ -154,6 +187,13 @@ static GLuint ubyte_types_scale[4] = { PIPE_FORMAT_R8G8B8A8_USCALED }; +static GLuint ubyte_types_int[4] = { + PIPE_FORMAT_R8_UINT, + PIPE_FORMAT_R8G8_UINT, + PIPE_FORMAT_R8G8B8_UINT, + PIPE_FORMAT_R8G8B8A8_UINT +}; + static GLuint byte_types_norm[4] = { PIPE_FORMAT_R8_SNORM, PIPE_FORMAT_R8G8_SNORM, @@ -168,6 +208,13 @@ static GLuint byte_types_scale[4] = { PIPE_FORMAT_R8G8B8A8_SSCALED }; +static GLuint byte_types_int[4] = { + PIPE_FORMAT_R8_SINT, + PIPE_FORMAT_R8G8_SINT, + PIPE_FORMAT_R8G8B8_SINT, + PIPE_FORMAT_R8G8B8A8_SINT +}; + static GLuint fixed_types[4] = { PIPE_FORMAT_R32_FIXED, PIPE_FORMAT_R32G32_FIXED, @@ -180,16 +227,50 @@ static GLuint fixed_types[4] = { /** * Return a PIPE_FORMAT_x for the given GL datatype and size. */ -GLuint +enum pipe_format st_pipe_vertex_format(GLenum type, GLuint size, GLenum format, - GLboolean normalized) + GLboolean normalized, GLboolean integer) { assert((type >= GL_BYTE && type <= GL_DOUBLE) || - type == GL_FIXED || type == GL_HALF_FLOAT); + type == GL_FIXED || type == GL_HALF_FLOAT || + type == GL_INT_2_10_10_10_REV || + type == GL_UNSIGNED_INT_2_10_10_10_REV); assert(size >= 1); assert(size <= 4); assert(format == GL_RGBA || format == GL_BGRA); + if (type == GL_INT_2_10_10_10_REV || + type == GL_UNSIGNED_INT_2_10_10_10_REV) { + assert(size == 4); + assert(!integer); + + if (format == GL_BGRA) { + if (type == GL_INT_2_10_10_10_REV) { + if (normalized) + return PIPE_FORMAT_B10G10R10A2_SNORM; + else + return PIPE_FORMAT_B10G10R10A2_SSCALED; + } else { + if (normalized) + return PIPE_FORMAT_B10G10R10A2_UNORM; + else + return PIPE_FORMAT_B10G10R10A2_USCALED; + } + } else { + if (type == GL_INT_2_10_10_10_REV) { + if (normalized) + return PIPE_FORMAT_R10G10B10A2_SNORM; + else + return PIPE_FORMAT_R10G10B10A2_SSCALED; + } else { + if (normalized) + return PIPE_FORMAT_R10G10B10A2_UNORM; + else + return PIPE_FORMAT_R10G10B10A2_USCALED; + } + } + } + if (format == GL_BGRA) { /* this is an odd-ball case */ assert(type == GL_UNSIGNED_BYTE); @@ -197,7 +278,18 @@ st_pipe_vertex_format(GLenum type, GLuint size, GLenum format, return PIPE_FORMAT_B8G8R8A8_UNORM; } - if (normalized) { + if (integer) { + switch (type) { + case GL_INT: return int_types_int[size-1]; + case GL_SHORT: return short_types_int[size-1]; + case GL_BYTE: return byte_types_int[size-1]; + case GL_UNSIGNED_INT: return uint_types_int[size-1]; + case GL_UNSIGNED_SHORT: return ushort_types_int[size-1]; + case GL_UNSIGNED_BYTE: return ubyte_types_int[size-1]; + default: assert(0); return 0; + } + } + else if (normalized) { switch (type) { case GL_DOUBLE: return double_types[size-1]; case GL_FLOAT: return float_types[size-1]; @@ -210,7 +302,7 @@ st_pipe_vertex_format(GLenum type, GLuint size, GLenum format, case GL_UNSIGNED_BYTE: return ubyte_types_norm[size-1]; case GL_FIXED: return fixed_types[size-1]; default: assert(0); return 0; - } + } } else { switch (type) { @@ -225,112 +317,76 @@ st_pipe_vertex_format(GLenum type, GLuint size, GLenum format, case GL_UNSIGNED_BYTE: return ubyte_types_scale[size-1]; case GL_FIXED: return fixed_types[size-1]; default: assert(0); return 0; - } + } } - return 0; /* silence compiler warning */ + return PIPE_FORMAT_NONE; /* silence compiler warning */ } +/** + * This is very similar to vbo_all_varyings_in_vbos() but we are + * only interested in per-vertex data. See bug 38626. + */ +static GLboolean +all_varyings_in_vbos(const struct gl_client_array *arrays[]) +{ + GLuint i; + + for (i = 0; i < VERT_ATTRIB_MAX; i++) + if (arrays[i]->StrideB && + !arrays[i]->InstanceDivisor && + !_mesa_is_bufferobj(arrays[i]->BufferObj)) + return GL_FALSE; + return GL_TRUE; +} /** * Examine the active arrays to determine if we have interleaved * vertex arrays all living in one VBO, or all living in user space. - * \param userSpace returns whether the arrays are in user space. */ static GLboolean is_interleaved_arrays(const struct st_vertex_program *vp, - const struct st_vp_varient *vpv, - const struct gl_client_array **arrays, - GLboolean *userSpace) + const struct st_vp_variant *vpv, + const struct gl_client_array **arrays) { GLuint attr; const struct gl_buffer_object *firstBufObj = NULL; GLint firstStride = -1; - GLuint num_client_arrays = 0; - const GLubyte *client_addr = NULL; + const GLubyte *firstPtr = NULL; + GLboolean userSpaceBuffer = GL_FALSE; for (attr = 0; attr < vpv->num_inputs; attr++) { const GLuint mesaAttr = vp->index_to_input[attr]; - const struct gl_buffer_object *bufObj = arrays[mesaAttr]->BufferObj; - const GLsizei stride = arrays[mesaAttr]->StrideB; /* in bytes */ + const struct gl_client_array *array = arrays[mesaAttr]; + const struct gl_buffer_object *bufObj = array->BufferObj; + const GLsizei stride = array->StrideB; /* in bytes */ - if (firstStride < 0) { + if (attr == 0) { + /* save info about the first array */ firstStride = stride; - } - else if (firstStride != stride) { - return GL_FALSE; - } - - if (!bufObj || !bufObj->Name) { - num_client_arrays++; - /* Try to detect if the client-space arrays are - * "close" to each other. - */ - if (!client_addr) { - client_addr = arrays[mesaAttr]->Ptr; - } - else if (abs(arrays[mesaAttr]->Ptr - client_addr) > firstStride) { - /* arrays start too far apart */ - return GL_FALSE; - } - } - else if (!firstBufObj) { + firstPtr = array->Ptr; firstBufObj = bufObj; + userSpaceBuffer = !bufObj || !bufObj->Name; } - else if (bufObj != firstBufObj) { - return GL_FALSE; - } - } - - *userSpace = (num_client_arrays == vpv->num_inputs); - /* debug_printf("user space: %s (%d arrays, %d inputs)\n", - (int)*userSpace ? "Yes" : "No", num_client_arrays, vp->num_inputs); */ - - return GL_TRUE; -} - - -/** - * Compute the memory range occupied by the arrays. - */ -static void -get_arrays_bounds(const struct st_vertex_program *vp, - const struct st_vp_varient *vpv, - const struct gl_client_array **arrays, - GLuint max_index, - const GLubyte **low, const GLubyte **high) -{ - const GLubyte *low_addr = NULL; - const GLubyte *high_addr = NULL; - GLuint attr; - - /* debug_printf("get_arrays_bounds: Handling %u attrs\n", vpv->num_inputs); */ + else { + /* check if other arrays interleave with the first, in same buffer */ + if (stride != firstStride) + return GL_FALSE; /* strides don't match */ - for (attr = 0; attr < vpv->num_inputs; attr++) { - const GLuint mesaAttr = vp->index_to_input[attr]; - const GLint stride = arrays[mesaAttr]->StrideB; - const GLubyte *start = arrays[mesaAttr]->Ptr; - const unsigned sz = (arrays[mesaAttr]->Size * - _mesa_sizeof_type(arrays[mesaAttr]->Type)); - const GLubyte *end = start + (max_index * stride) + sz; + if (bufObj != firstBufObj) + return GL_FALSE; /* arrays in different VBOs */ - /* debug_printf("attr %u: stride %d size %u start %p end %p\n", - attr, stride, sz, start, end); */ + if (abs(array->Ptr - firstPtr) > firstStride) + return GL_FALSE; /* arrays start too far apart */ - if (attr == 0) { - low_addr = start; - high_addr = end; - } - else { - low_addr = MIN2(low_addr, start); - high_addr = MAX2(high_addr, end); + if ((!_mesa_is_bufferobj(bufObj)) != userSpaceBuffer) + return GL_FALSE; /* mix of VBO and user-space arrays */ } } - *low = low_addr; - *high = high_addr; + return GL_TRUE; } @@ -339,64 +395,105 @@ get_arrays_bounds(const struct st_vertex_program *vp, * or all live in user space. * \param vbuffer returns vertex buffer info * \param velements returns vertex element info + * \return GL_TRUE for success, GL_FALSE otherwise (probably out of memory) */ -static void -setup_interleaved_attribs(GLcontext *ctx, +static GLboolean +setup_interleaved_attribs(struct gl_context *ctx, const struct st_vertex_program *vp, - const struct st_vp_varient *vpv, + const struct st_vp_variant *vpv, const struct gl_client_array **arrays, - GLuint max_index, - GLboolean userSpace, struct pipe_vertex_buffer *vbuffer, struct pipe_vertex_element velements[]) { - struct st_context *st = st_context(ctx); - struct pipe_context *pipe = st->pipe; GLuint attr; - const GLubyte *offset0 = NULL; + const GLubyte *low_addr = NULL; + GLboolean usingVBO; /* all arrays in a VBO? */ + struct gl_buffer_object *bufobj; + GLsizei stride; - for (attr = 0; attr < vpv->num_inputs; attr++) { - const GLuint mesaAttr = vp->index_to_input[attr]; - struct gl_buffer_object *bufobj = arrays[mesaAttr]->BufferObj; - struct st_buffer_object *stobj = st_buffer_object(bufobj); - GLsizei stride = arrays[mesaAttr]->StrideB; + /* Find the lowest address of the arrays we're drawing, + * Init bufobj and stride. + */ + if (vpv->num_inputs) { + const GLuint mesaAttr0 = vp->index_to_input[0]; + const struct gl_client_array *array = arrays[mesaAttr0]; - /*printf("stobj %u = %p\n", attr, (void*)stobj);*/ + /* Since we're doing interleaved arrays, we know there'll be at most + * one buffer object and the stride will be the same for all arrays. + * Grab them now. + */ + bufobj = array->BufferObj; + stride = array->StrideB; - if (attr == 0) { - const GLubyte *low, *high; - - get_arrays_bounds(vp, vpv, arrays, max_index, &low, &high); - /* debug_printf("buffer range: %p %p range %d max index %u\n", - low, high, high - low, max_index); */ - - offset0 = low; - if (userSpace) { - vbuffer->buffer = - pipe_user_buffer_create(pipe->screen, (void *) low, high - low, - PIPE_BIND_VERTEX_BUFFER); - vbuffer->buffer_offset = 0; - } - else { - vbuffer->buffer = NULL; - pipe_resource_reference(&vbuffer->buffer, stobj->buffer); - vbuffer->buffer_offset = pointer_to_offset(low); - } - vbuffer->stride = stride; /* in bytes */ - vbuffer->max_index = max_index; + low_addr = arrays[vp->index_to_input[0]]->Ptr; + + for (attr = 1; attr < vpv->num_inputs; attr++) { + const GLubyte *start = arrays[vp->index_to_input[attr]]->Ptr; + low_addr = MIN2(low_addr, start); } + } + else { + /* not sure we'll ever have zero inputs, but play it safe */ + bufobj = NULL; + stride = 0; + low_addr = 0; + } - velements[attr].src_offset = - (unsigned) (arrays[mesaAttr]->Ptr - offset0); - velements[attr].instance_divisor = 0; + /* are the arrays in user space? */ + usingVBO = _mesa_is_bufferobj(bufobj); + + for (attr = 0; attr < vpv->num_inputs; attr++) { + const GLuint mesaAttr = vp->index_to_input[attr]; + const struct gl_client_array *array = arrays[mesaAttr]; + unsigned src_offset = (unsigned) (array->Ptr - low_addr); + GLuint element_size = array->_ElementSize; + + assert(element_size == array->Size * _mesa_sizeof_type(array->Type)); + + velements[attr].src_offset = src_offset; + velements[attr].instance_divisor = array->InstanceDivisor; velements[attr].vertex_buffer_index = 0; - velements[attr].src_format = - st_pipe_vertex_format(arrays[mesaAttr]->Type, - arrays[mesaAttr]->Size, - arrays[mesaAttr]->Format, - arrays[mesaAttr]->Normalized); + velements[attr].src_format = st_pipe_vertex_format(array->Type, + array->Size, + array->Format, + array->Normalized, + array->Integer); assert(velements[attr].src_format); } + + /* + * Return the vbuffer info and setup user-space attrib info, if needed. + */ + if (vpv->num_inputs == 0) { + /* just defensive coding here */ + vbuffer->buffer = NULL; + vbuffer->user_buffer = NULL; + vbuffer->buffer_offset = 0; + vbuffer->stride = 0; + } + else if (usingVBO) { + /* all interleaved arrays in a VBO */ + struct st_buffer_object *stobj = st_buffer_object(bufobj); + + if (!stobj || !stobj->buffer) { + /* probably out of memory (or zero-sized buffer) */ + return GL_FALSE; + } + + vbuffer->buffer = stobj->buffer; + vbuffer->user_buffer = NULL; + vbuffer->buffer_offset = pointer_to_offset(low_addr); + vbuffer->stride = stride; + } + else { + /* all interleaved arrays in user memory */ + vbuffer->buffer = NULL; + vbuffer->user_buffer = low_addr; + vbuffer->buffer_offset = 0; + vbuffer->stride = stride; + } + + return GL_TRUE; } @@ -405,160 +502,308 @@ setup_interleaved_attribs(GLcontext *ctx, * vertex attribute. * \param vbuffer returns vertex buffer info * \param velements returns vertex element info + * \return GL_TRUE for success, GL_FALSE otherwise (probably out of memory) */ -static void -setup_non_interleaved_attribs(GLcontext *ctx, +static GLboolean +setup_non_interleaved_attribs(struct gl_context *ctx, const struct st_vertex_program *vp, - const struct st_vp_varient *vpv, + const struct st_vp_variant *vpv, const struct gl_client_array **arrays, - GLuint max_index, - GLboolean *userSpace, struct pipe_vertex_buffer vbuffer[], struct pipe_vertex_element velements[]) { - struct st_context *st = st_context(ctx); - struct pipe_context *pipe = st->pipe; GLuint attr; for (attr = 0; attr < vpv->num_inputs; attr++) { const GLuint mesaAttr = vp->index_to_input[attr]; - struct gl_buffer_object *bufobj = arrays[mesaAttr]->BufferObj; - GLsizei stride = arrays[mesaAttr]->StrideB; + const struct gl_client_array *array = arrays[mesaAttr]; + struct gl_buffer_object *bufobj = array->BufferObj; + GLsizei stride = array->StrideB; - *userSpace = GL_FALSE; + assert(array->_ElementSize == array->Size * _mesa_sizeof_type(array->Type)); - if (bufobj && bufobj->Name) { + if (_mesa_is_bufferobj(bufobj)) { /* Attribute data is in a VBO. * Recall that for VBOs, the gl_client_array->Ptr field is * really an offset from the start of the VBO, not a pointer. */ struct st_buffer_object *stobj = st_buffer_object(bufobj); - assert(stobj->buffer); - /*printf("stobj %u = %p\n", attr, (void*) stobj);*/ - vbuffer[attr].buffer = NULL; - pipe_resource_reference(&vbuffer[attr].buffer, stobj->buffer); - vbuffer[attr].buffer_offset = pointer_to_offset(arrays[mesaAttr]->Ptr); - velements[attr].src_offset = 0; + if (!stobj || !stobj->buffer) { + /* probably out of memory (or zero-sized buffer) */ + return GL_FALSE; + } + + vbuffer[attr].buffer = stobj->buffer; + vbuffer[attr].user_buffer = NULL; + vbuffer[attr].buffer_offset = pointer_to_offset(array->Ptr); } else { - /* attribute data is in user-space memory, not a VBO */ - uint bytes; - /*printf("user-space array %d stride %d\n", attr, stride);*/ - - *userSpace = GL_TRUE; - /* wrap user data */ - if (arrays[mesaAttr]->Ptr) { - /* user's vertex array */ - if (arrays[mesaAttr]->StrideB) { - bytes = arrays[mesaAttr]->StrideB * (max_index + 1); - } - else { - bytes = arrays[mesaAttr]->Size - * _mesa_sizeof_type(arrays[mesaAttr]->Type); - } - vbuffer[attr].buffer = - pipe_user_buffer_create(pipe->screen, - (void *) arrays[mesaAttr]->Ptr, bytes, - PIPE_BIND_VERTEX_BUFFER); + void *ptr; + + if (array->Ptr) { + ptr = (void *) array->Ptr; } else { /* no array, use ctx->Current.Attrib[] value */ - bytes = sizeof(ctx->Current.Attrib[0]); - vbuffer[attr].buffer = - pipe_user_buffer_create(pipe->screen, - (void *) ctx->Current.Attrib[mesaAttr], - bytes, - PIPE_BIND_VERTEX_BUFFER); + ptr = (void *) ctx->Current.Attrib[mesaAttr]; stride = 0; } + assert(ptr); + + vbuffer[attr].buffer = NULL; + vbuffer[attr].user_buffer = ptr; vbuffer[attr].buffer_offset = 0; - velements[attr].src_offset = 0; } - assert(velements[attr].src_offset <= 2048); /* 11-bit field */ - /* common-case setup */ vbuffer[attr].stride = stride; /* in bytes */ - vbuffer[attr].max_index = max_index; - velements[attr].instance_divisor = 0; + + velements[attr].src_offset = 0; + velements[attr].instance_divisor = array->InstanceDivisor; velements[attr].vertex_buffer_index = attr; - velements[attr].src_format - = st_pipe_vertex_format(arrays[mesaAttr]->Type, - arrays[mesaAttr]->Size, - arrays[mesaAttr]->Format, - arrays[mesaAttr]->Normalized); + velements[attr].src_format = st_pipe_vertex_format(array->Type, + array->Size, + array->Format, + array->Normalized, + array->Integer); assert(velements[attr].src_format); } + + return GL_TRUE; } static void -setup_index_buffer(GLcontext *ctx, +setup_index_buffer(struct st_context *st, const struct _mesa_index_buffer *ib, struct pipe_index_buffer *ibuffer) { - struct st_context *st = st_context(ctx); - struct pipe_context *pipe = st->pipe; + struct gl_buffer_object *bufobj = ib->obj; - memset(ibuffer, 0, sizeof(*ibuffer)); - if (ib) { - struct gl_buffer_object *bufobj = ib->obj; - - switch (ib->type) { - case GL_UNSIGNED_INT: - ibuffer->index_size = 4; - break; - case GL_UNSIGNED_SHORT: - ibuffer->index_size = 2; - break; - case GL_UNSIGNED_BYTE: - ibuffer->index_size = 1; - break; - default: - assert(0); - return; - } + ibuffer->index_size = vbo_sizeof_ib_type(ib->type); - /* get/create the index buffer object */ - if (bufobj && bufobj->Name) { - /* elements/indexes are in a real VBO */ - struct st_buffer_object *stobj = st_buffer_object(bufobj); - pipe_resource_reference(&ibuffer->buffer, stobj->buffer); - ibuffer->offset = pointer_to_offset(ib->ptr); - } - else { - /* element/indicies are in user space memory */ - ibuffer->buffer = - pipe_user_buffer_create(pipe->screen, (void *) ib->ptr, - ib->count * ibuffer->index_size, - PIPE_BIND_INDEX_BUFFER); - } + /* get/create the index buffer object */ + if (_mesa_is_bufferobj(bufobj)) { + /* indices are in a real VBO */ + ibuffer->buffer = st_buffer_object(bufobj)->buffer; + ibuffer->offset = pointer_to_offset(ib->ptr); } + else if (st->indexbuf_uploader) { + u_upload_data(st->indexbuf_uploader, 0, ib->count * ibuffer->index_size, + ib->ptr, &ibuffer->offset, &ibuffer->buffer); + u_upload_unmap(st->indexbuf_uploader); + } + else { + /* indices are in user space memory */ + ibuffer->user_buffer = ib->ptr; + } + + cso_set_index_buffer(st->cso_context, ibuffer); } + /** * Prior to drawing, check that any uniforms referenced by the * current shader have been set. If a uniform has not been set, * issue a warning. */ static void -check_uniforms(GLcontext *ctx) +check_uniforms(struct gl_context *ctx) { - const struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; - if (shProg && shProg->LinkStatus) { - GLuint i; - for (i = 0; i < shProg->Uniforms->NumUniforms; i++) { - const struct gl_uniform *u = &shProg->Uniforms->Uniforms[i]; - if (!u->Initialized) { + struct gl_shader_program *shProg[3] = { + ctx->Shader.CurrentVertexProgram, + ctx->Shader.CurrentGeometryProgram, + ctx->Shader.CurrentFragmentProgram, + }; + unsigned j; + + for (j = 0; j < 3; j++) { + unsigned i; + + if (shProg[j] == NULL || !shProg[j]->LinkStatus) + continue; + + for (i = 0; i < shProg[j]->NumUserUniformStorage; i++) { + const struct gl_uniform_storage *u = &shProg[j]->UniformStorage[i]; + if (!u->initialized) { _mesa_warning(ctx, "Using shader with uninitialized uniform: %s", - u->Name); + u->name); + } + } + } +} + + +/* + * Notes on primitive restart: + * The code below is used when the gallium driver does not support primitive + * restart itself. We map the index buffer, find the restart indexes, unmap + * the index buffer then draw the sub-primitives delineated by the restarts. + * A couple possible optimizations: + * 1. Save the list of sub-primitive (start, count) values in a list attached + * to the index buffer for re-use in subsequent draws. The list would be + * invalidated when the contents of the buffer changed. + * 2. If drawing triangle strips or quad strips, create a new index buffer + * that uses duplicated vertices to render the disjoint strips as one + * long strip. We'd have to be careful to avoid using too much memory + * for this. + * Finally, some apps might perform better if they don't use primitive restart + * at all rather than this fallback path. Set MESA_EXTENSION_OVERRIDE to + * "-GL_NV_primitive_restart" to test that. + */ + + +struct sub_primitive +{ + unsigned start, count; +}; + + +/** + * Scan the elements array to find restart indexes. Return a list + * of primitive (start,count) pairs to indicate how to draw the sub- + * primitives delineated by the restart index. + */ +static struct sub_primitive * +find_sub_primitives(const void *elements, unsigned element_size, + unsigned start, unsigned end, unsigned restart_index, + unsigned *num_sub_prims) +{ + const unsigned max_prims = end - start; + struct sub_primitive *sub_prims; + unsigned i, cur_start, cur_count, num; + + sub_prims = (struct sub_primitive *) + malloc(max_prims * sizeof(struct sub_primitive)); + + if (!sub_prims) { + *num_sub_prims = 0; + return NULL; + } + + cur_start = start; + cur_count = 0; + num = 0; + +#define SCAN_ELEMENTS(TYPE) \ + for (i = start; i < end; i++) { \ + if (((const TYPE *) elements)[i] == restart_index) { \ + if (cur_count > 0) { \ + assert(num < max_prims); \ + sub_prims[num].start = cur_start; \ + sub_prims[num].count = cur_count; \ + num++; \ + } \ + cur_start = i + 1; \ + cur_count = 0; \ + } \ + else { \ + cur_count++; \ + } \ + } \ + if (cur_count > 0) { \ + assert(num < max_prims); \ + sub_prims[num].start = cur_start; \ + sub_prims[num].count = cur_count; \ + num++; \ + } + + switch (element_size) { + case 1: + SCAN_ELEMENTS(ubyte); + break; + case 2: + SCAN_ELEMENTS(ushort); + break; + case 4: + SCAN_ELEMENTS(uint); + break; + default: + assert(0 && "bad index_size in find_sub_primitives()"); + } + +#undef SCAN_ELEMENTS + + *num_sub_prims = num; + + return sub_prims; +} + + +/** + * For gallium drivers that don't support the primitive restart + * feature, handle it here by breaking up the indexed primitive into + * sub-primitives. + */ +static void +handle_fallback_primitive_restart(struct cso_context *cso, + struct pipe_context *pipe, + const struct _mesa_index_buffer *ib, + struct pipe_index_buffer *ibuffer, + struct pipe_draw_info *orig_info) +{ + const unsigned start = orig_info->start; + const unsigned count = orig_info->count; + struct pipe_draw_info info = *orig_info; + struct pipe_transfer *transfer = NULL; + unsigned instance, i; + const void *ptr = NULL; + struct sub_primitive *sub_prims; + unsigned num_sub_prims; + + assert(info.indexed); + assert(ibuffer->buffer || ibuffer->user_buffer); + assert(ib); + + if (!ibuffer->buffer || !ibuffer->user_buffer || !ib) + return; + + info.primitive_restart = FALSE; + info.instance_count = 1; + + if (_mesa_is_bufferobj(ib->obj)) { + ptr = pipe_buffer_map_range(pipe, ibuffer->buffer, + start * ibuffer->index_size, /* start */ + count * ibuffer->index_size, /* length */ + PIPE_TRANSFER_READ, &transfer); + if (!ptr) + return; + + ptr = (uint8_t*)ptr + (ibuffer->offset - start * ibuffer->index_size); + } + else { + ptr = ib->ptr; + if (!ptr) + return; + } + + sub_prims = find_sub_primitives(ptr, ibuffer->index_size, + 0, count, orig_info->restart_index, + &num_sub_prims); + + if (transfer) + pipe_buffer_unmap(pipe, transfer); + + /* Now draw the sub primitives. + * Need to loop over instances as well to preserve draw order. + */ + for (instance = 0; instance < orig_info->instance_count; instance++) { + info.start_instance = instance + orig_info->start_instance; + for (i = 0; i < num_sub_prims; i++) { + info.start = sub_prims[i].start; + info.count = sub_prims[i].count; + if (u_trim_pipe_prim(info.mode, &info.count)) { + cso_draw_vbo(cso, &info); } } } + + if (sub_prims) + free(sub_prims); } @@ -567,7 +812,7 @@ check_uniforms(GLcontext *ctx) * the corresponding Gallium type. */ static unsigned -translate_prim(const GLcontext *ctx, unsigned prim) +translate_prim(const struct gl_context *ctx, unsigned prim) { /* GL prims should match Gallium prims, spot-check a few */ assert(GL_POINTS == PIPE_PRIM_POINTS); @@ -575,8 +820,8 @@ translate_prim(const GLcontext *ctx, unsigned prim) assert(GL_TRIANGLE_STRIP_ADJACENCY == PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY); /* Avoid quadstrips if it's easy to do so: - * Note: it's imporant to do the correct trimming if we change the prim type! - * We do that wherever this function is called. + * Note: it's important to do the correct trimming if we change the + * prim type! We do that wherever this function is called. */ if (prim == GL_QUAD_STRIP && ctx->Light.ShadeModel != GL_FLAT && @@ -588,6 +833,57 @@ translate_prim(const GLcontext *ctx, unsigned prim) } +/** + * Setup vertex arrays and buffers prior to drawing. + * \return GL_TRUE for success, GL_FALSE otherwise (probably out of memory) + */ +static GLboolean +st_validate_varrays(struct gl_context *ctx, + const struct gl_client_array **arrays) +{ + struct st_context *st = st_context(ctx); + const struct st_vertex_program *vp; + const struct st_vp_variant *vpv; + struct pipe_vertex_buffer vbuffer[PIPE_MAX_SHADER_INPUTS]; + struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS]; + unsigned num_vbuffers, num_velements; + + /* must get these after state validation! */ + vp = st->vp; + vpv = st->vp_variant; + + memset(velements, 0, sizeof(struct pipe_vertex_element) * vpv->num_inputs); + + /* + * Setup the vbuffer[] and velements[] arrays. + */ + if (is_interleaved_arrays(vp, vpv, arrays)) { + if (!setup_interleaved_attribs(ctx, vp, vpv, arrays, vbuffer, + velements)) { + return GL_FALSE; + } + + num_vbuffers = 1; + num_velements = vpv->num_inputs; + if (num_velements == 0) + num_vbuffers = 0; + } + else { + if (!setup_non_interleaved_attribs(ctx, vp, vpv, arrays, + vbuffer, velements)) { + return GL_FALSE; + } + + num_vbuffers = vpv->num_inputs; + num_velements = vpv->num_inputs; + } + + cso_set_vertex_buffers(st->cso_context, num_vbuffers, vbuffer); + cso_set_vertex_elements(st->cso_context, num_velements, velements); + + return GL_TRUE; +} + /** * This function gets plugged into the VBO module and is called when @@ -595,112 +891,91 @@ translate_prim(const GLcontext *ctx, unsigned prim) * Basically, translate the information into the format expected by gallium. */ void -st_draw_vbo(GLcontext *ctx, - const struct gl_client_array **arrays, +st_draw_vbo(struct gl_context *ctx, const struct _mesa_prim *prims, GLuint nr_prims, const struct _mesa_index_buffer *ib, GLboolean index_bounds_valid, GLuint min_index, - GLuint max_index) + GLuint max_index, + struct gl_transform_feedback_object *tfb_vertcount) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; - const struct st_vertex_program *vp; - const struct st_vp_varient *vpv; - struct pipe_vertex_buffer vbuffer[PIPE_MAX_SHADER_INPUTS]; - GLuint attr; - struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS]; - unsigned num_vbuffers, num_velements; - struct pipe_index_buffer ibuffer; - GLboolean userSpace = GL_FALSE; - GLboolean vertDataEdgeFlags; + struct pipe_index_buffer ibuffer = {0}; struct pipe_draw_info info; + const struct gl_client_array **arrays = ctx->Array._DrawArrays; unsigned i; + GLboolean new_array; /* Mesa core state should have been validated already */ assert(ctx->NewState == 0x0); - /* Gallium probably doesn't want this in some cases. */ - if (!index_bounds_valid) - if (!vbo_all_varyings_in_vbos(arrays)) - vbo_get_minmax_index(ctx, prims, ib, &min_index, &max_index); + /* Get Mesa driver state. */ + st->dirty.st |= ctx->NewDriverState; + ctx->NewDriverState = 0; - /* sanity check for pointer arithmetic below */ - assert(sizeof(arrays[0]->Ptr[0]) == 1); + new_array = + (st->dirty.st & (ST_NEW_VERTEX_ARRAYS | ST_NEW_VERTEX_PROGRAM)) || + (st->dirty.mesa & (_NEW_PROGRAM | _NEW_BUFFER_OBJECT)) != 0; - vertDataEdgeFlags = arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj && - arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj->Name; - if (vertDataEdgeFlags != st->vertdata_edgeflags) { - st->vertdata_edgeflags = vertDataEdgeFlags; - st->dirty.st |= ST_NEW_EDGEFLAGS_DATA; - } - - st_validate_state(st); + /* Validate state. */ + if (st->dirty.st) { + GLboolean vertDataEdgeFlags; - /* must get these after state validation! */ - vp = st->vp; - vpv = st->vp_varient; + vertDataEdgeFlags = arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj && + arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj->Name; + if (vertDataEdgeFlags != st->vertdata_edgeflags) { + st->vertdata_edgeflags = vertDataEdgeFlags; + st->dirty.st |= ST_NEW_EDGEFLAGS_DATA; + } -#if 0 - if (MESA_VERBOSE & VERBOSE_GLSL) { - check_uniforms(ctx); - } -#else - (void) check_uniforms; -#endif + st_validate_state(st); - memset(velements, 0, sizeof(struct pipe_vertex_element) * vpv->num_inputs); - /* - * Setup the vbuffer[] and velements[] arrays. - */ - if (is_interleaved_arrays(vp, vpv, arrays, &userSpace)) { - /*printf("Draw interleaved\n");*/ - setup_interleaved_attribs(ctx, vp, vpv, arrays, max_index, userSpace, - vbuffer, velements); - num_vbuffers = 1; - num_velements = vpv->num_inputs; - if (num_velements == 0) - num_vbuffers = 0; - } - else { - /*printf("Draw non-interleaved\n");*/ - setup_non_interleaved_attribs(ctx, vp, vpv, arrays, max_index, - &userSpace, vbuffer, velements); - num_vbuffers = vpv->num_inputs; - num_velements = vpv->num_inputs; - } + if (new_array) { + if (!st_validate_varrays(ctx, arrays)) { + /* probably out of memory, no-op the draw call */ + return; + } + } #if 0 - { - GLuint i; - for (i = 0; i < num_vbuffers; i++) { - printf("buffers[%d].stride = %u\n", i, vbuffer[i].stride); - printf("buffers[%d].max_index = %u\n", i, vbuffer[i].max_index); - printf("buffers[%d].buffer_offset = %u\n", i, vbuffer[i].buffer_offset); - printf("buffers[%d].buffer = %p\n", i, (void*) vbuffer[i].buffer); - } - for (i = 0; i < num_velements; i++) { - printf("vlements[%d].vbuffer_index = %u\n", i, velements[i].vertex_buffer_index); - printf("vlements[%d].src_offset = %u\n", i, velements[i].src_offset); - printf("vlements[%d].format = %s\n", i, util_format_name(velements[i].src_format)); + if (MESA_VERBOSE & VERBOSE_GLSL) { + check_uniforms(ctx); } - } +#else + (void) check_uniforms; #endif - - pipe->set_vertex_buffers(pipe, num_vbuffers, vbuffer); - cso_set_vertex_elements(st->cso_context, num_velements, velements); - - setup_index_buffer(ctx, ib, &ibuffer); - pipe->set_index_buffer(pipe, &ibuffer); + } util_draw_init_info(&info); if (ib) { + /* Get index bounds for user buffers. */ + if (!index_bounds_valid) + if (!all_varyings_in_vbos(arrays)) + vbo_get_minmax_indices(ctx, prims, ib, &min_index, &max_index, + nr_prims); + + setup_index_buffer(st, ib, &ibuffer); + info.indexed = TRUE; if (min_index != ~0 && max_index != ~0) { info.min_index = min_index; info.max_index = max_index; } + + /* The VBO module handles restart for the non-indexed GLDrawArrays + * so we only set these fields for indexed drawing: + */ + info.primitive_restart = ctx->Array.PrimitiveRestart; + info.restart_index = ctx->Array.RestartIndex; + } + else { + /* Transform feedback drawing is always non-indexed. */ + /* Set info.count_from_stream_output. */ + if (tfb_vertcount) { + st_transform_feedback_draw_init(tfb_vertcount, &info); + } } /* do actual drawing */ @@ -715,28 +990,34 @@ st_draw_vbo(GLcontext *ctx, info.max_index = info.start + info.count - 1; } - if (u_trim_pipe_prim(info.mode, &info.count)) - pipe->draw_vbo(pipe, &info); - } - - pipe_resource_reference(&ibuffer.buffer, NULL); - - /* unreference buffers (frees wrapped user-space buffer objects) */ - for (attr = 0; attr < num_vbuffers; attr++) { - pipe_resource_reference(&vbuffer[attr].buffer, NULL); - assert(!vbuffer[attr].buffer); + if (info.count_from_stream_output) { + cso_draw_vbo(st->cso_context, &info); + } + else if (info.primitive_restart) { + if (st->sw_primitive_restart) { + /* Handle primitive restart for drivers that doesn't support it */ + handle_fallback_primitive_restart(st->cso_context, pipe, ib, + &ibuffer, &info); + } + else { + /* don't trim, restarts might be inside index list */ + cso_draw_vbo(st->cso_context, &info); + } + } + else if (u_trim_pipe_prim(info.mode, &info.count)) + cso_draw_vbo(st->cso_context, &info); } - if (userSpace) - { - pipe->set_vertex_buffers(pipe, 0, NULL); + if (ib && st->indexbuf_uploader && !_mesa_is_bufferobj(ib->obj)) { + pipe_resource_reference(&ibuffer.buffer, NULL); } } -void st_init_draw( struct st_context *st ) +void +st_init_draw(struct st_context *st) { - GLcontext *ctx = st->ctx; + struct gl_context *ctx = st->ctx; vbo_set_draw_func(ctx, st_draw_vbo); @@ -754,11 +1035,10 @@ void st_init_draw( struct st_context *st ) } -void st_destroy_draw( struct st_context *st ) +void +st_destroy_draw(struct st_context *st) { #if FEATURE_feedback || FEATURE_rastpos draw_destroy(st->draw); #endif } - -