Merge commit 'origin/master' into gallium-sw-api-2
[mesa.git] / src / gallium / drivers / nv50 / nv50_push.c
1 #include "pipe/p_context.h"
2 #include "pipe/p_state.h"
3 #include "util/u_inlines.h"
4 #include "util/u_format.h"
5
6 #include "nouveau/nouveau_util.h"
7 #include "nv50_context.h"
8
9 struct push_context {
10 struct nv50_context *nv50;
11
12 unsigned vtx_size;
13
14 void *idxbuf;
15 unsigned idxsize;
16
17 float edgeflag;
18 int edgeflag_attr;
19
20 struct {
21 void *map;
22 unsigned stride;
23 unsigned divisor;
24 unsigned step;
25 void (*push)(struct nouveau_channel *, void *);
26 } attr[16];
27 unsigned attr_nr;
28 };
29
30 static void
31 emit_b32_1(struct nouveau_channel *chan, void *data)
32 {
33 uint32_t *v = data;
34
35 OUT_RING(chan, v[0]);
36 }
37
38 static void
39 emit_b32_2(struct nouveau_channel *chan, void *data)
40 {
41 uint32_t *v = data;
42
43 OUT_RING(chan, v[0]);
44 OUT_RING(chan, v[1]);
45 }
46
47 static void
48 emit_b32_3(struct nouveau_channel *chan, void *data)
49 {
50 uint32_t *v = data;
51
52 OUT_RING(chan, v[0]);
53 OUT_RING(chan, v[1]);
54 OUT_RING(chan, v[2]);
55 }
56
57 static void
58 emit_b32_4(struct nouveau_channel *chan, void *data)
59 {
60 uint32_t *v = data;
61
62 OUT_RING(chan, v[0]);
63 OUT_RING(chan, v[1]);
64 OUT_RING(chan, v[2]);
65 OUT_RING(chan, v[3]);
66 }
67
68 static void
69 emit_b16_1(struct nouveau_channel *chan, void *data)
70 {
71 uint16_t *v = data;
72
73 OUT_RING(chan, v[0]);
74 }
75
76 static void
77 emit_b16_3(struct nouveau_channel *chan, void *data)
78 {
79 uint16_t *v = data;
80
81 OUT_RING(chan, (v[1] << 16) | v[0]);
82 OUT_RING(chan, v[2]);
83 }
84
85 static void
86 emit_b08_1(struct nouveau_channel *chan, void *data)
87 {
88 uint8_t *v = data;
89
90 OUT_RING(chan, v[0]);
91 }
92
93 static void
94 emit_b08_3(struct nouveau_channel *chan, void *data)
95 {
96 uint8_t *v = data;
97
98 OUT_RING(chan, (v[2] << 16) | (v[1] << 8) | v[0]);
99 }
100
101 static INLINE void
102 emit_vertex(struct push_context *ctx, unsigned n)
103 {
104 struct nouveau_grobj *tesla = ctx->nv50->screen->tesla;
105 struct nouveau_channel *chan = tesla->channel;
106 int i;
107
108 if (ctx->edgeflag_attr < 16) {
109 float *edgeflag = ctx->attr[ctx->edgeflag_attr].map +
110 ctx->attr[ctx->edgeflag_attr].stride * n;
111
112 if (*edgeflag != ctx->edgeflag) {
113 BEGIN_RING(chan, tesla, NV50TCL_EDGEFLAG_ENABLE, 1);
114 OUT_RING (chan, *edgeflag ? 1 : 0);
115 ctx->edgeflag = *edgeflag;
116 }
117 }
118
119 BEGIN_RING_NI(chan, tesla, NV50TCL_VERTEX_DATA, ctx->vtx_size);
120 for (i = 0; i < ctx->attr_nr; i++)
121 ctx->attr[i].push(chan, ctx->attr[i].map + ctx->attr[i].stride * n);
122 }
123
124 static void
125 emit_edgeflag(void *priv, boolean enabled)
126 {
127 struct push_context *ctx = priv;
128 struct nouveau_grobj *tesla = ctx->nv50->screen->tesla;
129 struct nouveau_channel *chan = tesla->channel;
130
131 BEGIN_RING(chan, tesla, NV50TCL_EDGEFLAG_ENABLE, 1);
132 OUT_RING (chan, enabled ? 1 : 0);
133 }
134
135 static void
136 emit_elt08(void *priv, unsigned start, unsigned count)
137 {
138 struct push_context *ctx = priv;
139 uint8_t *idxbuf = ctx->idxbuf;
140
141 while (count--)
142 emit_vertex(ctx, idxbuf[start++]);
143 }
144
145 static void
146 emit_elt16(void *priv, unsigned start, unsigned count)
147 {
148 struct push_context *ctx = priv;
149 uint16_t *idxbuf = ctx->idxbuf;
150
151 while (count--)
152 emit_vertex(ctx, idxbuf[start++]);
153 }
154
155 static void
156 emit_elt32(void *priv, unsigned start, unsigned count)
157 {
158 struct push_context *ctx = priv;
159 uint32_t *idxbuf = ctx->idxbuf;
160
161 while (count--)
162 emit_vertex(ctx, idxbuf[start++]);
163 }
164
165 static void
166 emit_verts(void *priv, unsigned start, unsigned count)
167 {
168 while (count--)
169 emit_vertex(priv, start++);
170 }
171
172 void
173 nv50_push_elements_instanced(struct pipe_context *pipe,
174 struct pipe_buffer *idxbuf, unsigned idxsize,
175 unsigned mode, unsigned start, unsigned count,
176 unsigned i_start, unsigned i_count)
177 {
178 struct nv50_context *nv50 = nv50_context(pipe);
179 struct nouveau_grobj *tesla = nv50->screen->tesla;
180 struct nouveau_channel *chan = tesla->channel;
181 struct push_context ctx;
182 const unsigned p_overhead = 4 + /* begin/end */
183 4; /* potential edgeflag enable/disable */
184 const unsigned v_overhead = 1 + /* VERTEX_DATA packet header */
185 2; /* potential edgeflag modification */
186 struct u_split_prim s;
187 unsigned vtx_size;
188 boolean nzi = FALSE;
189 int i;
190
191 ctx.nv50 = nv50;
192 ctx.attr_nr = 0;
193 ctx.idxbuf = NULL;
194 ctx.vtx_size = 0;
195 ctx.edgeflag = 0.5f;
196 ctx.edgeflag_attr = nv50->vertprog->cfg.edgeflag_in;
197
198 /* map vertex buffers, determine vertex size */
199 for (i = 0; i < nv50->vtxelt->num_elements; i++) {
200 struct pipe_vertex_element *ve = &nv50->vtxelt->pipe[i];
201 struct pipe_vertex_buffer *vb = &nv50->vtxbuf[ve->vertex_buffer_index];
202 struct nouveau_bo *bo = nouveau_bo(vb->buffer);
203 unsigned size, nr_components, n;
204
205 if (!(nv50->vbo_fifo & (1 << i)))
206 continue;
207 n = ctx.attr_nr++;
208
209 if (nouveau_bo_map(bo, NOUVEAU_BO_RD)) {
210 assert(bo->map);
211 return;
212 }
213 ctx.attr[n].map = bo->map + vb->buffer_offset + ve->src_offset;
214 nouveau_bo_unmap(bo);
215
216 ctx.attr[n].stride = vb->stride;
217 ctx.attr[n].divisor = ve->instance_divisor;
218 if (ctx.attr[n].divisor) {
219 ctx.attr[n].step = i_start % ve->instance_divisor;
220 ctx.attr[n].map += i_start * vb->stride;
221 }
222
223 size = util_format_get_component_bits(ve->src_format,
224 UTIL_FORMAT_COLORSPACE_RGB, 0);
225 nr_components = util_format_get_nr_components(ve->src_format);
226 switch (size) {
227 case 8:
228 switch (nr_components) {
229 case 1: ctx.attr[n].push = emit_b08_1; break;
230 case 2: ctx.attr[n].push = emit_b16_1; break;
231 case 3: ctx.attr[n].push = emit_b08_3; break;
232 case 4: ctx.attr[n].push = emit_b32_1; break;
233 }
234 ctx.vtx_size++;
235 break;
236 case 16:
237 switch (nr_components) {
238 case 1: ctx.attr[n].push = emit_b16_1; break;
239 case 2: ctx.attr[n].push = emit_b32_1; break;
240 case 3: ctx.attr[n].push = emit_b16_3; break;
241 case 4: ctx.attr[n].push = emit_b32_2; break;
242 }
243 ctx.vtx_size += (nr_components + 1) >> 1;
244 break;
245 case 32:
246 switch (nr_components) {
247 case 1: ctx.attr[n].push = emit_b32_1; break;
248 case 2: ctx.attr[n].push = emit_b32_2; break;
249 case 3: ctx.attr[n].push = emit_b32_3; break;
250 case 4: ctx.attr[n].push = emit_b32_4; break;
251 }
252 ctx.vtx_size += nr_components;
253 break;
254 default:
255 assert(0);
256 return;
257 }
258 }
259 vtx_size = ctx.vtx_size + v_overhead;
260
261 /* map index buffer, if present */
262 if (idxbuf) {
263 struct nouveau_bo *bo = nouveau_bo(idxbuf);
264
265 if (nouveau_bo_map(bo, NOUVEAU_BO_RD)) {
266 assert(bo->map);
267 return;
268 }
269 ctx.idxbuf = bo->map;
270 ctx.idxsize = idxsize;
271 nouveau_bo_unmap(bo);
272 }
273
274 s.priv = &ctx;
275 s.edge = emit_edgeflag;
276 if (idxbuf) {
277 if (idxsize == 1)
278 s.emit = emit_elt08;
279 else
280 if (idxsize == 2)
281 s.emit = emit_elt16;
282 else
283 s.emit = emit_elt32;
284 } else
285 s.emit = emit_verts;
286
287 /* per-instance loop */
288 BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 2);
289 OUT_RING (chan, NV50_CB_AUX | (24 << 8));
290 OUT_RING (chan, i_start);
291 while (i_count--) {
292 unsigned max_verts;
293 boolean done;
294
295 for (i = 0; i < ctx.attr_nr; i++) {
296 if (!ctx.attr[i].divisor ||
297 ctx.attr[i].divisor != ++ctx.attr[i].step)
298 continue;
299 ctx.attr[i].step = 0;
300 ctx.attr[i].map += ctx.attr[i].stride;
301 }
302
303 u_split_prim_init(&s, mode, start, count);
304 do {
305 if (AVAIL_RING(chan) < p_overhead + (6 * vtx_size)) {
306 FIRE_RING(chan);
307 if (!nv50_state_validate(nv50, p_overhead + (6 * vtx_size))) {
308 assert(0);
309 return;
310 }
311 }
312
313 max_verts = AVAIL_RING(chan);
314 max_verts -= p_overhead;
315 max_verts /= vtx_size;
316
317 BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
318 OUT_RING (chan, nv50_prim(s.mode) | (nzi ? (1 << 28) : 0));
319 done = u_split_prim_next(&s, max_verts);
320 BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
321 OUT_RING (chan, 0);
322 } while (!done);
323
324 nzi = TRUE;
325 }
326 }