static GLboolean vbo_maybe_split(struct gl_context *ctx, const struct gl_client_array **arrays, const struct _mesa_prim *prims, GLuint nr_prims, const struct _mesa_index_buffer *ib, GLuint min_index, GLuint max_index) { struct nouveau_context *nctx = to_nouveau_context(ctx); struct nouveau_render_state *render = to_render_state(ctx); struct nouveau_bufctx *bufctx = nctx->hw.bufctx; unsigned pushbuf_avail = PUSHBUF_DWORDS - 2 * (bufctx->relocs + render->attr_count), vert_avail = get_max_vertices(ctx, NULL, pushbuf_avail), idx_avail = get_max_vertices(ctx, ib, pushbuf_avail); int stride; /* Try to keep client buffers smaller than the scratch BOs. */ if (render->mode == VBO && (stride = get_max_client_stride(ctx, arrays))) vert_avail = MIN2(vert_avail, NOUVEAU_SCRATCH_SIZE / stride); if (max_index - min_index > vert_avail || (ib && ib->count > idx_avail)) { struct split_limits limits = { .max_verts = vert_avail, .max_indices = idx_avail, .max_vb_size = ~0, }; vbo_split_prims(ctx, arrays, prims, nr_prims, ib, min_index, max_index, TAG(vbo_render_prims), &limits); return GL_TRUE; } return GL_FALSE; } /* VBO rendering path. */ static GLboolean check_update_array(struct nouveau_array *a, unsigned offset, struct nouveau_bo *bo, int *pdelta) { int delta = *pdelta; GLboolean dirty; if (a->bo == bo) { if (delta < 0) delta = ((int)offset - (int)a->offset) / a->stride; dirty = (delta < 0 || offset != (a->offset + delta * a->stride)); } else { dirty = GL_TRUE; } *pdelta = (dirty ? 0 : delta); return dirty; }
/* This is the main entrypoint into the slimmed-down software tnl * module. In a regular swtnl driver, this can be plugged straight * into the vbo->Driver.DrawPrims() callback. */ void _tnl_draw_prims( struct gl_context *ctx, const struct gl_client_array *arrays[], const struct _mesa_prim *prim, GLuint nr_prims, const struct _mesa_index_buffer *ib, GLuint min_index, GLuint max_index) { TNLcontext *tnl = TNL_CONTEXT(ctx); const GLuint TEST_SPLIT = 0; const GLint max = TEST_SPLIT ? 8 : tnl->vb.Size - MAX_CLIPPED_VERTICES; GLint max_basevertex = prim->basevertex; GLuint i; /* Mesa core state should have been validated already */ assert(ctx->NewState == 0x0); if (!_mesa_check_conditional_render(ctx)) return; /* don't draw */ for (i = 1; i < nr_prims; i++) max_basevertex = MAX2(max_basevertex, prim[i].basevertex); if (0) { printf("%s %d..%d\n", __FUNCTION__, min_index, max_index); for (i = 0; i < nr_prims; i++) printf("prim %d: %s start %d count %d\n", i, _mesa_lookup_enum_by_nr(prim[i].mode), prim[i].start, prim[i].count); } 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_vbo_draw_prims ); return; } else if ((GLint)max_index + max_basevertex > 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; limits.max_vb_size = ~0; limits.max_indices = ~0; /* This will split the buffers one way or another and * recursively call back into this function. */ vbo_split_prims( ctx, arrays, prim, nr_prims, ib, 0, max_index + prim->basevertex, _tnl_vbo_draw_prims, &limits ); } else { /* May need to map a vertex buffer object for every attribute plus * one for the index buffer. */ struct gl_buffer_object *bo[VERT_ATTRIB_MAX + 1]; GLuint nr_bo = 0; GLuint inst; for (i = 0; i < nr_prims;) { GLuint this_nr_prims; /* Our SW TNL pipeline doesn't handle basevertex yet, so bind_indices * will rebase the elements to the basevertex, and we'll only * emit strings of prims with the same basevertex in one draw call. */ for (this_nr_prims = 1; i + this_nr_prims < nr_prims; this_nr_prims++) { if (prim[i].basevertex != prim[i + this_nr_prims].basevertex) break; } assert(prim[i].num_instances > 0); /* Binding inputs may imply mapping some vertex buffer objects. * They will need to be unmapped below. */ for (inst = 0; inst < prim[i].num_instances; inst++) { bind_prims(ctx, &prim[i], this_nr_prims); bind_inputs(ctx, arrays, max_index + prim[i].basevertex + 1, bo, &nr_bo); bind_indices(ctx, ib, bo, &nr_bo); tnl->CurInstance = inst; TNL_CONTEXT(ctx)->Driver.RunPipeline(ctx); unmap_vbos(ctx, bo, nr_bo); free_space(ctx); } i += this_nr_prims; } } }
/* This is the main entrypoint into the slimmed-down software tnl * module. In a regular swtnl driver, this can be plugged straight * into the vbo->Driver.DrawPrims() callback. */ void _tnl_draw_prims( GLcontext *ctx, const struct gl_client_array *arrays[], const struct _mesa_prim *prim, GLuint nr_prims, const struct _mesa_index_buffer *ib, GLuint min_index, GLuint max_index) { TNLcontext *tnl = TNL_CONTEXT(ctx); const GLuint TEST_SPLIT = 0; const GLint max = TEST_SPLIT ? 8 : tnl->vb.Size - MAX_CLIPPED_VERTICES; if (0) { GLuint i; _mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index); for (i = 0; i < nr_prims; i++) _mesa_printf("prim %d: %s start %d count %d\n", i, _mesa_lookup_enum_by_nr(prim[i].mode), prim[i].start, prim[i].count); } 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; limits.max_vb_size = ~0; limits.max_indices = ~0; /* This will split the buffers one way or another and * recursively call back into this function. */ vbo_split_prims( ctx, arrays, prim, nr_prims, ib, 0, max_index, _tnl_draw_prims, &limits ); } else { /* May need to map a vertex buffer object for every attribute plus * one for the index buffer. */ struct gl_buffer_object *bo[VERT_ATTRIB_MAX + 1]; GLuint nr_bo = 0; /* Binding inputs may imply mapping some vertex buffer objects. * They will need to be unmapped below. */ 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); unmap_vbos(ctx, bo, nr_bo); free_space(ctx); } }