diff options
Diffstat (limited to 'src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c')
-rw-r--r-- | src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c b/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c new file mode 100644 index 0000000000..ba1192a170 --- /dev/null +++ b/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2009-2010 Francisco Jerez. + * 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, sublicense, 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 NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "main/bufferobj.h" +#include "nouveau_bufferobj.h" + +/* Arbitrary pushbuf length we can assume we can get with a single + * WAIT_RING. */ +#define PUSHBUF_DWORDS 2048 + +/* Functions to set up struct nouveau_array_state from something like + * a GL array or index buffer. */ + +static void +vbo_init_array(struct nouveau_array_state *a, int attr, int stride, + int fields, int type, struct gl_buffer_object *obj, + const void *ptr, GLboolean map) +{ + a->attr = attr; + a->stride = stride; + a->fields = fields; + a->type = type; + + if (_mesa_is_bufferobj(obj)) { + nouveau_bo_ref(to_nouveau_bufferobj(obj)->bo, &a->bo); + a->offset = (intptr_t)ptr; + + if (map) { + nouveau_bo_map(a->bo, NOUVEAU_BO_RD); + a->buf = a->bo->map + a->offset; + } else { + a->buf = NULL; + } + + } else { + nouveau_bo_ref(NULL, &a->bo); + a->offset = 0; + a->buf = ptr; + } + + if (a->buf) + get_array_extract(a, &a->extract_u, &a->extract_f); +} + +static void +vbo_deinit_array(struct nouveau_array_state *a) +{ + if (a->bo) { + if (a->bo->map) + nouveau_bo_unmap(a->bo); + nouveau_bo_ref(NULL, &a->bo); + } + + a->buf = NULL; + a->fields = 0; +} + +static void +vbo_init_arrays(GLcontext *ctx, const struct _mesa_index_buffer *ib, + const struct gl_client_array **arrays) +{ + struct nouveau_render_state *render = to_render_state(ctx); + int i; + + if (ib) + vbo_init_array(&render->ib, 0, 0, ib->count, ib->type, + ib->obj, ib->ptr, GL_TRUE); + + for (i = 0; i < render->attr_count; i++) { + int attr = render->map[i]; + + if (attr >= 0) { + const struct gl_client_array *array = arrays[attr]; + + vbo_init_array(&render->attrs[attr], attr, + array->StrideB, array->Size, + array->Type, array->BufferObj, + array->Ptr, render->mode == IMM); + } + } +} + +static void +vbo_deinit_arrays(GLcontext *ctx, const struct _mesa_index_buffer *ib, + const struct gl_client_array **arrays) +{ + struct nouveau_render_state *render = to_render_state(ctx); + int i; + + if (ib) + vbo_deinit_array(&render->ib); + + for (i = 0; i < render->attr_count; i++) { + int *attr = &render->map[i]; + + if (*attr >= 0) { + vbo_deinit_array(&render->attrs[*attr]); + *attr = -1; + } + } + + render->attr_count = 0; +} + +/* Make some rendering decisions from the GL context. */ + +static void +vbo_choose_render_mode(GLcontext *ctx, const struct gl_client_array **arrays) +{ + struct nouveau_render_state *render = to_render_state(ctx); + int i; + + render->mode = VBO; + + if (ctx->Light.Enabled) { + for (i = 0; i < MAT_ATTRIB_MAX; i++) { + if (arrays[VERT_ATTRIB_GENERIC0 + i]->StrideB) { + render->mode = IMM; + break; + } + } + } + + if (render->mode == VBO) + render->attr_count = NUM_VERTEX_ATTRS; + else + render->attr_count = 0; +} + +static void +vbo_emit_attr(GLcontext *ctx, const struct gl_client_array **arrays, int attr) +{ + struct nouveau_channel *chan = context_chan(ctx); + struct nouveau_render_state *render = to_render_state(ctx); + const struct gl_client_array *array = arrays[attr]; + struct nouveau_array_state *a = &render->attrs[attr]; + RENDER_LOCALS(ctx); + + if (!array->StrideB) { + if (attr >= VERT_ATTRIB_GENERIC0) + /* nouveau_update_state takes care of materials. */ + return; + + /* Constant attribute. */ + vbo_init_array(a, attr, array->StrideB, array->Size, + array->Type, array->BufferObj, array->Ptr, + GL_TRUE); + EMIT_IMM(ctx, a, 0); + vbo_deinit_array(a); + + } else { + /* Varying attribute. */ + struct nouveau_attr_info *info = &TAG(vertex_attrs)[attr]; + + if (render->mode == VBO) { + render->map[info->vbo_index] = attr; + render->vertex_size += array->_ElementSize; + } else { + render->map[render->attr_count++] = attr; + render->vertex_size += 4 * info->imm_fields; + } + } +} + +#define MAT(a) (VERT_ATTRIB_GENERIC0 + MAT_ATTRIB_##a) + +static void +vbo_choose_attrs(GLcontext *ctx, const struct gl_client_array **arrays) +{ + struct nouveau_render_state *render = to_render_state(ctx); + int i; + + /* Reset the vertex size. */ + render->vertex_size = 0; + + vbo_emit_attr(ctx, arrays, VERT_ATTRIB_COLOR0); + if (ctx->Fog.ColorSumEnabled && !ctx->Light.Enabled) + vbo_emit_attr(ctx, arrays, VERT_ATTRIB_COLOR1); + + for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) { + if (ctx->Texture._EnabledCoordUnits & (1 << i)) + vbo_emit_attr(ctx, arrays, VERT_ATTRIB_TEX0 + i); + } + + if (ctx->Fog.Enabled && ctx->Fog.FogCoordinateSource == GL_FOG_COORD) + vbo_emit_attr(ctx, arrays, VERT_ATTRIB_FOG); + + if (ctx->Light.Enabled) { + vbo_emit_attr(ctx, arrays, VERT_ATTRIB_NORMAL); + + vbo_emit_attr(ctx, arrays, MAT(FRONT_AMBIENT)); + vbo_emit_attr(ctx, arrays, MAT(FRONT_DIFFUSE)); + vbo_emit_attr(ctx, arrays, MAT(FRONT_SPECULAR)); + vbo_emit_attr(ctx, arrays, MAT(FRONT_SHININESS)); + + if (ctx->Light.Model.TwoSide) { + vbo_emit_attr(ctx, arrays, MAT(BACK_AMBIENT)); + vbo_emit_attr(ctx, arrays, MAT(BACK_DIFFUSE)); + vbo_emit_attr(ctx, arrays, MAT(BACK_SPECULAR)); + vbo_emit_attr(ctx, arrays, MAT(BACK_SHININESS)); + } + } + + vbo_emit_attr(ctx, arrays, VERT_ATTRIB_POS); +} + +static void +TAG(vbo_render_prims)(GLcontext *ctx, const struct gl_client_array **arrays, + const struct _mesa_prim *prims, GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLboolean index_bounds_valid, + GLuint min_index, GLuint max_index); + +static GLboolean +vbo_maybe_split(GLcontext *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); + unsigned pushbuf_avail = PUSHBUF_DWORDS - 2 * nctx->bo.count, + vert_avail = get_max_vertices(ctx, NULL, pushbuf_avail), + idx_avail = get_max_vertices(ctx, ib, pushbuf_avail); + + if ((ib && ib->count > idx_avail) || + (!ib && max_index - min_index > vert_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 void +vbo_bind_vertices(GLcontext *ctx, const struct gl_client_array **arrays, + GLint basevertex, GLuint min_index, GLuint max_index) +{ + struct nouveau_render_state *render = to_render_state(ctx); + int i; + + for (i = 0; i < NUM_VERTEX_ATTRS; i++) { + int attr = render->map[i]; + + if (attr >= 0) { + const struct gl_client_array *array = arrays[attr]; + struct nouveau_array_state *a = &render->attrs[attr]; + unsigned delta = (basevertex + min_index) * a->stride, + size = (max_index - min_index + 1) * a->stride; + + if (a->bo) { + a->offset = (intptr_t)array->Ptr + delta; + } else { + void *scratch = get_scratch_vbo(ctx, size, + &a->bo, + &a->offset); + + memcpy(scratch, a->buf + delta, size); + } + } + } + + TAG(render_bind_vertices)(ctx); +} + +static void +vbo_draw_vbo(GLcontext *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_channel *chan = context_chan(ctx); + dispatch_t dispatch; + int delta = -min_index, basevertex = 0, i; + RENDER_LOCALS(ctx); + + get_array_dispatch(&to_render_state(ctx)->ib, &dispatch); + + TAG(render_set_format)(ctx); + + for (i = 0; i < nr_prims; i++) { + unsigned start = prims[i].start, + count = prims[i].count; + + if (i == 0 || basevertex != prims[i].basevertex) { + basevertex = prims[i].basevertex; + vbo_bind_vertices(ctx, arrays, basevertex, + min_index, max_index); + } + + if (count > get_max_vertices(ctx, ib, chan->pushbuf->remaining)) + WAIT_RING(chan, PUSHBUF_DWORDS); + + BATCH_BEGIN(nvgl_primitive(prims[i].mode)); + dispatch(ctx, start, delta, count); + BATCH_END(); + } + + FIRE_RING(chan); +} + +/* Immediate rendering path. */ + +static unsigned +extract_id(struct nouveau_array_state *a, int i, int j) +{ + return j; +} + +static void +vbo_draw_imm(GLcontext *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_render_state *render = to_render_state(ctx); + struct nouveau_channel *chan = context_chan(ctx); + extract_u_t extract = ib ? render->ib.extract_u : extract_id; + int i, j, k; + RENDER_LOCALS(ctx); + + for (i = 0; i < nr_prims; i++) { + unsigned start = prims[i].start, + end = start + prims[i].count; + + if (prims[i].count > get_max_vertices(ctx, ib, + chan->pushbuf->remaining)) + WAIT_RING(chan, PUSHBUF_DWORDS); + + BATCH_BEGIN(nvgl_primitive(prims[i].mode)); + + for (; start < end; start++) { + j = prims[i].basevertex + + extract(&render->ib, 0, start); + + for (k = 0; k < render->attr_count; k++) + EMIT_IMM(ctx, &render->attrs[render->map[k]], + j); + } + + BATCH_END(); + } + + FIRE_RING(chan); +} + +/* draw_prims entry point when we're doing hw-tnl. */ + +static void +TAG(vbo_render_prims)(GLcontext *ctx, const struct gl_client_array **arrays, + const struct _mesa_prim *prims, GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLboolean index_bounds_valid, + GLuint min_index, GLuint max_index) +{ + struct nouveau_render_state *render = to_render_state(ctx); + + if (!index_bounds_valid) + vbo_get_minmax_index(ctx, prims, ib, &min_index, &max_index); + + vbo_choose_render_mode(ctx, arrays); + vbo_choose_attrs(ctx, arrays); + + if (vbo_maybe_split(ctx, arrays, prims, nr_prims, ib, min_index, + max_index)) + return; + + vbo_init_arrays(ctx, ib, arrays); + + if (render->mode == VBO) + vbo_draw_vbo(ctx, arrays, prims, nr_prims, ib, min_index, + max_index); + else + vbo_draw_imm(ctx, arrays, prims, nr_prims, ib, min_index, + max_index); + + vbo_deinit_arrays(ctx, ib, arrays); +} |