*
* 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 <keith@tungstengraphics.com>
#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"
#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,
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,
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,
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,
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,
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,
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,
/**
* 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);
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];
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) {
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;
}
* 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;
}
* 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);
}
* 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);
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 &&
}
+/**
+ * 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
* 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 */
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);
}
-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
}
-
-