nv50_vbo.c revision 9b233ce7de7923feb4b8ef4e1994baa4f13daeef
1/*
2 * Copyright 2008 Ben Skeggs
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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#include "pipe/p_context.h"
24#include "pipe/p_state.h"
25#include "util/u_inlines.h"
26#include "util/u_format.h"
27
28#include "nouveau/nouveau_util.h"
29#include "nv50_context.h"
30
31static INLINE uint32_t
32nv50_vbo_type_to_hw(enum pipe_format format)
33{
34	const struct util_format_description *desc;
35
36	desc = util_format_description(format);
37	assert(desc);
38
39	switch (desc->channel[0].type) {
40	case UTIL_FORMAT_TYPE_FLOAT:
41		return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_FLOAT;
42	case UTIL_FORMAT_TYPE_UNSIGNED:
43		if (desc->channel[0].normalized) {
44			return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_UNORM;
45		}
46		return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_USCALED;
47	case UTIL_FORMAT_TYPE_SIGNED:
48		if (desc->channel[0].normalized) {
49			return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_SNORM;
50		}
51		return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_SSCALED;
52	/*
53	case PIPE_FORMAT_TYPE_UINT:
54		return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_UINT;
55	case PIPE_FORMAT_TYPE_SINT:
56		return NV50TCL_VERTEX_ARRAY_ATTRIB_TYPE_SINT; */
57	default:
58		return 0;
59	}
60}
61
62static INLINE uint32_t
63nv50_vbo_size_to_hw(unsigned size, unsigned nr_c)
64{
65	static const uint32_t hw_values[] = {
66		0, 0, 0, 0,
67		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_8,
68		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_8_8,
69		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_8_8_8,
70		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_8_8_8_8,
71		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_16,
72		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_16_16,
73		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_16_16_16,
74		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_16_16_16_16,
75		0, 0, 0, 0,
76		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_32,
77		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_32_32,
78		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_32_32_32,
79		NV50TCL_VERTEX_ARRAY_ATTRIB_FORMAT_32_32_32_32 };
80
81	/* we'd also have R11G11B10 and R10G10B10A2 */
82
83	assert(nr_c > 0 && nr_c <= 4);
84
85	if (size > 32)
86		return 0;
87	size >>= (3 - 2);
88
89	return hw_values[size + (nr_c - 1)];
90}
91
92static INLINE uint32_t
93nv50_vbo_vtxelt_to_hw(struct pipe_vertex_element *ve)
94{
95	uint32_t hw_type, hw_size;
96	enum pipe_format pf = ve->src_format;
97	const struct util_format_description *desc;
98	unsigned size, nr_components;
99
100	desc = util_format_description(pf);
101	assert(desc);
102
103	size = util_format_get_component_bits(pf, UTIL_FORMAT_COLORSPACE_RGB, 0);
104	nr_components = util_format_get_nr_components(pf);
105
106	hw_type = nv50_vbo_type_to_hw(pf);
107	hw_size = nv50_vbo_size_to_hw(size, nr_components);
108
109	if (!hw_type || !hw_size) {
110		NOUVEAU_ERR("unsupported vbo format: %s\n", util_format_name(pf));
111		abort();
112		return 0x24e80000;
113	}
114
115	if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_Z) /* BGRA */
116		hw_size |= (1 << 31); /* no real swizzle bits :-( */
117
118	return (hw_type | hw_size);
119}
120
121struct instance {
122	struct nouveau_bo *bo;
123	unsigned delta;
124	unsigned stride;
125	unsigned step;
126	unsigned divisor;
127};
128
129static void
130instance_init(struct nv50_context *nv50, struct instance *a, unsigned first)
131{
132	int i;
133
134	for (i = 0; i < nv50->vtxelt->num_elements; i++) {
135		struct pipe_vertex_element *ve = &nv50->vtxelt->pipe[i];
136		struct pipe_vertex_buffer *vb;
137
138		a[i].divisor = ve->instance_divisor;
139		if (a[i].divisor) {
140			vb = &nv50->vtxbuf[ve->vertex_buffer_index];
141
142			a[i].bo = nouveau_bo(vb->buffer);
143			a[i].stride = vb->stride;
144			a[i].step = first % a[i].divisor;
145			a[i].delta = vb->buffer_offset + ve->src_offset +
146				     (first * a[i].stride);
147		}
148	}
149}
150
151static void
152instance_step(struct nv50_context *nv50, struct instance *a)
153{
154	struct nouveau_channel *chan = nv50->screen->tesla->channel;
155	struct nouveau_grobj *tesla = nv50->screen->tesla;
156	int i;
157
158	for (i = 0; i < nv50->vtxelt->num_elements; i++) {
159		if (!a[i].divisor)
160			continue;
161
162		BEGIN_RING(chan, tesla,
163			   NV50TCL_VERTEX_ARRAY_START_HIGH(i), 2);
164		OUT_RELOCh(chan, a[i].bo, a[i].delta, NOUVEAU_BO_RD |
165			   NOUVEAU_BO_VRAM | NOUVEAU_BO_GART);
166		OUT_RELOCl(chan, a[i].bo, a[i].delta, NOUVEAU_BO_RD |
167			   NOUVEAU_BO_VRAM | NOUVEAU_BO_GART);
168		if (++a[i].step == a[i].divisor) {
169			a[i].step = 0;
170			a[i].delta += a[i].stride;
171		}
172	}
173}
174
175void
176nv50_draw_arrays_instanced(struct pipe_context *pipe,
177			   unsigned mode, unsigned start, unsigned count,
178			   unsigned startInstance, unsigned instanceCount)
179{
180	struct nv50_context *nv50 = nv50_context(pipe);
181	struct nouveau_channel *chan = nv50->screen->tesla->channel;
182	struct nouveau_grobj *tesla = nv50->screen->tesla;
183	struct instance a[16];
184	unsigned prim = nv50_prim(mode);
185
186	instance_init(nv50, a, startInstance);
187	if (!nv50_state_validate(nv50, 10 + 16*3))
188		return;
189
190	if (nv50->vbo_fifo) {
191		nv50_push_elements_instanced(pipe, NULL, 0, mode, start,
192					     count, startInstance,
193					     instanceCount);
194		return;
195	}
196
197	BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 2);
198	OUT_RING  (chan, NV50_CB_AUX | (24 << 8));
199	OUT_RING  (chan, startInstance);
200	while (instanceCount--) {
201		if (AVAIL_RING(chan) < (7 + 16*3)) {
202			FIRE_RING(chan);
203			if (!nv50_state_validate(nv50, 7 + 16*3)) {
204				assert(0);
205				return;
206			}
207		}
208		instance_step(nv50, a);
209
210		BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
211		OUT_RING  (chan, prim);
212		BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BUFFER_FIRST, 2);
213		OUT_RING  (chan, start);
214		OUT_RING  (chan, count);
215		BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
216		OUT_RING  (chan, 0);
217
218		prim |= (1 << 28);
219	}
220}
221
222void
223nv50_draw_arrays(struct pipe_context *pipe, unsigned mode, unsigned start,
224		 unsigned count)
225{
226	nv50_draw_arrays_instanced(pipe, mode, start, count, 0, 1);
227}
228
229struct inline_ctx {
230	struct nv50_context *nv50;
231	void *map;
232};
233
234static void
235inline_elt08(void *priv, unsigned start, unsigned count)
236{
237	struct inline_ctx *ctx = priv;
238	struct nouveau_grobj *tesla = ctx->nv50->screen->tesla;
239	struct nouveau_channel *chan = tesla->channel;
240	uint8_t *map = (uint8_t *)ctx->map + start;
241
242	if (count & 1) {
243		BEGIN_RING(chan, tesla, NV50TCL_VB_ELEMENT_U32, 1);
244		OUT_RING  (chan, map[0]);
245		map++;
246		count &= ~1;
247	}
248
249	count >>= 1;
250	if (!count)
251		return;
252
253	BEGIN_RING_NI(chan, tesla, NV50TCL_VB_ELEMENT_U16, count);
254	while (count--) {
255		OUT_RING(chan, (map[1] << 16) | map[0]);
256		map += 2;
257	}
258}
259
260static void
261inline_elt16(void *priv, unsigned start, unsigned count)
262{
263	struct inline_ctx *ctx = priv;
264	struct nouveau_grobj *tesla = ctx->nv50->screen->tesla;
265	struct nouveau_channel *chan = tesla->channel;
266	uint16_t *map = (uint16_t *)ctx->map + start;
267
268	if (count & 1) {
269		BEGIN_RING(chan, tesla, NV50TCL_VB_ELEMENT_U32, 1);
270		OUT_RING  (chan, map[0]);
271		count &= ~1;
272		map++;
273	}
274
275	count >>= 1;
276	if (!count)
277		return;
278
279	BEGIN_RING_NI(chan, tesla, NV50TCL_VB_ELEMENT_U16, count);
280	while (count--) {
281		OUT_RING(chan, (map[1] << 16) | map[0]);
282		map += 2;
283	}
284}
285
286static void
287inline_elt32(void *priv, unsigned start, unsigned count)
288{
289	struct inline_ctx *ctx = priv;
290	struct nouveau_grobj *tesla = ctx->nv50->screen->tesla;
291	struct nouveau_channel *chan = tesla->channel;
292
293	BEGIN_RING_NI(chan, tesla, NV50TCL_VB_ELEMENT_U32, count);
294	OUT_RINGp    (chan, (uint32_t *)ctx->map + start, count);
295}
296
297static void
298inline_edgeflag(void *priv, boolean enabled)
299{
300	struct inline_ctx *ctx = priv;
301	struct nouveau_grobj *tesla = ctx->nv50->screen->tesla;
302	struct nouveau_channel *chan = tesla->channel;
303
304	BEGIN_RING(chan, tesla, NV50TCL_EDGEFLAG_ENABLE, 1);
305	OUT_RING  (chan, enabled ? 1 : 0);
306}
307
308static void
309nv50_draw_elements_inline(struct pipe_context *pipe,
310			  struct pipe_buffer *indexBuffer, unsigned indexSize,
311			  unsigned mode, unsigned start, unsigned count,
312			  unsigned startInstance, unsigned instanceCount)
313{
314	struct pipe_screen *pscreen = pipe->screen;
315	struct nv50_context *nv50 = nv50_context(pipe);
316	struct nouveau_channel *chan = nv50->screen->tesla->channel;
317	struct nouveau_grobj *tesla = nv50->screen->tesla;
318	struct instance a[16];
319	struct inline_ctx ctx;
320	struct u_split_prim s;
321	boolean nzi = FALSE;
322	unsigned overhead;
323
324	overhead = 16*3; /* potential instance adjustments */
325	overhead += 4; /* Begin()/End() */
326	overhead += 4; /* potential edgeflag disable/reenable */
327	overhead += 3; /* potentially 3 VTX_ELT_U16/U32 packet headers */
328
329	s.priv = &ctx;
330	if (indexSize == 1)
331		s.emit = inline_elt08;
332	else
333	if (indexSize == 2)
334		s.emit = inline_elt16;
335	else
336		s.emit = inline_elt32;
337	s.edge = inline_edgeflag;
338
339	ctx.nv50 = nv50;
340	ctx.map = pipe_buffer_map(pscreen, indexBuffer, PIPE_BUFFER_USAGE_CPU_READ);
341	assert(ctx.map);
342	if (!ctx.map)
343		return;
344
345	instance_init(nv50, a, startInstance);
346	if (!nv50_state_validate(nv50, overhead + 6 + 3))
347		return;
348
349	BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 2);
350	OUT_RING  (chan, NV50_CB_AUX | (24 << 8));
351	OUT_RING  (chan, startInstance);
352	while (instanceCount--) {
353		unsigned max_verts;
354		boolean done;
355
356		u_split_prim_init(&s, mode, start, count);
357		do {
358			if (AVAIL_RING(chan) < (overhead + 6)) {
359				FIRE_RING(chan);
360				if (!nv50_state_validate(nv50, (overhead + 6))) {
361					assert(0);
362					return;
363				}
364			}
365
366			max_verts = AVAIL_RING(chan) - overhead;
367			if (max_verts > 2047)
368				max_verts = 2047;
369			if (indexSize != 4)
370				max_verts <<= 1;
371			instance_step(nv50, a);
372
373			BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
374			OUT_RING  (chan, nv50_prim(s.mode) | (nzi ? (1<<28) : 0));
375			done = u_split_prim_next(&s, max_verts);
376			BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
377			OUT_RING  (chan, 0);
378		} while (!done);
379
380		nzi = TRUE;
381	}
382
383	pipe_buffer_unmap(pscreen, indexBuffer);
384}
385
386void
387nv50_draw_elements_instanced(struct pipe_context *pipe,
388			     struct pipe_buffer *indexBuffer,
389			     unsigned indexSize,
390			     unsigned mode, unsigned start, unsigned count,
391			     unsigned startInstance, unsigned instanceCount)
392{
393	struct nv50_context *nv50 = nv50_context(pipe);
394	struct nouveau_channel *chan = nv50->screen->tesla->channel;
395	struct nouveau_grobj *tesla = nv50->screen->tesla;
396	struct instance a[16];
397	unsigned prim = nv50_prim(mode);
398
399	instance_init(nv50, a, startInstance);
400	if (!nv50_state_validate(nv50, 13 + 16*3))
401		return;
402
403	if (nv50->vbo_fifo) {
404		nv50_push_elements_instanced(pipe, indexBuffer, indexSize,
405					     mode, start, count, startInstance,
406					     instanceCount);
407		return;
408	} else
409	if (!(indexBuffer->usage & PIPE_BUFFER_USAGE_INDEX) || indexSize == 1) {
410		nv50_draw_elements_inline(pipe, indexBuffer, indexSize,
411					  mode, start, count, startInstance,
412					  instanceCount);
413		return;
414	}
415
416	BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 2);
417	OUT_RING  (chan, NV50_CB_AUX | (24 << 8));
418	OUT_RING  (chan, startInstance);
419	while (instanceCount--) {
420		if (AVAIL_RING(chan) < (7 + 16*3)) {
421			FIRE_RING(chan);
422			if (!nv50_state_validate(nv50, 10 + 16*3)) {
423				assert(0);
424				return;
425			}
426		}
427		instance_step(nv50, a);
428
429		BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
430		OUT_RING  (chan, prim);
431		if (indexSize == 4) {
432			BEGIN_RING(chan, tesla, NV50TCL_VB_ELEMENT_U32 | 0x30000, 0);
433			OUT_RING  (chan, count);
434			nouveau_pushbuf_submit(chan, nouveau_bo(indexBuffer),
435					       start << 2, count << 2);
436		} else
437		if (indexSize == 2) {
438			unsigned vb_start = (start & ~1);
439			unsigned vb_end = (start + count + 1) & ~1;
440			unsigned dwords = (vb_end - vb_start) >> 1;
441
442			BEGIN_RING(chan, tesla, NV50TCL_VB_ELEMENT_U16_SETUP, 1);
443			OUT_RING  (chan, ((start & 1) << 31) | count);
444			BEGIN_RING(chan, tesla, NV50TCL_VB_ELEMENT_U16 | 0x30000, 0);
445			OUT_RING  (chan, dwords);
446			nouveau_pushbuf_submit(chan, nouveau_bo(indexBuffer),
447					       vb_start << 1, dwords << 2);
448			BEGIN_RING(chan, tesla, NV50TCL_VB_ELEMENT_U16_SETUP, 1);
449			OUT_RING  (chan, 0);
450		}
451		BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
452		OUT_RING  (chan, 0);
453
454		prim |= (1 << 28);
455	}
456}
457
458void
459nv50_draw_elements(struct pipe_context *pipe,
460		   struct pipe_buffer *indexBuffer, unsigned indexSize,
461		   unsigned mode, unsigned start, unsigned count)
462{
463	nv50_draw_elements_instanced(pipe, indexBuffer, indexSize,
464				     mode, start, count, 0, 1);
465}
466
467static INLINE boolean
468nv50_vbo_static_attrib(struct nv50_context *nv50, unsigned attrib,
469		       struct nouveau_stateobj **pso,
470		       struct pipe_vertex_element *ve,
471		       struct pipe_vertex_buffer *vb)
472
473{
474	struct nouveau_stateobj *so;
475	struct nouveau_grobj *tesla = nv50->screen->tesla;
476	struct nouveau_bo *bo = nouveau_bo(vb->buffer);
477	float v[4];
478	int ret;
479	unsigned nr_components = util_format_get_nr_components(ve->src_format);
480
481	ret = nouveau_bo_map(bo, NOUVEAU_BO_RD);
482	if (ret)
483		return FALSE;
484
485	util_format_read_4f(ve->src_format, v, 0, (uint8_t *)bo->map +
486			    (vb->buffer_offset + ve->src_offset), 0,
487			    0, 0, 1, 1);
488	so = *pso;
489	if (!so)
490		*pso = so = so_new(nv50->vtxelt->num_elements,
491				   nv50->vtxelt->num_elements * 4, 0);
492
493	switch (nr_components) {
494	case 4:
495		so_method(so, tesla, NV50TCL_VTX_ATTR_4F_X(attrib), 4);
496		so_data  (so, fui(v[0]));
497		so_data  (so, fui(v[1]));
498		so_data  (so, fui(v[2]));
499		so_data  (so, fui(v[3]));
500		break;
501	case 3:
502		so_method(so, tesla, NV50TCL_VTX_ATTR_3F_X(attrib), 3);
503		so_data  (so, fui(v[0]));
504		so_data  (so, fui(v[1]));
505		so_data  (so, fui(v[2]));
506		break;
507	case 2:
508		so_method(so, tesla, NV50TCL_VTX_ATTR_2F_X(attrib), 2);
509		so_data  (so, fui(v[0]));
510		so_data  (so, fui(v[1]));
511		break;
512	case 1:
513		if (attrib == nv50->vertprog->cfg.edgeflag_in) {
514			so_method(so, tesla, NV50TCL_EDGEFLAG_ENABLE, 1);
515			so_data  (so, v[0] ? 1 : 0);
516		}
517		so_method(so, tesla, NV50TCL_VTX_ATTR_1F(attrib), 1);
518		so_data  (so, fui(v[0]));
519		break;
520	default:
521		nouveau_bo_unmap(bo);
522		return FALSE;
523	}
524
525	nouveau_bo_unmap(bo);
526	return TRUE;
527}
528
529void
530nv50_vtxelt_construct(struct nv50_vtxelt_stateobj *cso)
531{
532	unsigned i;
533
534	for (i = 0; i < cso->num_elements; ++i) {
535		struct pipe_vertex_element *ve = &cso->pipe[i];
536
537		cso->hw[i] = nv50_vbo_vtxelt_to_hw(ve);
538	}
539}
540
541struct nouveau_stateobj *
542nv50_vbo_validate(struct nv50_context *nv50)
543{
544	struct nouveau_grobj *tesla = nv50->screen->tesla;
545	struct nouveau_stateobj *vtxbuf, *vtxfmt, *vtxattr;
546	unsigned i, n_ve;
547
548	/* don't validate if Gallium took away our buffers */
549	if (nv50->vtxbuf_nr == 0)
550		return NULL;
551
552	if (nv50->screen->force_push ||
553	    nv50->vertprog->cfg.edgeflag_in < 16)
554		nv50->vbo_fifo = 0xffff;
555
556	for (i = 0; i < nv50->vtxbuf_nr; i++) {
557		if (nv50->vtxbuf[i].stride &&
558		    !(nv50->vtxbuf[i].buffer->usage & PIPE_BUFFER_USAGE_VERTEX))
559			nv50->vbo_fifo = 0xffff;
560	}
561
562	n_ve = MAX2(nv50->vtxelt->num_elements, nv50->state.vtxelt_nr);
563
564	vtxattr = NULL;
565	vtxbuf = so_new(n_ve * 2, n_ve * 5, nv50->vtxelt->num_elements * 4);
566	vtxfmt = so_new(1, n_ve, 0);
567	so_method(vtxfmt, tesla, NV50TCL_VERTEX_ARRAY_ATTRIB(0), n_ve);
568
569	for (i = 0; i < nv50->vtxelt->num_elements; i++) {
570		struct pipe_vertex_element *ve = &nv50->vtxelt->pipe[i];
571		struct pipe_vertex_buffer *vb =
572			&nv50->vtxbuf[ve->vertex_buffer_index];
573		struct nouveau_bo *bo = nouveau_bo(vb->buffer);
574		uint32_t hw = nv50->vtxelt->hw[i];
575
576		if (!vb->stride &&
577		    nv50_vbo_static_attrib(nv50, i, &vtxattr, ve, vb)) {
578			so_data(vtxfmt, hw | (1 << 4));
579
580			so_method(vtxbuf, tesla,
581				  NV50TCL_VERTEX_ARRAY_FORMAT(i), 1);
582			so_data  (vtxbuf, 0);
583
584			nv50->vbo_fifo &= ~(1 << i);
585			continue;
586		}
587
588		if (nv50->vbo_fifo) {
589			so_data  (vtxfmt, hw | (ve->instance_divisor ? (1 << 4) : i));
590			so_method(vtxbuf, tesla,
591				  NV50TCL_VERTEX_ARRAY_FORMAT(i), 1);
592			so_data  (vtxbuf, 0);
593			continue;
594		}
595
596		so_data(vtxfmt, hw | i);
597
598		so_method(vtxbuf, tesla, NV50TCL_VERTEX_ARRAY_FORMAT(i), 3);
599		so_data  (vtxbuf, 0x20000000 |
600			  (ve->instance_divisor ? 0 : vb->stride));
601		so_reloc (vtxbuf, bo, vb->buffer_offset +
602			  ve->src_offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_GART |
603			  NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
604		so_reloc (vtxbuf, bo, vb->buffer_offset +
605			  ve->src_offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_GART |
606			  NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
607
608		/* vertex array limits */
609		so_method(vtxbuf, tesla, NV50TCL_VERTEX_ARRAY_LIMIT_HIGH(i), 2);
610		so_reloc (vtxbuf, bo, vb->buffer->size - 1,
611			  NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD |
612			  NOUVEAU_BO_HIGH, 0, 0);
613		so_reloc (vtxbuf, bo, vb->buffer->size - 1,
614			  NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD |
615			  NOUVEAU_BO_LOW, 0, 0);
616	}
617	for (; i < n_ve; ++i) {
618		so_data  (vtxfmt, 0x7e080010);
619
620		so_method(vtxbuf, tesla, NV50TCL_VERTEX_ARRAY_FORMAT(i), 1);
621		so_data  (vtxbuf, 0);
622	}
623	nv50->state.vtxelt_nr = nv50->vtxelt->num_elements;
624
625	so_ref (vtxbuf, &nv50->state.vtxbuf);
626	so_ref (vtxattr, &nv50->state.vtxattr);
627	so_ref (NULL, &vtxbuf);
628	so_ref (NULL, &vtxattr);
629	return vtxfmt;
630}
631
632
633