nouveau_vbo_t.c revision c944fb5ffe7cf16154d6395001f43a6c965cab1f
1/* 2 * Copyright (C) 2009-2010 Francisco Jerez. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 */ 26 27#include "nouveau_bufferobj.h" 28#include "nouveau_util.h" 29 30#include "main/bufferobj.h" 31#include "main/image.h" 32 33/* Arbitrary pushbuf length we can assume we can get with a single 34 * WAIT_RING. */ 35#define PUSHBUF_DWORDS 2048 36 37/* Functions to set up struct nouveau_array_state from something like 38 * a GL array or index buffer. */ 39 40static void 41vbo_init_array(struct nouveau_array_state *a, int attr, int stride, 42 int fields, int type, struct gl_buffer_object *obj, 43 const void *ptr, GLboolean map) 44{ 45 a->attr = attr; 46 a->stride = stride; 47 a->fields = fields; 48 a->type = type; 49 50 if (_mesa_is_bufferobj(obj)) { 51 nouveau_bo_ref(to_nouveau_bufferobj(obj)->bo, &a->bo); 52 a->offset = (intptr_t)ptr; 53 54 if (map) { 55 nouveau_bo_map(a->bo, NOUVEAU_BO_RD); 56 a->buf = a->bo->map + a->offset; 57 } else { 58 a->buf = NULL; 59 } 60 61 } else { 62 nouveau_bo_ref(NULL, &a->bo); 63 a->offset = 0; 64 65 if (map) 66 a->buf = ptr; 67 else 68 a->buf = NULL; 69 } 70 71 if (a->buf) 72 get_array_extract(a, &a->extract_u, &a->extract_f); 73} 74 75static void 76vbo_deinit_array(struct nouveau_array_state *a) 77{ 78 if (a->bo) { 79 if (a->bo->map) 80 nouveau_bo_unmap(a->bo); 81 nouveau_bo_ref(NULL, &a->bo); 82 } 83 84 a->buf = NULL; 85 a->fields = 0; 86} 87 88static void 89vbo_init_arrays(GLcontext *ctx, const struct _mesa_index_buffer *ib, 90 const struct gl_client_array **arrays) 91{ 92 struct nouveau_render_state *render = to_render_state(ctx); 93 int i; 94 95 if (ib) 96 vbo_init_array(&render->ib, 0, 0, ib->count, ib->type, 97 ib->obj, ib->ptr, GL_TRUE); 98 99 for (i = 0; i < render->attr_count; i++) { 100 int attr = render->map[i]; 101 102 if (attr >= 0) { 103 const struct gl_client_array *array = arrays[attr]; 104 int stride; 105 106 if (render->mode == VBO && 107 !_mesa_is_bufferobj(array->BufferObj)) 108 /* Pack client buffers. */ 109 stride = align(_mesa_sizeof_type(array->Type) 110 * array->Size, 4); 111 else 112 stride = array->StrideB; 113 114 vbo_init_array(&render->attrs[attr], attr, 115 stride, array->Size, array->Type, 116 array->BufferObj, array->Ptr, 117 render->mode == IMM); 118 } 119 } 120} 121 122static void 123vbo_deinit_arrays(GLcontext *ctx, const struct _mesa_index_buffer *ib, 124 const struct gl_client_array **arrays) 125{ 126 struct nouveau_render_state *render = to_render_state(ctx); 127 int i; 128 129 if (ib) 130 vbo_deinit_array(&render->ib); 131 132 for (i = 0; i < render->attr_count; i++) { 133 int *attr = &render->map[i]; 134 135 if (*attr >= 0) { 136 vbo_deinit_array(&render->attrs[*attr]); 137 *attr = -1; 138 } 139 } 140 141 render->attr_count = 0; 142} 143 144/* Make some rendering decisions from the GL context. */ 145 146static void 147vbo_choose_render_mode(GLcontext *ctx, const struct gl_client_array **arrays) 148{ 149 struct nouveau_render_state *render = to_render_state(ctx); 150 int i; 151 152 render->mode = VBO; 153 154 if (ctx->Light.Enabled) { 155 for (i = 0; i < MAT_ATTRIB_MAX; i++) { 156 if (arrays[VERT_ATTRIB_GENERIC0 + i]->StrideB) { 157 render->mode = IMM; 158 break; 159 } 160 } 161 } 162 163 if (render->mode == VBO) 164 render->attr_count = NUM_VERTEX_ATTRS; 165 else 166 render->attr_count = 0; 167} 168 169static void 170vbo_emit_attr(GLcontext *ctx, const struct gl_client_array **arrays, int attr) 171{ 172 struct nouveau_channel *chan = context_chan(ctx); 173 struct nouveau_render_state *render = to_render_state(ctx); 174 const struct gl_client_array *array = arrays[attr]; 175 struct nouveau_array_state *a = &render->attrs[attr]; 176 RENDER_LOCALS(ctx); 177 178 if (!array->StrideB) { 179 if (attr >= VERT_ATTRIB_GENERIC0) 180 /* nouveau_update_state takes care of materials. */ 181 return; 182 183 /* Constant attribute. */ 184 vbo_init_array(a, attr, array->StrideB, array->Size, 185 array->Type, array->BufferObj, array->Ptr, 186 GL_TRUE); 187 EMIT_IMM(ctx, a, 0); 188 vbo_deinit_array(a); 189 190 } else { 191 /* Varying attribute. */ 192 struct nouveau_attr_info *info = &TAG(vertex_attrs)[attr]; 193 194 if (render->mode == VBO) { 195 render->map[info->vbo_index] = attr; 196 render->vertex_size += array->_ElementSize; 197 } else { 198 render->map[render->attr_count++] = attr; 199 render->vertex_size += 4 * info->imm_fields; 200 } 201 } 202} 203 204#define MAT(a) (VERT_ATTRIB_GENERIC0 + MAT_ATTRIB_##a) 205 206static void 207vbo_choose_attrs(GLcontext *ctx, const struct gl_client_array **arrays) 208{ 209 struct nouveau_render_state *render = to_render_state(ctx); 210 int i; 211 212 /* Reset the vertex size. */ 213 render->vertex_size = 0; 214 215 vbo_emit_attr(ctx, arrays, VERT_ATTRIB_COLOR0); 216 if (ctx->Fog.ColorSumEnabled && !ctx->Light.Enabled) 217 vbo_emit_attr(ctx, arrays, VERT_ATTRIB_COLOR1); 218 219 for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) { 220 if (ctx->Texture._EnabledCoordUnits & (1 << i)) 221 vbo_emit_attr(ctx, arrays, VERT_ATTRIB_TEX0 + i); 222 } 223 224 if (ctx->Fog.Enabled && ctx->Fog.FogCoordinateSource == GL_FOG_COORD) 225 vbo_emit_attr(ctx, arrays, VERT_ATTRIB_FOG); 226 227 if (ctx->Light.Enabled || 228 (ctx->Texture._GenFlags & TEXGEN_NEED_NORMALS)) 229 vbo_emit_attr(ctx, arrays, VERT_ATTRIB_NORMAL); 230 231 if (ctx->Light.Enabled) { 232 vbo_emit_attr(ctx, arrays, MAT(FRONT_AMBIENT)); 233 vbo_emit_attr(ctx, arrays, MAT(FRONT_DIFFUSE)); 234 vbo_emit_attr(ctx, arrays, MAT(FRONT_SPECULAR)); 235 vbo_emit_attr(ctx, arrays, MAT(FRONT_SHININESS)); 236 237 if (ctx->Light.Model.TwoSide) { 238 vbo_emit_attr(ctx, arrays, MAT(BACK_AMBIENT)); 239 vbo_emit_attr(ctx, arrays, MAT(BACK_DIFFUSE)); 240 vbo_emit_attr(ctx, arrays, MAT(BACK_SPECULAR)); 241 vbo_emit_attr(ctx, arrays, MAT(BACK_SHININESS)); 242 } 243 } 244 245 vbo_emit_attr(ctx, arrays, VERT_ATTRIB_POS); 246} 247 248static unsigned 249get_max_client_stride(GLcontext *ctx, const struct gl_client_array **arrays) 250{ 251 struct nouveau_render_state *render = to_render_state(ctx); 252 int i, s = 0; 253 254 for (i = 0; i < render->attr_count; i++) { 255 int attr = render->map[i]; 256 257 if (attr >= 0) { 258 const struct gl_client_array *a = arrays[attr]; 259 260 if (!_mesa_is_bufferobj(a->BufferObj)) 261 s = MAX2(a->StrideB, s); 262 } 263 } 264 265 return s; 266} 267 268static void 269TAG(vbo_render_prims)(GLcontext *ctx, const struct gl_client_array **arrays, 270 const struct _mesa_prim *prims, GLuint nr_prims, 271 const struct _mesa_index_buffer *ib, 272 GLboolean index_bounds_valid, 273 GLuint min_index, GLuint max_index); 274 275static GLboolean 276vbo_maybe_split(GLcontext *ctx, const struct gl_client_array **arrays, 277 const struct _mesa_prim *prims, GLuint nr_prims, 278 const struct _mesa_index_buffer *ib, 279 GLuint min_index, GLuint max_index) 280{ 281 struct nouveau_context *nctx = to_nouveau_context(ctx); 282 struct nouveau_render_state *render = to_render_state(ctx); 283 unsigned pushbuf_avail = PUSHBUF_DWORDS - 2 * (nctx->bo.count + 284 render->attr_count), 285 vert_avail = get_max_vertices(ctx, NULL, pushbuf_avail), 286 idx_avail = get_max_vertices(ctx, ib, pushbuf_avail); 287 int stride; 288 289 /* Try to keep client buffers smaller than the scratch BOs. */ 290 if (render->mode == VBO && 291 (stride = get_max_client_stride(ctx, arrays))) 292 vert_avail = MIN2(vert_avail, 293 RENDER_SCRATCH_SIZE / stride); 294 295 if (max_index - min_index > vert_avail || 296 (ib && ib->count > idx_avail)) { 297 struct split_limits limits = { 298 .max_verts = vert_avail, 299 .max_indices = idx_avail, 300 .max_vb_size = ~0, 301 }; 302 303 vbo_split_prims(ctx, arrays, prims, nr_prims, ib, min_index, 304 max_index, TAG(vbo_render_prims), &limits); 305 return GL_TRUE; 306 } 307 308 return GL_FALSE; 309} 310 311/* VBO rendering path. */ 312 313static void 314vbo_bind_vertices(GLcontext *ctx, const struct gl_client_array **arrays, 315 GLint basevertex, GLuint min_index, GLuint max_index) 316{ 317 struct nouveau_render_state *render = to_render_state(ctx); 318 int i; 319 320 for (i = 0; i < NUM_VERTEX_ATTRS; i++) { 321 int attr = render->map[i]; 322 323 if (attr >= 0) { 324 const struct gl_client_array *array = arrays[attr]; 325 struct nouveau_array_state *a = &render->attrs[attr]; 326 unsigned delta = (basevertex + min_index) 327 * array->StrideB; 328 329 if (a->bo) { 330 a->offset = (intptr_t)array->Ptr + delta; 331 } else { 332 int j, n = max_index - min_index + 1; 333 char *sp = (char *)array->Ptr + delta; 334 char *dp = get_scratch_vbo(ctx, n * a->stride, 335 &a->bo, &a->offset); 336 337 for (j = 0; j < n; j++) 338 memcpy(dp + j * a->stride, 339 sp + j * array->StrideB, 340 a->stride); 341 } 342 } 343 } 344 345 TAG(render_bind_vertices)(ctx); 346} 347 348static void 349vbo_draw_vbo(GLcontext *ctx, const struct gl_client_array **arrays, 350 const struct _mesa_prim *prims, GLuint nr_prims, 351 const struct _mesa_index_buffer *ib, GLuint min_index, 352 GLuint max_index) 353{ 354 struct nouveau_channel *chan = context_chan(ctx); 355 dispatch_t dispatch; 356 int delta = -min_index, basevertex = 0, i; 357 RENDER_LOCALS(ctx); 358 359 get_array_dispatch(&to_render_state(ctx)->ib, &dispatch); 360 361 TAG(render_set_format)(ctx); 362 363 for (i = 0; i < nr_prims; i++) { 364 unsigned start = prims[i].start, 365 count = prims[i].count; 366 367 if (i == 0 || basevertex != prims[i].basevertex) { 368 basevertex = prims[i].basevertex; 369 vbo_bind_vertices(ctx, arrays, basevertex, 370 min_index, max_index); 371 } 372 373 if (count > get_max_vertices(ctx, ib, AVAIL_RING(chan))) 374 WAIT_RING(chan, PUSHBUF_DWORDS); 375 376 BATCH_BEGIN(nvgl_primitive(prims[i].mode)); 377 dispatch(ctx, start, delta, count); 378 BATCH_END(); 379 } 380} 381 382/* Immediate rendering path. */ 383 384static unsigned 385extract_id(struct nouveau_array_state *a, int i, int j) 386{ 387 return j; 388} 389 390static void 391vbo_draw_imm(GLcontext *ctx, const struct gl_client_array **arrays, 392 const struct _mesa_prim *prims, GLuint nr_prims, 393 const struct _mesa_index_buffer *ib, GLuint min_index, 394 GLuint max_index) 395{ 396 struct nouveau_render_state *render = to_render_state(ctx); 397 struct nouveau_channel *chan = context_chan(ctx); 398 extract_u_t extract = ib ? render->ib.extract_u : extract_id; 399 int i, j, k; 400 RENDER_LOCALS(ctx); 401 402 for (i = 0; i < nr_prims; i++) { 403 unsigned start = prims[i].start, 404 end = start + prims[i].count; 405 406 if (prims[i].count > get_max_vertices(ctx, ib, 407 AVAIL_RING(chan))) 408 WAIT_RING(chan, PUSHBUF_DWORDS); 409 410 BATCH_BEGIN(nvgl_primitive(prims[i].mode)); 411 412 for (; start < end; start++) { 413 j = prims[i].basevertex + 414 extract(&render->ib, 0, start); 415 416 for (k = 0; k < render->attr_count; k++) 417 EMIT_IMM(ctx, &render->attrs[render->map[k]], 418 j); 419 } 420 421 BATCH_END(); 422 } 423} 424 425/* draw_prims entry point when we're doing hw-tnl. */ 426 427static void 428TAG(vbo_render_prims)(GLcontext *ctx, const struct gl_client_array **arrays, 429 const struct _mesa_prim *prims, GLuint nr_prims, 430 const struct _mesa_index_buffer *ib, 431 GLboolean index_bounds_valid, 432 GLuint min_index, GLuint max_index) 433{ 434 struct nouveau_render_state *render = to_render_state(ctx); 435 436 if (!index_bounds_valid) 437 vbo_get_minmax_index(ctx, prims, ib, &min_index, &max_index); 438 439 vbo_choose_render_mode(ctx, arrays); 440 vbo_choose_attrs(ctx, arrays); 441 442 if (vbo_maybe_split(ctx, arrays, prims, nr_prims, ib, min_index, 443 max_index)) 444 return; 445 446 vbo_init_arrays(ctx, ib, arrays); 447 448 if (render->mode == VBO) 449 vbo_draw_vbo(ctx, arrays, prims, nr_prims, ib, min_index, 450 max_index); 451 else 452 vbo_draw_imm(ctx, arrays, prims, nr_prims, ib, min_index, 453 max_index); 454 455 vbo_deinit_arrays(ctx, ib, arrays); 456} 457