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