1 /* 2 * Copyright 2012 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 * 24 */ 25 26 #include "util/u_format.h" 27 #include "util/u_inlines.h" 28 #include "translate/translate.h" 29 30 #include "nouveau_fence.h" 31 #include "nv_object.xml.h" 32 #include "nv30/nv30-40_3d.xml.h" 33 #include "nv30/nv30_context.h" 34 #include "nv30/nv30_format.h" 35 36 static void 37 nv30_emit_vtxattr(struct nv30_context *nv30, struct pipe_vertex_buffer *vb, 38 struct pipe_vertex_element *ve, unsigned attr) 39 { 40 const unsigned nc = util_format_get_nr_components(ve->src_format); 41 struct nouveau_pushbuf *push = nv30->base.pushbuf; 42 struct nv04_resource *res = nv04_resource(vb->buffer); 43 const struct util_format_description *desc = 44 util_format_description(ve->src_format); 45 const void *data; 46 float v[4]; 47 48 data = nouveau_resource_map_offset(&nv30->base, res, vb->buffer_offset + 49 ve->src_offset, NOUVEAU_BO_RD); 50 51 desc->unpack_rgba_float(v, 0, data, 0, 1, 1); 52 53 switch (nc) { 54 case 4: 55 BEGIN_NV04(push, NV30_3D(VTX_ATTR_4F(attr)), 4); 56 PUSH_DATAf(push, v[0]); 57 PUSH_DATAf(push, v[1]); 58 PUSH_DATAf(push, v[2]); 59 PUSH_DATAf(push, v[3]); 60 break; 61 case 3: 62 BEGIN_NV04(push, NV30_3D(VTX_ATTR_3F(attr)), 3); 63 PUSH_DATAf(push, v[0]); 64 PUSH_DATAf(push, v[1]); 65 PUSH_DATAf(push, v[2]); 66 break; 67 case 2: 68 BEGIN_NV04(push, NV30_3D(VTX_ATTR_2F(attr)), 2); 69 PUSH_DATAf(push, v[0]); 70 PUSH_DATAf(push, v[1]); 71 break; 72 case 1: 73 BEGIN_NV04(push, NV30_3D(VTX_ATTR_1F(attr)), 1); 74 PUSH_DATAf(push, v[0]); 75 break; 76 default: 77 assert(0); 78 break; 79 } 80 } 81 82 static inline void 83 nv30_vbuf_range(struct nv30_context *nv30, int vbi, 84 uint32_t *base, uint32_t *size) 85 { 86 assert(nv30->vbo_max_index != ~0); 87 *base = nv30->vbo_min_index * nv30->vtxbuf[vbi].stride; 88 *size = (nv30->vbo_max_index - 89 nv30->vbo_min_index + 1) * nv30->vtxbuf[vbi].stride; 90 } 91 92 static void 93 nv30_prevalidate_vbufs(struct nv30_context *nv30) 94 { 95 struct pipe_vertex_buffer *vb; 96 struct nv04_resource *buf; 97 int i; 98 uint32_t base, size; 99 100 nv30->vbo_fifo = nv30->vbo_user = 0; 101 102 for (i = 0; i < nv30->num_vtxbufs; i++) { 103 vb = &nv30->vtxbuf[i]; 104 if (!vb->stride || !vb->buffer) /* NOTE: user_buffer not implemented */ 105 continue; 106 buf = nv04_resource(vb->buffer); 107 108 /* NOTE: user buffers with temporary storage count as mapped by GPU */ 109 if (!nouveau_resource_mapped_by_gpu(vb->buffer)) { 110 if (nv30->vbo_push_hint) { 111 nv30->vbo_fifo = ~0; 112 continue; 113 } else { 114 if (buf->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY) { 115 nv30->vbo_user |= 1 << i; 116 assert(vb->stride > vb->buffer_offset); 117 nv30_vbuf_range(nv30, i, &base, &size); 118 nouveau_user_buffer_upload(&nv30->base, buf, base, size); 119 } else { 120 nouveau_buffer_migrate(&nv30->base, buf, NOUVEAU_BO_GART); 121 } 122 nv30->base.vbo_dirty = true; 123 } 124 } 125 } 126 } 127 128 static void 129 nv30_update_user_vbufs(struct nv30_context *nv30) 130 { 131 struct nouveau_pushbuf *push = nv30->base.pushbuf; 132 uint32_t base, offset, size; 133 int i; 134 uint32_t written = 0; 135 136 for (i = 0; i < nv30->vertex->num_elements; i++) { 137 struct pipe_vertex_element *ve = &nv30->vertex->pipe[i]; 138 const int b = ve->vertex_buffer_index; 139 struct pipe_vertex_buffer *vb = &nv30->vtxbuf[b]; 140 struct nv04_resource *buf = nv04_resource(vb->buffer); 141 142 if (!(nv30->vbo_user & (1 << b))) 143 continue; 144 145 if (!vb->stride) { 146 nv30_emit_vtxattr(nv30, vb, ve, i); 147 continue; 148 } 149 nv30_vbuf_range(nv30, b, &base, &size); 150 151 if (!(written & (1 << b))) { 152 written |= 1 << b; 153 nouveau_user_buffer_upload(&nv30->base, buf, base, size); 154 } 155 156 offset = vb->buffer_offset + ve->src_offset; 157 158 BEGIN_NV04(push, NV30_3D(VTXBUF(i)), 1); 159 PUSH_RESRC(push, NV30_3D(VTXBUF(i)), BUFCTX_VTXTMP, buf, offset, 160 NOUVEAU_BO_LOW | NOUVEAU_BO_RD, 161 0, NV30_3D_VTXBUF_DMA1); 162 } 163 nv30->base.vbo_dirty = true; 164 } 165 166 static inline void 167 nv30_release_user_vbufs(struct nv30_context *nv30) 168 { 169 uint32_t vbo_user = nv30->vbo_user; 170 171 while (vbo_user) { 172 int i = ffs(vbo_user) - 1; 173 vbo_user &= ~(1 << i); 174 175 nouveau_buffer_release_gpu_storage(nv04_resource(nv30->vtxbuf[i].buffer)); 176 } 177 178 nouveau_bufctx_reset(nv30->bufctx, BUFCTX_VTXTMP); 179 } 180 181 void 182 nv30_vbo_validate(struct nv30_context *nv30) 183 { 184 struct nouveau_pushbuf *push = nv30->base.pushbuf; 185 struct nv30_vertex_stateobj *vertex = nv30->vertex; 186 struct pipe_vertex_element *ve; 187 struct pipe_vertex_buffer *vb; 188 unsigned i, redefine; 189 190 nouveau_bufctx_reset(nv30->bufctx, BUFCTX_VTXBUF); 191 if (!nv30->vertex || nv30->draw_flags) 192 return; 193 194 #ifdef PIPE_ARCH_BIG_ENDIAN 195 if (1) { /* Figure out where the buffers are getting messed up */ 196 #else 197 if (unlikely(vertex->need_conversion)) { 198 #endif 199 nv30->vbo_fifo = ~0; 200 nv30->vbo_user = 0; 201 } else { 202 nv30_prevalidate_vbufs(nv30); 203 } 204 205 if (!PUSH_SPACE(push, 128)) 206 return; 207 208 redefine = MAX2(vertex->num_elements, nv30->state.num_vtxelts); 209 if (redefine == 0) 210 return; 211 212 BEGIN_NV04(push, NV30_3D(VTXFMT(0)), redefine); 213 214 for (i = 0; i < vertex->num_elements; i++) { 215 ve = &vertex->pipe[i]; 216 vb = &nv30->vtxbuf[ve->vertex_buffer_index]; 217 218 if (likely(vb->stride) || nv30->vbo_fifo) 219 PUSH_DATA (push, (vb->stride << 8) | vertex->element[i].state); 220 else 221 PUSH_DATA (push, NV30_3D_VTXFMT_TYPE_V32_FLOAT); 222 } 223 224 for (; i < nv30->state.num_vtxelts; i++) { 225 PUSH_DATA (push, NV30_3D_VTXFMT_TYPE_V32_FLOAT); 226 } 227 228 for (i = 0; i < vertex->num_elements; i++) { 229 struct nv04_resource *res; 230 unsigned offset; 231 bool user; 232 233 ve = &vertex->pipe[i]; 234 vb = &nv30->vtxbuf[ve->vertex_buffer_index]; 235 user = (nv30->vbo_user & (1 << ve->vertex_buffer_index)); 236 237 res = nv04_resource(vb->buffer); 238 239 if (nv30->vbo_fifo || unlikely(vb->stride == 0)) { 240 if (!nv30->vbo_fifo) 241 nv30_emit_vtxattr(nv30, vb, ve, i); 242 continue; 243 } 244 245 offset = ve->src_offset + vb->buffer_offset; 246 247 BEGIN_NV04(push, NV30_3D(VTXBUF(i)), 1); 248 PUSH_RESRC(push, NV30_3D(VTXBUF(i)), user ? BUFCTX_VTXTMP : BUFCTX_VTXBUF, 249 res, offset, NOUVEAU_BO_LOW | NOUVEAU_BO_RD, 250 0, NV30_3D_VTXBUF_DMA1); 251 } 252 253 nv30->state.num_vtxelts = vertex->num_elements; 254 } 255 256 static void * 257 nv30_vertex_state_create(struct pipe_context *pipe, unsigned num_elements, 258 const struct pipe_vertex_element *elements) 259 { 260 struct nv30_vertex_stateobj *so; 261 struct translate_key transkey; 262 unsigned i; 263 264 so = MALLOC(sizeof(*so) + sizeof(*so->element) * num_elements); 265 if (!so) 266 return NULL; 267 memcpy(so->pipe, elements, sizeof(*elements) * num_elements); 268 so->num_elements = num_elements; 269 so->need_conversion = false; 270 271 transkey.nr_elements = 0; 272 transkey.output_stride = 0; 273 274 for (i = 0; i < num_elements; i++) { 275 const struct pipe_vertex_element *ve = &elements[i]; 276 const unsigned vbi = ve->vertex_buffer_index; 277 enum pipe_format fmt = ve->src_format; 278 279 so->element[i].state = nv30_vtxfmt(pipe->screen, fmt)->hw; 280 if (!so->element[i].state) { 281 switch (util_format_get_nr_components(fmt)) { 282 case 1: fmt = PIPE_FORMAT_R32_FLOAT; break; 283 case 2: fmt = PIPE_FORMAT_R32G32_FLOAT; break; 284 case 3: fmt = PIPE_FORMAT_R32G32B32_FLOAT; break; 285 case 4: fmt = PIPE_FORMAT_R32G32B32A32_FLOAT; break; 286 default: 287 assert(0); 288 FREE(so); 289 return NULL; 290 } 291 so->element[i].state = nv30_vtxfmt(pipe->screen, fmt)->hw; 292 so->need_conversion = true; 293 } 294 295 if (1) { 296 unsigned j = transkey.nr_elements++; 297 298 transkey.element[j].type = TRANSLATE_ELEMENT_NORMAL; 299 transkey.element[j].input_format = ve->src_format; 300 transkey.element[j].input_buffer = vbi; 301 transkey.element[j].input_offset = ve->src_offset; 302 transkey.element[j].instance_divisor = ve->instance_divisor; 303 304 transkey.element[j].output_format = fmt; 305 transkey.element[j].output_offset = transkey.output_stride; 306 transkey.output_stride += (util_format_get_stride(fmt, 1) + 3) & ~3; 307 } 308 } 309 310 so->translate = translate_create(&transkey); 311 so->vtx_size = transkey.output_stride / 4; 312 so->vtx_per_packet_max = NV04_PFIFO_MAX_PACKET_LEN / MAX2(so->vtx_size, 1); 313 return so; 314 } 315 316 static void 317 nv30_vertex_state_delete(struct pipe_context *pipe, void *hwcso) 318 { 319 struct nv30_vertex_stateobj *so = hwcso; 320 321 if (so->translate) 322 so->translate->release(so->translate); 323 FREE(hwcso); 324 } 325 326 static void 327 nv30_vertex_state_bind(struct pipe_context *pipe, void *hwcso) 328 { 329 struct nv30_context *nv30 = nv30_context(pipe); 330 331 nv30->vertex = hwcso; 332 nv30->dirty |= NV30_NEW_VERTEX; 333 } 334 335 static void 336 nv30_draw_arrays(struct nv30_context *nv30, 337 unsigned mode, unsigned start, unsigned count, 338 unsigned instance_count) 339 { 340 struct nouveau_pushbuf *push = nv30->base.pushbuf; 341 unsigned prim; 342 343 prim = nv30_prim_gl(mode); 344 345 BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1); 346 PUSH_DATA (push, prim); 347 while (count) { 348 const unsigned mpush = 2047 * 256; 349 unsigned npush = (count > mpush) ? mpush : count; 350 unsigned wpush = ((npush + 255) & ~255) >> 8; 351 352 count -= npush; 353 354 BEGIN_NI04(push, NV30_3D(VB_VERTEX_BATCH), wpush); 355 while (npush >= 256) { 356 PUSH_DATA (push, 0xff000000 | start); 357 start += 256; 358 npush -= 256; 359 } 360 361 if (npush) 362 PUSH_DATA (push, ((npush - 1) << 24) | start); 363 } 364 BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1); 365 PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP); 366 } 367 368 static void 369 nv30_draw_elements_inline_u08(struct nouveau_pushbuf *push, const uint8_t *map, 370 unsigned start, unsigned count) 371 { 372 map += start; 373 374 if (count & 1) { 375 BEGIN_NV04(push, NV30_3D(VB_ELEMENT_U32), 1); 376 PUSH_DATA (push, *map++); 377 } 378 379 count >>= 1; 380 while (count) { 381 unsigned npush = MIN2(count, NV04_PFIFO_MAX_PACKET_LEN); 382 count -= npush; 383 384 BEGIN_NI04(push, NV30_3D(VB_ELEMENT_U16), npush); 385 while (npush--) { 386 PUSH_DATA (push, (map[1] << 16) | map[0]); 387 map += 2; 388 } 389 } 390 391 } 392 393 static void 394 nv30_draw_elements_inline_u16(struct nouveau_pushbuf *push, const uint16_t *map, 395 unsigned start, unsigned count) 396 { 397 map += start; 398 399 if (count & 1) { 400 BEGIN_NV04(push, NV30_3D(VB_ELEMENT_U32), 1); 401 PUSH_DATA (push, *map++); 402 } 403 404 count >>= 1; 405 while (count) { 406 unsigned npush = MIN2(count, NV04_PFIFO_MAX_PACKET_LEN); 407 count -= npush; 408 409 BEGIN_NI04(push, NV30_3D(VB_ELEMENT_U16), npush); 410 while (npush--) { 411 PUSH_DATA (push, (map[1] << 16) | map[0]); 412 map += 2; 413 } 414 } 415 } 416 417 static void 418 nv30_draw_elements_inline_u32(struct nouveau_pushbuf *push, const uint32_t *map, 419 unsigned start, unsigned count) 420 { 421 map += start; 422 423 while (count) { 424 const unsigned nr = MIN2(count, NV04_PFIFO_MAX_PACKET_LEN); 425 426 BEGIN_NI04(push, NV30_3D(VB_ELEMENT_U32), nr); 427 PUSH_DATAp(push, map, nr); 428 429 map += nr; 430 count -= nr; 431 } 432 } 433 434 static void 435 nv30_draw_elements_inline_u32_short(struct nouveau_pushbuf *push, 436 const uint32_t *map, 437 unsigned start, unsigned count) 438 { 439 map += start; 440 441 if (count & 1) { 442 BEGIN_NV04(push, NV30_3D(VB_ELEMENT_U32), 1); 443 PUSH_DATA (push, *map++); 444 } 445 446 count >>= 1; 447 while (count) { 448 unsigned npush = MIN2(count, NV04_PFIFO_MAX_PACKET_LEN); 449 count -= npush; 450 451 BEGIN_NI04(push, NV30_3D(VB_ELEMENT_U16), npush); 452 while (npush--) { 453 PUSH_DATA (push, (map[1] << 16) | map[0]); 454 map += 2; 455 } 456 } 457 } 458 459 static void 460 nv30_draw_elements(struct nv30_context *nv30, bool shorten, 461 unsigned mode, unsigned start, unsigned count, 462 unsigned instance_count, int32_t index_bias) 463 { 464 const unsigned index_size = nv30->idxbuf.index_size; 465 struct nouveau_pushbuf *push = nv30->base.pushbuf; 466 struct nouveau_object *eng3d = nv30->screen->eng3d; 467 unsigned prim = nv30_prim_gl(mode); 468 469 if (eng3d->oclass >= NV40_3D_CLASS && index_bias != nv30->state.index_bias) { 470 BEGIN_NV04(push, NV40_3D(VB_ELEMENT_BASE), 1); 471 PUSH_DATA (push, index_bias); 472 nv30->state.index_bias = index_bias; 473 } 474 475 if (eng3d->oclass == NV40_3D_CLASS && index_size > 1 && 476 nv30->idxbuf.buffer) { 477 struct nv04_resource *res = nv04_resource(nv30->idxbuf.buffer); 478 unsigned offset = nv30->idxbuf.offset; 479 480 assert(nouveau_resource_mapped_by_gpu(&res->base)); 481 482 BEGIN_NV04(push, NV30_3D(IDXBUF_OFFSET), 2); 483 PUSH_RESRC(push, NV30_3D(IDXBUF_OFFSET), BUFCTX_IDXBUF, res, offset, 484 NOUVEAU_BO_LOW | NOUVEAU_BO_RD, 0, 0); 485 PUSH_MTHD (push, NV30_3D(IDXBUF_FORMAT), BUFCTX_IDXBUF, res->bo, 486 (index_size == 2) ? 0x00000010 : 0x00000000, 487 res->domain | NOUVEAU_BO_RD, 488 0, NV30_3D_IDXBUF_FORMAT_DMA1); 489 BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1); 490 PUSH_DATA (push, prim); 491 while (count) { 492 const unsigned mpush = 2047 * 256; 493 unsigned npush = (count > mpush) ? mpush : count; 494 unsigned wpush = ((npush + 255) & ~255) >> 8; 495 496 count -= npush; 497 498 BEGIN_NI04(push, NV30_3D(VB_INDEX_BATCH), wpush); 499 while (npush >= 256) { 500 PUSH_DATA (push, 0xff000000 | start); 501 start += 256; 502 npush -= 256; 503 } 504 505 if (npush) 506 PUSH_DATA (push, ((npush - 1) << 24) | start); 507 } 508 BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1); 509 PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP); 510 PUSH_RESET(push, BUFCTX_IDXBUF); 511 } else { 512 const void *data; 513 if (nv30->idxbuf.buffer) 514 data = nouveau_resource_map_offset(&nv30->base, 515 nv04_resource(nv30->idxbuf.buffer), 516 nv30->idxbuf.offset, NOUVEAU_BO_RD); 517 else 518 data = nv30->idxbuf.user_buffer; 519 if (!data) 520 return; 521 522 BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1); 523 PUSH_DATA (push, prim); 524 switch (index_size) { 525 case 1: 526 nv30_draw_elements_inline_u08(push, data, start, count); 527 break; 528 case 2: 529 nv30_draw_elements_inline_u16(push, data, start, count); 530 break; 531 case 4: 532 if (shorten) 533 nv30_draw_elements_inline_u32_short(push, data, start, count); 534 else 535 nv30_draw_elements_inline_u32(push, data, start, count); 536 break; 537 default: 538 assert(0); 539 return; 540 } 541 BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1); 542 PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP); 543 } 544 } 545 546 static void 547 nv30_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info) 548 { 549 struct nv30_context *nv30 = nv30_context(pipe); 550 struct nouveau_pushbuf *push = nv30->base.pushbuf; 551 int i; 552 553 /* For picking only a few vertices from a large user buffer, push is better, 554 * if index count is larger and we expect repeated vertices, suggest upload. 555 */ 556 nv30->vbo_push_hint = /* the 64 is heuristic */ 557 !(info->indexed && 558 ((info->max_index - info->min_index + 64) < info->count)); 559 560 nv30->vbo_min_index = info->min_index; 561 nv30->vbo_max_index = info->max_index; 562 563 if (nv30->vbo_push_hint != !!nv30->vbo_fifo) 564 nv30->dirty |= NV30_NEW_ARRAYS; 565 566 push->user_priv = &nv30->bufctx; 567 if (nv30->vbo_user && !(nv30->dirty & (NV30_NEW_VERTEX | NV30_NEW_ARRAYS))) 568 nv30_update_user_vbufs(nv30); 569 570 nv30_state_validate(nv30, ~0, true); 571 if (nv30->draw_flags) { 572 nv30_render_vbo(pipe, info); 573 return; 574 } else 575 if (nv30->vbo_fifo) { 576 nv30_push_vbo(nv30, info); 577 return; 578 } 579 580 for (i = 0; i < nv30->num_vtxbufs && !nv30->base.vbo_dirty; ++i) { 581 if (!nv30->vtxbuf[i].buffer) 582 continue; 583 if (nv30->vtxbuf[i].buffer->flags & PIPE_RESOURCE_FLAG_MAP_COHERENT) 584 nv30->base.vbo_dirty = true; 585 } 586 587 if (!nv30->base.vbo_dirty && nv30->idxbuf.buffer && 588 nv30->idxbuf.buffer->flags & PIPE_RESOURCE_FLAG_MAP_COHERENT) 589 nv30->base.vbo_dirty = true; 590 591 if (nv30->base.vbo_dirty) { 592 BEGIN_NV04(push, NV30_3D(VTX_CACHE_INVALIDATE_1710), 1); 593 PUSH_DATA (push, 0); 594 nv30->base.vbo_dirty = false; 595 } 596 597 if (!info->indexed) { 598 nv30_draw_arrays(nv30, 599 info->mode, info->start, info->count, 600 info->instance_count); 601 } else { 602 bool shorten = info->max_index <= 65535; 603 604 if (info->primitive_restart != nv30->state.prim_restart) { 605 if (info->primitive_restart) { 606 BEGIN_NV04(push, NV40_3D(PRIM_RESTART_ENABLE), 2); 607 PUSH_DATA (push, 1); 608 PUSH_DATA (push, info->restart_index); 609 610 if (info->restart_index > 65535) 611 shorten = false; 612 } else { 613 BEGIN_NV04(push, NV40_3D(PRIM_RESTART_ENABLE), 1); 614 PUSH_DATA (push, 0); 615 } 616 nv30->state.prim_restart = info->primitive_restart; 617 } else 618 if (info->primitive_restart) { 619 BEGIN_NV04(push, NV40_3D(PRIM_RESTART_INDEX), 1); 620 PUSH_DATA (push, info->restart_index); 621 622 if (info->restart_index > 65535) 623 shorten = false; 624 } 625 626 nv30_draw_elements(nv30, shorten, 627 info->mode, info->start, info->count, 628 info->instance_count, info->index_bias); 629 } 630 631 nv30_state_release(nv30); 632 nv30_release_user_vbufs(nv30); 633 } 634 635 void 636 nv30_vbo_init(struct pipe_context *pipe) 637 { 638 pipe->create_vertex_elements_state = nv30_vertex_state_create; 639 pipe->delete_vertex_elements_state = nv30_vertex_state_delete; 640 pipe->bind_vertex_elements_state = nv30_vertex_state_bind; 641 pipe->draw_vbo = nv30_draw_vbo; 642 } 643