nv50_shader_state.c revision 4c1e7d931dd6e5676297bee23932cc6d66c93cac
1/* 2 * Copyright 2008 Ben Skeggs 3 * Copyright 2010 Christoph Bumiller 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 20 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#include "pipe/p_context.h" 25#include "pipe/p_defines.h" 26#include "pipe/p_state.h" 27#include "util/u_inlines.h" 28 29#include "nv50_context.h" 30 31static void 32nv50_transfer_constbuf(struct nv50_context *nv50, 33 struct pipe_resource *buf, unsigned size, unsigned cbi) 34{ 35 struct pipe_context *pipe = &nv50->pipe; 36 struct pipe_transfer *transfer; 37 struct nouveau_channel *chan = nv50->screen->base.channel; 38 struct nouveau_grobj *tesla = nv50->screen->tesla; 39 uint32_t *map; 40 unsigned count, start; 41 42 map = pipe_buffer_map(pipe, buf, PIPE_TRANSFER_READ, &transfer); 43 if (!map) 44 return; 45 46 count = (buf->width0 + 3) / 4; 47 start = 0; 48 49 while (count) { 50 unsigned nr = AVAIL_RING(chan); 51 52 if (nr < 8) { 53 FIRE_RING(chan); 54 continue; 55 } 56 nr = MIN2(count, nr - 7); 57 nr = MIN2(nr, 2074); 58 59 nv50_screen_reloc_constbuf(nv50->screen, cbi); 60 61 BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 1); 62 OUT_RING (chan, (start << 8) | cbi); 63 BEGIN_RING_NI(chan, tesla, NV50TCL_CB_DATA(0), nr); 64 OUT_RINGp (chan, map, nr); 65 66 count -= nr; 67 start += nr; 68 map += nr; 69 } 70 71 pipe_buffer_unmap(pipe, buf, transfer); 72} 73 74static void 75nv50_program_validate_data(struct nv50_context *nv50, struct nv50_program *p) 76{ 77 struct nouveau_channel *chan = nv50->screen->base.channel; 78 struct nouveau_grobj *tesla = nv50->screen->tesla; 79 unsigned cbi; 80 81 if (p->immd_size) { 82 uint32_t *data = p->immd; 83 unsigned count = p->immd_size / 4; 84 unsigned start = 0; 85 86 while (count) { 87 unsigned nr = AVAIL_RING(chan); 88 89 if (nr < 8) { 90 FIRE_RING(chan); 91 continue; 92 } 93 nr = MIN2(count, nr - 7); 94 nr = MIN2(nr, 2074); 95 96 nv50_screen_reloc_constbuf(nv50->screen, NV50_CB_PMISC); 97 98 BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 1); 99 OUT_RING (chan, (start << 8) | NV50_CB_PMISC); 100 BEGIN_RING_NI(chan, tesla, NV50TCL_CB_DATA(0), nr); 101 OUT_RINGp (chan, data, nr); 102 103 count -= nr; 104 start += nr; 105 data += nr; 106 } 107 } 108 109 /* If the state tracker doesn't change the constbuf, and it is first 110 * validated with a program that doesn't use it, this check prevents 111 * it from even being uploaded. */ 112 /* 113 if (p->parm_size == 0) 114 return; 115 */ 116 117 switch (p->type) { 118 case PIPE_SHADER_VERTEX: 119 cbi = NV50_CB_PVP; 120 break; 121 case PIPE_SHADER_FRAGMENT: 122 cbi = NV50_CB_PFP; 123 break; 124 case PIPE_SHADER_GEOMETRY: 125 cbi = NV50_CB_PGP; 126 break; 127 default: 128 assert(0); 129 return; 130 } 131 132 nv50_transfer_constbuf(nv50, nv50->constbuf[p->type], p->parm_size, cbi); 133} 134 135static void 136nv50_program_validate_code(struct nv50_context *nv50, struct nv50_program *p) 137{ 138 struct nouveau_channel *chan = nv50->screen->base.channel; 139 struct nouveau_grobj *tesla = nv50->screen->tesla; 140 struct nouveau_grobj *eng2d = nv50->screen->eng2d; 141 int ret; 142 unsigned offset; 143 unsigned size = p->code_size; 144 uint32_t *data = p->code; 145 146 assert(p->translated); 147 148 /* TODO: use a single bo (for each type) for shader code */ 149 if (p->bo) 150 return; 151 ret = nouveau_bo_new(chan->device, NOUVEAU_BO_VRAM, 0x100, size, &p->bo); 152 assert(!ret); 153 154 offset = p->code_start = 0; 155 156 BEGIN_RING(chan, eng2d, NV50_2D_DST_FORMAT, 2); 157 OUT_RING (chan, NV50_2D_DST_FORMAT_R8_UNORM); 158 OUT_RING (chan, 1); 159 BEGIN_RING(chan, eng2d, NV50_2D_DST_PITCH, 1); 160 OUT_RING (chan, 0x40000); 161 BEGIN_RING(chan, eng2d, NV50_2D_DST_WIDTH, 2); 162 OUT_RING (chan, 0x10000); 163 OUT_RING (chan, 1); 164 165 while (size) { 166 unsigned nr = size / 4; 167 168 if (AVAIL_RING(chan) < 32) 169 FIRE_RING(chan); 170 171 nr = MIN2(nr, AVAIL_RING(chan) - 18); 172 nr = MIN2(nr, 1792); 173 if (nr < (size / 4)) 174 nr &= ~0x3f; 175 assert(!(size & 3)); 176 177 BEGIN_RING(chan, eng2d, NV50_2D_DST_ADDRESS_HIGH, 2); 178 OUT_RELOCh(chan, p->bo, offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR); 179 OUT_RELOCl(chan, p->bo, offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR); 180 BEGIN_RING(chan, eng2d, NV50_2D_SIFC_BITMAP_ENABLE, 2); 181 OUT_RING (chan, 0); 182 OUT_RING (chan, NV50_2D_SIFC_FORMAT_R8_UNORM); 183 BEGIN_RING(chan, eng2d, NV50_2D_SIFC_WIDTH, 10); 184 OUT_RING (chan, nr * 4); 185 OUT_RING (chan, 1); 186 OUT_RING (chan, 0); 187 OUT_RING (chan, 1); 188 OUT_RING (chan, 0); 189 OUT_RING (chan, 1); 190 OUT_RING (chan, 0); 191 OUT_RING (chan, 0); 192 OUT_RING (chan, 0); 193 OUT_RING (chan, 0); 194 195 BEGIN_RING_NI(chan, eng2d, NV50_2D_SIFC_DATA, nr); 196 OUT_RINGp (chan, data, nr); 197 198 data += nr; 199 offset += nr * 4; 200 size -= nr * 4; 201 } 202 203 BEGIN_RING(chan, tesla, NV50TCL_CODE_CB_FLUSH, 1); 204 OUT_RING (chan, 0); 205} 206 207static void 208nv50_vp_update_stateobj(struct nv50_context *nv50, struct nv50_program *p) 209{ 210 struct nouveau_grobj *tesla = nv50->screen->tesla; 211 struct nouveau_stateobj *so = so_new(5, 7, 2); 212 213 nv50_program_validate_code(nv50, p); 214 215 so_method(so, tesla, NV50TCL_VP_ADDRESS_HIGH, 2); 216 so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | 217 NOUVEAU_BO_HIGH, 0, 0); 218 so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | 219 NOUVEAU_BO_LOW, 0, 0); 220 so_method(so, tesla, NV50TCL_VP_ATTR_EN_0, 2); 221 so_data (so, p->vp.attrs[0]); 222 so_data (so, p->vp.attrs[1]); 223 so_method(so, tesla, NV50TCL_VP_REG_ALLOC_RESULT, 1); 224 so_data (so, p->max_out); 225 so_method(so, tesla, NV50TCL_VP_REG_ALLOC_TEMP, 1); 226 so_data (so, p->max_gpr); 227 so_method(so, tesla, NV50TCL_VP_START_ID, 1); 228 so_data (so, p->code_start); 229 230 so_ref(so, &p->so); 231 so_ref(NULL, &so); 232} 233 234static void 235nv50_fp_update_stateobj(struct nv50_context *nv50, struct nv50_program *p) 236{ 237 struct nouveau_grobj *tesla = nv50->screen->tesla; 238 struct nouveau_stateobj *so = so_new(6, 7, 2); 239 240 nv50_program_validate_code(nv50, p); 241 242 so_method(so, tesla, NV50TCL_FP_ADDRESS_HIGH, 2); 243 so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | 244 NOUVEAU_BO_HIGH, 0, 0); 245 so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | 246 NOUVEAU_BO_LOW, 0, 0); 247 so_method(so, tesla, NV50TCL_FP_REG_ALLOC_TEMP, 1); 248 so_data (so, p->max_gpr); 249 so_method(so, tesla, NV50TCL_FP_RESULT_COUNT, 1); 250 so_data (so, p->max_out); 251 so_method(so, tesla, NV50TCL_FP_CONTROL, 1); 252 so_data (so, p->fp.flags[0]); 253 so_method(so, tesla, NV50TCL_FP_CTRL_UNK196C, 1); 254 so_data (so, p->fp.flags[1]); 255 so_method(so, tesla, NV50TCL_FP_START_ID, 1); 256 so_data (so, p->code_start); 257 258 so_ref(so, &p->so); 259 so_ref(NULL, &so); 260} 261 262static void 263nv50_gp_update_stateobj(struct nv50_context *nv50, struct nv50_program *p) 264{ 265 struct nouveau_grobj *tesla = nv50->screen->tesla; 266 struct nouveau_stateobj *so = so_new(6, 7, 2); 267 268 nv50_program_validate_code(nv50, p); 269 270 so_method(so, tesla, NV50TCL_GP_ADDRESS_HIGH, 2); 271 so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | 272 NOUVEAU_BO_HIGH, 0, 0); 273 so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | 274 NOUVEAU_BO_LOW, 0, 0); 275 so_method(so, tesla, NV50TCL_GP_REG_ALLOC_TEMP, 1); 276 so_data (so, p->max_gpr); 277 so_method(so, tesla, NV50TCL_GP_REG_ALLOC_RESULT, 1); 278 so_data (so, p->max_out); 279 so_method(so, tesla, NV50TCL_GP_OUTPUT_PRIMITIVE_TYPE, 1); 280 so_data (so, p->gp.prim_type); 281 so_method(so, tesla, NV50TCL_GP_VERTEX_OUTPUT_COUNT, 1); 282 so_data (so, p->gp.vert_count); 283 so_method(so, tesla, NV50TCL_GP_START_ID, 1); 284 so_data (so, p->code_start); 285 286 so_ref(so, &p->so); 287 so_ref(NULL, &so); 288} 289 290static boolean 291nv50_program_validate(struct nv50_program *p) 292{ 293 p->translated = nv50_program_tx(p); 294 assert(p->translated); 295 return p->translated; 296} 297 298static INLINE void 299nv50_program_validate_common(struct nv50_context *nv50, struct nv50_program *p) 300{ 301 nv50_program_validate_code(nv50, p); 302 303 if (p->uses_lmem) 304 nv50->req_lmem |= 1 << p->type; 305 else 306 nv50->req_lmem &= ~(1 << p->type); 307} 308 309struct nouveau_stateobj * 310nv50_vertprog_validate(struct nv50_context *nv50) 311{ 312 struct nv50_program *p = nv50->vertprog; 313 struct nouveau_stateobj *so = NULL; 314 315 if (!p->translated) { 316 if (nv50_program_validate(p)) 317 nv50_vp_update_stateobj(nv50, p); 318 else 319 return NULL; 320 } 321 322 if (nv50->dirty & NV50_NEW_VERTPROG_CB) 323 nv50_program_validate_data(nv50, p); 324 325 if (!(nv50->dirty & NV50_NEW_VERTPROG)) 326 return NULL; 327 328 nv50_program_validate_common(nv50, p); 329 330 so_ref(p->so, &so); 331 return so; 332} 333 334struct nouveau_stateobj * 335nv50_fragprog_validate(struct nv50_context *nv50) 336{ 337 struct nv50_program *p = nv50->fragprog; 338 struct nouveau_stateobj *so = NULL; 339 340 if (!p->translated) { 341 if (nv50_program_validate(p)) 342 nv50_fp_update_stateobj(nv50, p); 343 else 344 return NULL; 345 } 346 347 if (nv50->dirty & NV50_NEW_FRAGPROG_CB) 348 nv50_program_validate_data(nv50, p); 349 350 if (!(nv50->dirty & NV50_NEW_FRAGPROG)) 351 return NULL; 352 353 nv50_program_validate_common(nv50, p); 354 355 so_ref(p->so, &so); 356 return so; 357} 358 359struct nouveau_stateobj * 360nv50_geomprog_validate(struct nv50_context *nv50) 361{ 362 struct nv50_program *p = nv50->geomprog; 363 struct nouveau_stateobj *so = NULL; 364 365 if (!p->translated) { 366 if (nv50_program_validate(p)) 367 nv50_gp_update_stateobj(nv50, p); 368 else 369 return NULL; 370 } 371 372 if (nv50->dirty & NV50_NEW_GEOMPROG_CB) 373 nv50_program_validate_data(nv50, p); 374 375 if (!(nv50->dirty & NV50_NEW_GEOMPROG)) 376 return NULL; 377 378 nv50_program_validate_common(nv50, p); 379 380 so_ref(p->so, &so); 381 return so; 382} 383 384/* XXX: this might not work correctly in all cases yet: we assume that 385 * an FP generic input that is not written in the VP is gl_PointCoord. 386 */ 387static uint32_t 388nv50_pntc_replace(struct nv50_context *nv50, uint32_t pntc[8], unsigned m) 389{ 390 struct nv50_program *vp = nv50->vertprog; 391 struct nv50_program *fp = nv50->fragprog; 392 unsigned i, c; 393 394 memset(pntc, 0, 8 * sizeof(uint32_t)); 395 396 if (nv50->geomprog) 397 vp = nv50->geomprog; 398 399 for (i = 0; i < fp->in_nr; i++) { 400 unsigned j, n = util_bitcount(fp->in[i].mask); 401 402 if (fp->in[i].sn != TGSI_SEMANTIC_GENERIC) { 403 m += n; 404 continue; 405 } 406 407 for (j = 0; j < vp->out_nr; ++j) 408 if (vp->out[j].sn == fp->in[i].sn && vp->out[j].si == fp->in[i].si) 409 break; 410 411 if (j < vp->out_nr) { 412 uint32_t en = nv50->rasterizer->pipe.sprite_coord_enable; 413 414 if (!(en & (1 << vp->out[j].si))) { 415 m += n; 416 continue; 417 } 418 } 419 420 /* this is either PointCoord or replaced by sprite coords */ 421 for (c = 0; c < 4; c++) { 422 if (!(fp->in[i].mask & (1 << c))) 423 continue; 424 pntc[m / 8] |= (c + 1) << ((m % 8) * 4); 425 ++m; 426 } 427 } 428 if (nv50->rasterizer->pipe.sprite_coord_mode == PIPE_SPRITE_COORD_LOWER_LEFT) 429 return 0; 430 return (1 << 4); 431} 432 433static int 434nv50_vec4_map(uint32_t *map32, int mid, uint32_t lin[4], 435 struct nv50_varying *in, struct nv50_varying *out) 436{ 437 int c; 438 uint8_t mv = out->mask, mf = in->mask, oid = out->hw; 439 uint8_t *map = (uint8_t *)map32; 440 441 for (c = 0; c < 4; ++c) { 442 if (mf & 1) { 443 if (in->linear) 444 lin[mid / 32] |= 1 << (mid % 32); 445 if (mv & 1) 446 map[mid] = oid; 447 else 448 if (c == 3) 449 map[mid] |= 1; 450 ++mid; 451 } 452 453 oid += mv & 1; 454 mf >>= 1; 455 mv >>= 1; 456 } 457 458 return mid; 459} 460 461struct nouveau_stateobj * 462nv50_fp_linkage_validate(struct nv50_context *nv50) 463{ 464 struct nouveau_grobj *tesla = nv50->screen->tesla; 465 struct nv50_program *vp; 466 struct nv50_program *fp = nv50->fragprog; 467 struct nouveau_stateobj *so; 468 struct nv50_varying dummy; 469 int i, n, c, m; 470 471 uint32_t map[16], lin[4], pntc[8]; 472 473 uint32_t interp = fp->fp.interp; 474 uint32_t colors = fp->fp.colors; 475 uint32_t clip = 0x04; 476 uint32_t psiz = 0x000; 477 uint32_t primid = 0; 478 uint32_t sysval = 0; 479 480 if (nv50->geomprog) { 481 vp = nv50->geomprog; 482 memset(map, 0x80, sizeof(map)); 483 } else { 484 vp = nv50->vertprog; 485 memset(map, 0x40, sizeof(map)); 486 } 487 memset(lin, 0, sizeof(lin)); 488 489 dummy.linear = 0; 490 dummy.mask = 0xf; /* map all components of HPOS */ 491 m = nv50_vec4_map(map, 0, lin, &dummy, &vp->out[0]); 492 493 if (vp->vp.clpd < 0x40) { 494 for (c = 0; c < vp->vp.clpd_nr; ++c) { 495 map[m / 4] |= (vp->vp.clpd + c) << ((m % 4) * 8); 496 ++m; 497 } 498 clip |= vp->vp.clpd_nr << 8; 499 } 500 501 colors |= m << 8; /* adjust BFC0 id */ 502 503 /* if light_twoside is active, it seems FFC0_ID == BFC0_ID is bad */ 504 if (nv50->rasterizer->pipe.light_twoside) { 505 for (i = 0; i < 2; ++i) 506 m = nv50_vec4_map(map, m, lin, 507 &fp->in[fp->vp.bfc[i]], 508 &vp->out[vp->vp.bfc[i]]); 509 } 510 511 colors += m - 4; /* adjust FFC0 id */ 512 interp |= m << 8; /* set mid where 'normal' FP inputs start */ 513 514 dummy.mask = 0x0; 515 for (i = 0; i < fp->in_nr; i++) { 516 for (n = 0; n < vp->out_nr; ++n) 517 if (vp->out[n].sn == fp->in[i].sn && 518 vp->out[n].si == fp->in[i].si) 519 break; 520 521 m = nv50_vec4_map(map, m, lin, 522 &fp->in[i], (n < vp->out_nr) ? &vp->out[n] : &dummy); 523 } 524 525 /* PrimitiveID either is replaced by the system value, or 526 * written by the geometry shader into an output register 527 */ 528 if (fp->gp.primid < 0x40) { 529 i = (m % 4) * 8; 530 map[m / 4] = (map[m / 4] & ~(0xff << i)) | (vp->gp.primid << i); 531 primid = m++; 532 } 533 534 if (nv50->rasterizer->pipe.point_size_per_vertex) { 535 i = (m % 4) * 8; 536 map[m / 4] = (map[m / 4] & ~(0xff << i)) | (vp->vp.psiz << i); 537 psiz = (m++ << 4) | 1; 538 } 539 540 /* now fill the stateobj (at most 28 so_data) */ 541 so = so_new(10, 54, 0); 542 543 n = (m + 3) / 4; 544 assert(m <= 64); 545 if (vp->type == PIPE_SHADER_GEOMETRY) { 546 so_method(so, tesla, NV50TCL_GP_RESULT_MAP_SIZE, 1); 547 so_data (so, m); 548 so_method(so, tesla, NV50TCL_GP_RESULT_MAP(0), n); 549 so_datap (so, map, n); 550 } else { 551 so_method(so, tesla, NV50TCL_VP_GP_BUILTIN_ATTR_EN, 1); 552 so_data (so, vp->vp.attrs[2]); 553 554 so_method(so, tesla, NV50TCL_MAP_SEMANTIC_4, 1); 555 so_data (so, primid); 556 557 so_method(so, tesla, NV50TCL_VP_RESULT_MAP_SIZE, 1); 558 so_data (so, m); 559 so_method(so, tesla, NV50TCL_VP_RESULT_MAP(0), n); 560 so_datap (so, map, n); 561 } 562 563 so_method(so, tesla, NV50TCL_MAP_SEMANTIC_0, 4); 564 so_data (so, colors); 565 so_data (so, clip); 566 so_data (so, sysval); 567 so_data (so, psiz); 568 569 so_method(so, tesla, NV50TCL_FP_INTERPOLANT_CTRL, 1); 570 so_data (so, interp); 571 572 so_method(so, tesla, NV50TCL_NOPERSPECTIVE_BITMAP(0), 4); 573 so_datap (so, lin, 4); 574 575 if (nv50->rasterizer->pipe.point_quad_rasterization) { 576 so_method(so, tesla, NV50TCL_POINT_SPRITE_CTRL, 1); 577 so_data (so, 578 nv50_pntc_replace(nv50, pntc, (interp >> 8) & 0xff)); 579 580 so_method(so, tesla, NV50TCL_POINT_COORD_REPLACE_MAP(0), 8); 581 so_datap (so, pntc, 8); 582 } 583 584 so_method(so, tesla, NV50TCL_GP_ENABLE, 1); 585 so_data (so, (vp->type == PIPE_SHADER_GEOMETRY) ? 1 : 0); 586 587 return so; 588} 589 590static int 591nv50_vp_gp_mapping(uint32_t *map32, int m, 592 struct nv50_program *vp, struct nv50_program *gp) 593{ 594 uint8_t *map = (uint8_t *)map32; 595 int i, j, c; 596 597 for (i = 0; i < gp->in_nr; ++i) { 598 uint8_t oid = 0, mv = 0, mg = gp->in[i].mask; 599 600 for (j = 0; j < vp->out_nr; ++j) { 601 if (vp->out[j].sn == gp->in[i].sn && 602 vp->out[j].si == gp->in[i].si) { 603 mv = vp->out[j].mask; 604 oid = vp->out[j].hw; 605 break; 606 } 607 } 608 609 for (c = 0; c < 4; ++c, mv >>= 1, mg >>= 1) { 610 if (mg & mv & 1) 611 map[m++] = oid; 612 else 613 if (mg & 1) 614 map[m++] = (c == 3) ? 0x41 : 0x40; 615 oid += mv & 1; 616 } 617 } 618 return m; 619} 620 621struct nouveau_stateobj * 622nv50_gp_linkage_validate(struct nv50_context *nv50) 623{ 624 struct nouveau_grobj *tesla = nv50->screen->tesla; 625 struct nouveau_stateobj *so; 626 struct nv50_program *vp = nv50->vertprog; 627 struct nv50_program *gp = nv50->geomprog; 628 uint32_t map[16]; 629 int m = 0; 630 631 if (!gp) 632 return NULL; 633 memset(map, 0, sizeof(map)); 634 635 m = nv50_vp_gp_mapping(map, m, vp, gp); 636 637 so = so_new(3, 24 - 3, 0); 638 639 so_method(so, tesla, NV50TCL_VP_GP_BUILTIN_ATTR_EN, 1); 640 so_data (so, vp->vp.attrs[2] | gp->vp.attrs[2]); 641 642 assert(m <= 32); 643 so_method(so, tesla, NV50TCL_VP_RESULT_MAP_SIZE, 1); 644 so_data (so, m); 645 646 m = (m + 3) / 4; 647 so_method(so, tesla, NV50TCL_VP_RESULT_MAP(0), m); 648 so_datap (so, map, m); 649 650 return so; 651} 652