brw->state.dirty.brw |= BRW_NEW_INPUT_VARYING;
}
-
+/* XXX: could split the primitive list to fallback only on the
+ * non-conformant primitives.
+ */
static GLboolean check_fallbacks( struct brw_context *brw,
const struct _mesa_prim *prim,
GLuint nr_prims )
return GL_FALSE;
}
-
+/* May fail if out of video memory for texture or vbo upload, or on
+ * fallback conditions.
+ */
static GLboolean brw_try_draw_prims( GLcontext *ctx,
const struct gl_client_array *arrays[],
const struct _mesa_prim *prim,
return retval;
}
+static GLboolean brw_need_rebase( GLcontext *ctx,
+ const struct gl_client_array *arrays[],
+ const struct _mesa_index_buffer *ib,
+ GLuint min_index )
+{
+ if (min_index == 0)
+ return GL_FALSE;
+
+ if (ib) {
+ if (!vbo_all_varyings_in_vbos(arrays))
+ return GL_TRUE;
+ else
+ return GL_FALSE;
+ }
+ else {
+ /* Hmm. This isn't quite what I wanted. BRW can actually
+ * handle the mixed case well enough that we shouldn't need to
+ * rebase. However, it's probably not very common, nor hugely
+ * expensive to do it this way:
+ */
+ if (!vbo_all_varyings_in_vbos(arrays))
+ return GL_TRUE;
+ else
+ return GL_FALSE;
+ }
+}
+
void brw_draw_prims( GLcontext *ctx,
const struct gl_client_array *arrays[],
struct intel_context *intel = intel_context(ctx);
GLboolean retval;
+ /* Decide if we want to rebase. If so we end up recursing once
+ * only into this function.
+ */
+ if (brw_need_rebase( ctx, arrays, ib, min_index )) {
+ vbo_rebase_prims( ctx, arrays,
+ prim, nr_prims,
+ ib, min_index, max_index,
+ brw_draw_prims );
+
+ return;
+ }
+
+
+ /* Make a first attempt at drawing:
+ */
retval = brw_try_draw_prims(ctx, arrays, prim, nr_prims, ib, min_index, max_index);
for (i = 0; i < BRW_NR_UPLOAD_BUFS; i++) {
brw->vb.upload.vbo[i] = ctx->Driver.NewBufferObject(ctx, 1, GL_ARRAY_BUFFER_ARB);
- /* XXX: Set these to no-backing-store
+ /* NOTE: These are set to no-backing-store.
*/
bmBufferSetInvalidateCB(&brw->intel,
intel_bufferobj_buffer(intel_buffer_object(brw->vb.upload.vbo[i])),
brw_invalidate_vbo_cb,
&brw->intel,
GL_TRUE);
-
}
ctx->Driver.BufferData( ctx,
GLuint i,
const struct gl_client_array *array,
GLuint element_size,
- GLuint min_index,
GLuint count)
{
GLcontext *ctx = &brw->intel.ctx;
map += offset;
copy_strided_array( map,
- array->Ptr + min_index * array->StrideB,
+ array->Ptr,
element_size,
array->StrideB,
count);
}
upload[nr_uploads++] = input;
- input->vbo_rebase_offset = 0;
+ assert(min_index == 0);
}
- else
- input->vbo_rebase_offset = min_index * input->glarray->StrideB;
}
/* Upload interleaved arrays if all uploads are interleaved
input0->glarray = copy_array_to_vbo_array(brw, 0,
input0->glarray,
interleave,
- min_index,
input0->count);
for (i = 1; i < nr_uploads; i++) {
input->glarray = copy_array_to_vbo_array(brw, i,
input->glarray,
input->element_size,
- min_index,
input->count);
}
vbp.vb[i].vb0.bits.pad = 0;
vbp.vb[i].vb0.bits.access_type = BRW_VERTEXBUFFER_ACCESS_VERTEXDATA;
vbp.vb[i].vb0.bits.vb_index = i;
- vbp.vb[i].offset = (GLuint)input->glarray->Ptr + input->vbo_rebase_offset;
+ vbp.vb[i].offset = (GLuint)input->glarray->Ptr;
vbp.vb[i].buffer = array_buffer(input->glarray);
- vbp.vb[i].max_index = max_index - min_index;
+ vbp.vb[i].max_index = max_index;
}
-
-static void rebase_indices_to_vbo_indices( struct brw_context *brw,
- const struct _mesa_index_buffer *index_buffer,
- struct gl_buffer_object **vbo_return,
- GLuint *offset_return )
+void brw_upload_indices( struct brw_context *brw,
+ const struct _mesa_index_buffer *index_buffer )
{
GLcontext *ctx = &brw->intel.ctx;
- GLuint min_index = index_buffer->rebase;
- const void *indices = index_buffer->ptr;
- GLsizei count = index_buffer->count;
- GLenum type = index_buffer->type;
- GLuint size = element_size(type) * count;
- struct gl_buffer_object *bufferobj;
- GLuint offset;
- GLuint i;
-
- get_space(brw, size, &bufferobj, &offset);
+ struct intel_context *intel = &brw->intel;
+ GLuint ib_size = get_size(index_buffer->type) * index_buffer->count;
+ struct gl_buffer_object *bufferobj = index_buffer->obj;
+ GLuint offset = (GLuint)index_buffer->ptr;
- *vbo_return = bufferobj;
- *offset_return = offset;
+ /* Turn into a proper VBO:
+ */
+ if (!bufferobj->Name) {
+
+ /* Get new bufferobj, offset:
+ */
+ get_space(brw, ib_size, &bufferobj, &offset);
- if (min_index == 0) {
/* Straight upload
*/
ctx->Driver.BufferSubData( ctx,
GL_ELEMENT_ARRAY_BUFFER_ARB,
offset,
- size,
- indices,
+ ib_size,
+ index_buffer->ptr,
bufferobj);
}
- else {
- void *map = ctx->Driver.MapBuffer(ctx,
- GL_ELEMENT_ARRAY_BUFFER_ARB,
- GL_DYNAMIC_DRAW_ARB,
- bufferobj);
-
- map += offset;
-
- switch (type) {
- case GL_UNSIGNED_INT: {
- GLuint *ui_map = (GLuint *)map;
- const GLuint *ui_indices = (const GLuint *)indices;
-
- for (i = 0; i < count; i++)
- ui_map[i] = ui_indices[i] - min_index;
- break;
- }
- case GL_UNSIGNED_SHORT: {
- GLushort *us_map = (GLushort *)map;
- const GLushort *us_indices = (const GLushort *)indices;
-
- for (i = 0; i < count; i++)
- us_map[i] = us_indices[i] - min_index;
- break;
- }
- case GL_UNSIGNED_BYTE: {
- GLubyte *ub_map = (GLubyte *)map;
- const GLubyte *ub_indices = (const GLubyte *)indices;
-
- for (i = 0; i < count; i++)
- ub_map[i] = ub_indices[i] - min_index;
- break;
- }
- }
-
- ctx->Driver.UnmapBuffer(ctx,
- GL_ELEMENT_ARRAY_BUFFER_ARB,
- bufferobj);
-
- }
-}
-
-
-
-void brw_upload_indices( struct brw_context *brw,
- const struct _mesa_index_buffer *index_buffer)
-{
- struct intel_context *intel = &brw->intel;
- GLuint ib_size = get_size(index_buffer->type) * index_buffer->count;
- struct gl_buffer_object *bufferobj = index_buffer->obj;
- GLuint offset = (GLuint)index_buffer->ptr;
-
- /* Already turned into a proper VBO:
- */
- if (!index_buffer->obj->Name) {
- rebase_indices_to_vbo_indices(brw, index_buffer, &bufferobj, &offset );
- }
/* Emit the indexbuffer packet:
*/
*/
static void _tnl_import_array( GLcontext *ctx,
GLuint attrib,
- GLuint start,
- GLuint end,
+ GLuint count,
const struct gl_client_array *input,
const GLubyte *ptr )
{
TNLcontext *tnl = TNL_CONTEXT(ctx);
struct vertex_buffer *VB = &tnl->vb;
- const GLuint count = end - start;
GLuint stride = input->StrideB;
- ptr += start * stride;
-
if (input->Type != GL_FLOAT) {
const GLuint sz = input->Size;
GLubyte *buf = get_space(ctx, count * sz * sizeof(GLfloat));
static void bind_inputs( GLcontext *ctx,
const struct gl_client_array *inputs[],
- GLint min_index, GLint max_index,
+ GLint count,
struct gl_buffer_object **bo,
GLuint *nr_bo )
{
ptr = inputs[i]->Ptr;
/* Just make sure the array is floating point, otherwise convert to
- * temporary storage. Rebase arrays so that 'min_index' becomes
- * element zero.
+ * temporary storage.
*
* XXX: remove the GLvector4f type at some stage and just use
* client arrays.
*/
- _tnl_import_array(ctx, i, min_index, max_index, inputs[i], ptr);
+ _tnl_import_array(ctx, i, count, inputs[i], ptr);
}
/* We process only the vertices between min & max index:
*/
- VB->Count = max_index - min_index;
+ VB->Count = count;
/* Legacy pointers -- remove one day.
*/
static void bind_indices( GLcontext *ctx,
const struct _mesa_index_buffer *ib,
- GLuint min_index,
struct gl_buffer_object **bo,
GLuint *nr_bo)
{
GLuint i;
void *ptr;
- if (!ib)
+ if (!ib) {
+ VB->Elts = NULL;
return;
+ }
if (ib->obj->Name && !ib->obj->Pointer) {
bo[*nr_bo] = ib->obj;
ptr = ADD_POINTERS(ib->obj->Pointer, ib->ptr);
- if (ib->type == GL_UNSIGNED_INT && min_index == 0) {
+ if (ib->type == GL_UNSIGNED_INT) {
VB->Elts = (GLuint *) ptr;
- VB->Elts += ib->rebase;
}
else {
GLuint *elts = (GLuint *)get_space(ctx, ib->count * sizeof(GLuint));
VB->Elts = elts;
- switch (ib->type) {
- case GL_UNSIGNED_INT: {
- const GLuint *in = ((GLuint *)ptr) + ib->rebase;
- for (i = 0; i < ib->count; i++)
- *elts++ = *in++ - min_index;
- break;
- }
- case GL_UNSIGNED_SHORT: {
- const GLushort *in = ((GLushort *)ptr) + ib->rebase;
+ if (ib->type == GL_UNSIGNED_SHORT) {
+ const GLushort *in = (GLushort *)ptr;
for (i = 0; i < ib->count; i++)
- *elts++ = (GLuint)(*in++) - min_index;
- break;
+ *elts++ = (GLuint)(*in++);
}
- case GL_UNSIGNED_BYTE: {
- const GLubyte *in = ((GLubyte *)ptr) + ib->rebase;
+ else {
+ const GLubyte *in = (GLubyte *)ptr;
for (i = 0; i < ib->count; i++)
- *elts++ = (GLuint)(*in++) - min_index;
- break;
+ *elts++ = (GLuint)(*in++);
}
- }
}
}
static void bind_prims( GLcontext *ctx,
const struct _mesa_prim *prim,
- GLuint nr_prims,
- GLuint min_index )
+ GLuint nr_prims )
{
TNLcontext *tnl = TNL_CONTEXT(ctx);
struct vertex_buffer *VB = &tnl->vb;
- GLuint i;
-
- if (min_index != 0) {
- struct _mesa_prim *tmp = (struct _mesa_prim *)get_space(ctx, nr_prims * sizeof(*prim));
-
- for (i = 0; i < nr_prims; i++) {
- tmp[i] = prim[i];
- tmp[i].start -= min_index;
- }
-
- VB->Primitive = tmp;
- }
- else {
- VB->Primitive = prim;
- }
+ VB->Primitive = prim;
VB->PrimitiveCount = nr_prims;
}
GLuint max_index)
{
TNLcontext *tnl = TNL_CONTEXT(ctx);
- struct vertex_buffer *VB = &tnl->vb;
- GLint max = VB->Size;
-
-#ifdef TEST_SPLIT
- max = 8 + MAX_CLIPPED_VERTICES;
-#endif
-
- assert(max_index > min_index);
- assert(!(max_index & 0x80000000));
+ const GLuint TEST_SPLIT = 0;
+ const GLint max = TEST_SPLIT ? 8 : tnl->vb.Size - MAX_CLIPPED_VERTICES;
- VB->Elts = NULL;
-
-#if 0
+ if (0)
{
GLuint i;
_mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index);
prim[i].start,
prim[i].count);
}
-#endif
- /* The software TNL pipeline has a fixed amount of storage for
- * vertices and it is necessary to split incoming drawing commands
- * if they exceed that limit.
- */
- if (max_index - min_index >= max - MAX_CLIPPED_VERTICES) {
+ if (min_index) {
+ /* We always translate away calls with min_index != 0.
+ */
+ vbo_rebase_prims( ctx, arrays, prim, nr_prims, ib,
+ min_index, max_index,
+ _tnl_draw_prims );
+ return;
+ }
+ else if (max_index >= max) {
+ /* The software TNL pipeline has a fixed amount of storage for
+ * vertices and it is necessary to split incoming drawing commands
+ * if they exceed that limit.
+ */
struct split_limits limits;
- limits.max_verts = max - MAX_CLIPPED_VERTICES;
+ limits.max_verts = max;
limits.max_vb_size = ~0;
limits.max_indices = ~0;
* recursively call back into this function.
*/
vbo_split_prims( ctx, arrays, prim, nr_prims, ib,
- min_index, max_index,
+ 0, max_index,
_tnl_draw_prims,
&limits );
}
/* Binding inputs may imply mapping some vertex buffer objects.
* They will need to be unmapped below.
*/
- bind_inputs(ctx, arrays, min_index, max_index+1, bo, &nr_bo);
- bind_indices(ctx, ib, min_index, bo, &nr_bo);
- bind_prims(ctx, prim, nr_prims, VB->Elts ? 0 : min_index );
+ bind_inputs(ctx, arrays, max_index+1, bo, &nr_bo);
+ bind_indices(ctx, ib, bo, &nr_bo);
+ bind_prims(ctx, prim, nr_prims );
TNL_CONTEXT(ctx)->Driver.RunPipeline(ctx);
GLenum type;
struct gl_buffer_object *obj;
const void *ptr;
- GLuint rebase;
};
#include "vbo_context.h"
-static GLuint get_max_index( GLuint count, GLuint type,
- const GLvoid *indices )
+/* Compute min and max elements for drawelements calls.
+ */
+static void get_minmax_index( GLuint count, GLuint type,
+ const GLvoid *indices,
+ GLuint *min_index,
+ GLuint *max_index)
{
GLint i;
- /* Compute max element. This is only needed for upload of non-VBO,
- * non-constant data elements.
- *
- * XXX: Postpone this calculation until it is known that it is
- * needed. Otherwise could scan this pointlessly in the all-vbo
- * case.
- */
switch(type) {
case GL_UNSIGNED_INT: {
const GLuint *ui_indices = (const GLuint *)indices;
- GLuint max_ui = 0;
- for (i = 0; i < count; i++)
- if (ui_indices[i] > max_ui)
- max_ui = ui_indices[i];
- return max_ui;
+ GLuint max_ui = ui_indices[0];
+ GLuint min_ui = ui_indices[0];
+ for (i = 1; i < count; i++) {
+ if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
+ if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
+ }
+ *min_index = min_ui;
+ *max_index = max_ui;
+ break;
}
case GL_UNSIGNED_SHORT: {
const GLushort *us_indices = (const GLushort *)indices;
- GLuint max_us = 0;
- for (i = 0; i < count; i++)
- if (us_indices[i] > max_us)
- max_us = us_indices[i];
- return max_us;
+ GLuint max_us = us_indices[0];
+ GLuint min_us = us_indices[0];
+ for (i = 1; i < count; i++) {
+ if (us_indices[i] > max_us) max_us = us_indices[i];
+ if (us_indices[i] < min_us) min_us = us_indices[i];
+ }
+ *min_index = min_us;
+ *max_index = max_us;
+ break;
}
case GL_UNSIGNED_BYTE: {
const GLubyte *ub_indices = (const GLubyte *)indices;
- GLuint max_ub = 0;
- for (i = 0; i < count; i++)
- if (ub_indices[i] > max_ub)
- max_ub = ub_indices[i];
- return max_ub;
+ GLuint max_ub = ub_indices[0];
+ GLuint min_ub = ub_indices[0];
+ for (i = 1; i < count; i++) {
+ if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
+ if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
+ }
+ *min_index = min_ub;
+ *max_index = max_ub;
+ break;
}
default:
- return 0;
+ assert(0);
+ break;
}
}
prim[0].end = 1;
prim[0].weak = 0;
prim[0].pad = 0;
+ prim[0].mode = mode;
+ prim[0].start = start;
+ prim[0].count = count;
+ prim[0].indexed = 0;
- if (exec->array.inputs[0]->BufferObj->Name) {
- /* Use vertex attribute as a hint to tell us if we expect all
- * arrays to be in VBO's and if so, don't worry about avoiding
- * the upload of elements < start.
- */
- prim[0].mode = mode;
- prim[0].start = start;
- prim[0].count = count;
- prim[0].indexed = 0;
-
- vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, 0, start + count );
- }
- else {
- /* If not using VBO's, we don't want to upload any more elements
- * than necessary from the arrays as they will not be valid next
- * time the application tries to draw with them.
- */
- prim[0].mode = mode;
- prim[0].start = 0;
- prim[0].count = count;
- prim[0].indexed = 0;
-
- vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, start, start + count );
- }
+ vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, start, start + count - 1 );
}
ib.obj = ctx->Array.ElementArrayBufferObj;
ib.ptr = indices;
- if (ctx->Array.ElementArrayBufferObj->Name) {
- /* Use the fact that indices are in a VBO as a hint that the
- * program has put all the arrays in VBO's and we don't have to
- * worry about performance implications of start > 0.
- *
- * XXX: consider passing start as min_index to draw_prims instead.
- * XXX: don't rebase because it didn't work.
- */
- ib.rebase = 0;
- }
- else {
- ib.rebase = /*start*/ 0;
- }
-
prim[0].begin = 1;
prim[0].end = 1;
prim[0].weak = 0;
prim[0].count = count;
prim[0].indexed = 1;
- vbo->draw_prims( ctx, exec->array.inputs, prim, 1, &ib, /*ib.rebase*/ start, end+1 );
-}
+ /* Need to give special consideration to rendering a range of
+ * indices starting somewhere above zero. Typically the
+ * application is issuing multiple DrawRangeElements() to draw
+ * successive primitives layed out linearly in the vertex arrays.
+ * Unless the vertex arrays are all in a VBO (or locked as with
+ * CVA), the OpenGL semantics imply that we need to re-read or
+ * re-upload the vertex data on each draw call.
+ *
+ * In the case of hardware tnl, we want to avoid starting the
+ * upload at zero, as it will mean every draw call uploads an
+ * increasing amount of not-used vertex data. Worse - in the
+ * software tnl module, all those vertices might be transformed and
+ * lit but never rendered.
+ *
+ * If we just upload or transform the vertices in start..end,
+ * however, the indices will be incorrect.
+ *
+ * At this level, we don't know exactly what the requirements of
+ * the backend are going to be, though it will likely boil down to
+ * either:
+ *
+ * 1) Do nothing, everything is in a VBO and is processed once
+ * only.
+ *
+ * 2) Adjust the indices and vertex arrays so that start becomes
+ * zero.
+ *
+ * Rather than doing anything here, I'll provide a helper function
+ * for the latter case elsewhere.
+ */
+ vbo->draw_prims( ctx, exec->array.inputs, prim, 1, &ib, start, end );
+}
static void GLAPIENTRY
vbo_exec_DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
{
GET_CURRENT_CONTEXT(ctx);
- GLuint max_index;
+ GLuint min_index = 0;
+ GLuint max_index = 0;
if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
return;
GL_READ_ONLY,
ctx->Array.ElementArrayBufferObj);
- max_index = get_max_index(count, type, ADD_POINTERS(map, indices));
+ get_minmax_index(count, type, ADD_POINTERS(map, indices), &min_index, &max_index);
ctx->Driver.UnmapBuffer(ctx,
GL_ELEMENT_ARRAY_BUFFER_ARB,
ctx->Array.ElementArrayBufferObj);
}
else {
- max_index = get_max_index(count, type, indices);
+ get_minmax_index(count, type, indices, &min_index, &max_index);
}
- vbo_exec_DrawRangeElements(mode, 0, max_index, count, type, indices);
+ vbo_exec_DrawRangeElements(mode, min_index, max_index, count, type, indices);
}
assert(min_index != 0);
- _mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index);
+ if (0)
+ _mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index);
if (ib) {
/* Unfortunately need to adjust each index individually.
copy->dstib.type = GL_UNSIGNED_INT;
copy->dstib.obj = ctx->Array.NullBufferObj;
copy->dstib.ptr = copy->dstelt;
- copy->dstib.rebase = 0;
}
ib.type = GL_UNSIGNED_INT;
ib.obj = split->ctx->Array.NullBufferObj;
ib.ptr = elts;
- ib.rebase = 0; /* ? */
tmpprim = *prim;
tmpprim.indexed = 1;