i915g: Change state code in vbuf code
[mesa.git] / src / gallium / drivers / i915 / i915_prim_vbuf.c
1 /**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 /**
29 * \file
30 * Build post-transformation, post-clipping vertex buffers and element
31 * lists by hooking into the end of the primitive pipeline and
32 * manipulating the vertex_id field in the vertex headers.
33 *
34 * XXX: work in progress
35 *
36 * \author José Fonseca <jrfonseca@tungstengraphics.com>
37 * \author Keith Whitwell <keith@tungstengraphics.com>
38 */
39
40
41 #include "draw/draw_context.h"
42 #include "draw/draw_vbuf.h"
43 #include "util/u_debug.h"
44 #include "util/u_inlines.h"
45 #include "util/u_math.h"
46 #include "util/u_memory.h"
47 #include "util/u_fifo.h"
48
49 #include "i915_context.h"
50 #include "i915_reg.h"
51 #include "i915_batch.h"
52 #include "i915_state.h"
53
54
55 #undef VBUF_USE_FIFO
56 #undef VBUF_MAP_BUFFER
57
58 /**
59 * Primitive renderer for i915.
60 */
61 struct i915_vbuf_render {
62 struct vbuf_render base;
63
64 struct i915_context *i915;
65
66 /** Vertex size in bytes */
67 size_t vertex_size;
68
69 /** Software primitive */
70 unsigned prim;
71
72 /** Hardware primitive */
73 unsigned hwprim;
74
75 /** Genereate a vertex list */
76 unsigned fallback;
77
78 /* Stuff for the vbo */
79 struct i915_winsys_buffer *vbo;
80 size_t vbo_size; /**< current size of allocated buffer */
81 size_t vbo_alloc_size; /**< minimum buffer size to allocate */
82 size_t vbo_offset;
83 void *vbo_ptr;
84 size_t vbo_max_used;
85
86 #ifndef VBUF_MAP_BUFFER
87 size_t map_used_start;
88 size_t map_used_end;
89 size_t map_size;
90 #endif
91
92 #ifdef VBUF_USE_FIFO
93 /* Stuff for the pool */
94 struct util_fifo *pool_fifo;
95 unsigned pool_used;
96 unsigned pool_buffer_size;
97 boolean pool_not_used;
98 #endif
99 };
100
101
102 /**
103 * Basically a cast wrapper.
104 */
105 static INLINE struct i915_vbuf_render *
106 i915_vbuf_render(struct vbuf_render *render)
107 {
108 assert(render);
109 return (struct i915_vbuf_render *)render;
110 }
111
112 static void
113 i915_vbuf_update_vbo_state(struct vbuf_render *render)
114 {
115 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
116 struct i915_context *i915 = i915_render->i915;
117
118 if (i915->vbo != i915_render->vbo ||
119 i915->vbo_offset != i915_render->vbo_offset) {
120 i915->vbo = i915_render->vbo;
121 i915->vbo_offset = i915_render->vbo_offset;
122 i915->dirty |= I915_NEW_VBO;
123 }
124 }
125
126 static const struct vertex_info *
127 i915_vbuf_render_get_vertex_info(struct vbuf_render *render)
128 {
129 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
130 struct i915_context *i915 = i915_render->i915;
131
132 if (i915->dirty) {
133 /* make sure we have up to date vertex layout */
134 i915_update_derived(i915);
135 }
136
137 return &i915->current.vertex_info;
138 }
139
140 static boolean
141 i915_vbuf_render_reserve(struct i915_vbuf_render *i915_render, size_t size)
142 {
143 struct i915_context *i915 = i915_render->i915;
144
145 if (i915_render->vbo_size < size + i915_render->vbo_offset)
146 return FALSE;
147
148 if (i915->vbo_flushed)
149 return FALSE;
150
151 return TRUE;
152 }
153
154 static void
155 i915_vbuf_render_new_buf(struct i915_vbuf_render *i915_render, size_t size)
156 {
157 struct i915_context *i915 = i915_render->i915;
158 struct i915_winsys *iws = i915->iws;
159
160 if (i915_render->vbo) {
161 #ifdef VBUF_USE_FIFO
162 if (i915_render->pool_not_used)
163 iws->buffer_destroy(iws, i915_render->vbo);
164 else
165 u_fifo_add(i915_render->pool_fifo, i915_render->vbo);
166 i915_render->vbo = NULL;
167 #else
168 iws->buffer_destroy(iws, i915_render->vbo);
169 #endif
170 }
171
172 i915->vbo_flushed = 0;
173
174 i915_render->vbo_size = MAX2(size, i915_render->vbo_alloc_size);
175 i915_render->vbo_offset = 0;
176
177 #ifndef VBUF_MAP_BUFFER
178 if (i915_render->vbo_size > i915_render->map_size) {
179 i915_render->map_size = i915_render->vbo_size;
180 FREE(i915_render->vbo_ptr);
181 i915_render->vbo_ptr = MALLOC(i915_render->map_size);
182 }
183 #endif
184
185 #ifdef VBUF_USE_FIFO
186 if (i915_render->vbo_size != i915_render->pool_buffer_size) {
187 i915_render->pool_not_used = TRUE;
188 i915_render->vbo = iws->buffer_create(iws, i915_render->vbo_size, 64,
189 I915_NEW_VERTEX);
190 } else {
191 i915_render->pool_not_used = FALSE;
192
193 if (i915_render->pool_used >= 2) {
194 FLUSH_BATCH(NULL);
195 i915->vbo_flushed = 0;
196 i915_render->pool_used = 0;
197 }
198 u_fifo_pop(i915_render->pool_fifo, (void**)&i915_render->vbo);
199 }
200 #else
201 i915_render->vbo = iws->buffer_create(iws, i915_render->vbo_size,
202 64, I915_NEW_VERTEX);
203 #endif
204 }
205
206 static boolean
207 i915_vbuf_render_allocate_vertices(struct vbuf_render *render,
208 ushort vertex_size,
209 ushort nr_vertices)
210 {
211 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
212 size_t size = (size_t)vertex_size * (size_t)nr_vertices;
213
214 if (!i915_vbuf_render_reserve(i915_render, size)) {
215 #ifdef VBUF_USE_FIFO
216 /* incase we flushed reset the number of pool buffers used */
217 if (i915->vbo_flushed)
218 i915_render->pool_used = 0;
219 #endif
220 i915_vbuf_render_new_buf(i915_render, size);
221 }
222
223 i915_render->vertex_size = vertex_size;
224
225 i915_vbuf_update_vbo_state(render);
226
227 if (!i915_render->vbo)
228 return FALSE;
229 return TRUE;
230 }
231
232 static void *
233 i915_vbuf_render_map_vertices(struct vbuf_render *render)
234 {
235 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
236 struct i915_context *i915 = i915_render->i915;
237 struct i915_winsys *iws = i915->iws;
238
239 if (i915->vbo_flushed)
240 debug_printf("%s bad vbo flush occured stalling on hw\n", __FUNCTION__);
241
242 #ifdef VBUF_MAP_BUFFER
243 i915_render->vbo_ptr = iws->buffer_map(iws, i915_render->vbo, TRUE);
244 return (unsigned char *)i915_render->vbo_ptr + i915_render->vbo_offset;
245 #else
246 (void)iws;
247 return (unsigned char *)i915_render->vbo_ptr;
248 #endif
249 }
250
251 static void
252 i915_vbuf_render_unmap_vertices(struct vbuf_render *render,
253 ushort min_index,
254 ushort max_index)
255 {
256 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
257 struct i915_context *i915 = i915_render->i915;
258 struct i915_winsys *iws = i915->iws;
259
260 i915_render->vbo_max_used = MAX2(i915_render->vbo_max_used, i915_render->vertex_size * (max_index + 1));
261 #ifdef VBUF_MAP_BUFFER
262 iws->buffer_unmap(iws, i915_render->vbo);
263 #else
264 i915_render->map_used_start = i915_render->vertex_size * min_index;
265 i915_render->map_used_end = i915_render->vertex_size * (max_index + 1);
266 iws->buffer_write(iws, i915_render->vbo,
267 i915_render->map_used_start + i915_render->vbo_offset,
268 i915_render->map_used_end - i915_render->map_used_start,
269 (unsigned char *)i915_render->vbo_ptr + i915_render->map_used_start);
270
271 #endif
272 }
273
274 static boolean
275 i915_vbuf_render_set_primitive(struct vbuf_render *render,
276 unsigned prim)
277 {
278 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
279 i915_render->prim = prim;
280
281 switch(prim) {
282 case PIPE_PRIM_POINTS:
283 i915_render->hwprim = PRIM3D_POINTLIST;
284 i915_render->fallback = 0;
285 return TRUE;
286 case PIPE_PRIM_LINES:
287 i915_render->hwprim = PRIM3D_LINELIST;
288 i915_render->fallback = 0;
289 return TRUE;
290 case PIPE_PRIM_LINE_LOOP:
291 i915_render->hwprim = PRIM3D_LINELIST;
292 i915_render->fallback = PIPE_PRIM_LINE_LOOP;
293 return TRUE;
294 case PIPE_PRIM_LINE_STRIP:
295 i915_render->hwprim = PRIM3D_LINESTRIP;
296 i915_render->fallback = 0;
297 return TRUE;
298 case PIPE_PRIM_TRIANGLES:
299 i915_render->hwprim = PRIM3D_TRILIST;
300 i915_render->fallback = 0;
301 return TRUE;
302 case PIPE_PRIM_TRIANGLE_STRIP:
303 i915_render->hwprim = PRIM3D_TRISTRIP;
304 i915_render->fallback = 0;
305 return TRUE;
306 case PIPE_PRIM_TRIANGLE_FAN:
307 i915_render->hwprim = PRIM3D_TRIFAN;
308 i915_render->fallback = 0;
309 return TRUE;
310 case PIPE_PRIM_QUADS:
311 i915_render->hwprim = PRIM3D_TRILIST;
312 i915_render->fallback = PIPE_PRIM_QUADS;
313 return TRUE;
314 case PIPE_PRIM_QUAD_STRIP:
315 i915_render->hwprim = PRIM3D_TRILIST;
316 i915_render->fallback = PIPE_PRIM_QUAD_STRIP;
317 return TRUE;
318 case PIPE_PRIM_POLYGON:
319 i915_render->hwprim = PRIM3D_POLY;
320 i915_render->fallback = 0;
321 return TRUE;
322 default:
323 /* FIXME: Actually, can handle a lot more just fine... */
324 return FALSE;
325 }
326 }
327
328 /**
329 * Used for fallbacks in draw_arrays
330 */
331 static void
332 draw_arrays_generate_indices(struct vbuf_render *render,
333 unsigned start, uint nr,
334 unsigned type)
335 {
336 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
337 struct i915_context *i915 = i915_render->i915;
338 unsigned i;
339 unsigned end = start + nr;
340 switch(type) {
341 case 0:
342 for (i = start; i+1 < end; i += 2)
343 OUT_BATCH((i+0) | (i+1) << 16);
344 if (i < end)
345 OUT_BATCH(i);
346 break;
347 case PIPE_PRIM_LINE_LOOP:
348 if (nr >= 2) {
349 for (i = start + 1; i < end; i++)
350 OUT_BATCH((i-0) | (i+0) << 16);
351 OUT_BATCH((i-0) | ( start) << 16);
352 }
353 break;
354 case PIPE_PRIM_QUADS:
355 for (i = start; i + 3 < end; i += 4) {
356 OUT_BATCH((i+0) | (i+1) << 16);
357 OUT_BATCH((i+3) | (i+1) << 16);
358 OUT_BATCH((i+2) | (i+3) << 16);
359 }
360 break;
361 case PIPE_PRIM_QUAD_STRIP:
362 for (i = start; i + 3 < end; i += 2) {
363 OUT_BATCH((i+0) | (i+1) << 16);
364 OUT_BATCH((i+3) | (i+2) << 16);
365 OUT_BATCH((i+0) | (i+3) << 16);
366 }
367 break;
368 default:
369 assert(0);
370 }
371 }
372
373 static unsigned
374 draw_arrays_calc_nr_indices(uint nr, unsigned type)
375 {
376 switch (type) {
377 case 0:
378 return nr;
379 case PIPE_PRIM_LINE_LOOP:
380 if (nr >= 2)
381 return nr * 2;
382 else
383 return 0;
384 case PIPE_PRIM_QUADS:
385 return (nr / 4) * 6;
386 case PIPE_PRIM_QUAD_STRIP:
387 return ((nr - 2) / 2) * 6;
388 default:
389 assert(0);
390 return 0;
391 }
392 }
393
394 static void
395 draw_arrays_fallback(struct vbuf_render *render,
396 unsigned start,
397 uint nr)
398 {
399 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
400 struct i915_context *i915 = i915_render->i915;
401 unsigned nr_indices;
402
403 if (i915->dirty)
404 i915_update_derived(i915);
405
406 if (i915->hardware_dirty)
407 i915_emit_hardware_state(i915);
408
409 nr_indices = draw_arrays_calc_nr_indices(nr, i915_render->fallback);
410 if (!nr_indices)
411 return;
412
413 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
414 FLUSH_BATCH(NULL);
415
416 /* Make sure state is re-emitted after a flush:
417 */
418 i915_update_derived(i915);
419 i915_emit_hardware_state(i915);
420 i915->vbo_flushed = 1;
421
422 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
423 assert(0);
424 goto out;
425 }
426 }
427
428 OUT_BATCH(_3DPRIMITIVE |
429 PRIM_INDIRECT |
430 i915_render->hwprim |
431 PRIM_INDIRECT_ELTS |
432 nr_indices);
433
434 draw_arrays_generate_indices(render, start, nr, i915_render->fallback);
435
436 out:
437 return;
438 }
439
440 static void
441 i915_vbuf_render_draw_arrays(struct vbuf_render *render,
442 unsigned start,
443 uint nr)
444 {
445 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
446 struct i915_context *i915 = i915_render->i915;
447
448 if (i915_render->fallback) {
449 draw_arrays_fallback(render, start, nr);
450 return;
451 }
452
453 if (i915->dirty)
454 i915_update_derived(i915);
455
456 if (i915->hardware_dirty)
457 i915_emit_hardware_state(i915);
458
459 if (!BEGIN_BATCH(2, 0)) {
460 FLUSH_BATCH(NULL);
461
462 /* Make sure state is re-emitted after a flush:
463 */
464 i915_update_derived(i915);
465 i915_emit_hardware_state(i915);
466 i915->vbo_flushed = 1;
467
468 if (!BEGIN_BATCH(2, 0)) {
469 assert(0);
470 goto out;
471 }
472 }
473
474 OUT_BATCH(_3DPRIMITIVE |
475 PRIM_INDIRECT |
476 PRIM_INDIRECT_SEQUENTIAL |
477 i915_render->hwprim |
478 nr);
479 OUT_BATCH(start); /* Beginning vertex index */
480
481 out:
482 return;
483 }
484
485 /**
486 * Used for normal and fallback emitting of indices
487 * If type is zero normal operation assumed.
488 */
489 static void
490 draw_generate_indices(struct vbuf_render *render,
491 const ushort *indices,
492 uint nr_indices,
493 unsigned type)
494 {
495 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
496 struct i915_context *i915 = i915_render->i915;
497 unsigned i;
498
499 switch(type) {
500 case 0:
501 for (i = 0; i + 1 < nr_indices; i += 2) {
502 OUT_BATCH(indices[i] | indices[i+1] << 16);
503 }
504 if (i < nr_indices) {
505 OUT_BATCH(indices[i]);
506 }
507 break;
508 case PIPE_PRIM_LINE_LOOP:
509 if (nr_indices >= 2) {
510 for (i = 1; i < nr_indices; i++)
511 OUT_BATCH(indices[i-1] | indices[i] << 16);
512 OUT_BATCH(indices[i-1] | indices[0] << 16);
513 }
514 break;
515 case PIPE_PRIM_QUADS:
516 for (i = 0; i + 3 < nr_indices; i += 4) {
517 OUT_BATCH(indices[i+0] | indices[i+1] << 16);
518 OUT_BATCH(indices[i+3] | indices[i+1] << 16);
519 OUT_BATCH(indices[i+2] | indices[i+3] << 16);
520 }
521 break;
522 case PIPE_PRIM_QUAD_STRIP:
523 for (i = 0; i + 3 < nr_indices; i += 2) {
524 OUT_BATCH(indices[i+0] | indices[i+1] << 16);
525 OUT_BATCH(indices[i+3] | indices[i+2] << 16);
526 OUT_BATCH(indices[i+0] | indices[i+3] << 16);
527 }
528 break;
529 default:
530 assert(0);
531 break;
532 }
533 }
534
535 static unsigned
536 draw_calc_nr_indices(uint nr_indices, unsigned type)
537 {
538 switch (type) {
539 case 0:
540 return nr_indices;
541 case PIPE_PRIM_LINE_LOOP:
542 if (nr_indices >= 2)
543 return nr_indices * 2;
544 else
545 return 0;
546 case PIPE_PRIM_QUADS:
547 return (nr_indices / 4) * 6;
548 case PIPE_PRIM_QUAD_STRIP:
549 return ((nr_indices - 2) / 2) * 6;
550 default:
551 assert(0);
552 return 0;
553 }
554 }
555
556 static void
557 i915_vbuf_render_draw_elements(struct vbuf_render *render,
558 const ushort *indices,
559 uint nr_indices)
560 {
561 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
562 struct i915_context *i915 = i915_render->i915;
563 unsigned save_nr_indices;
564
565 save_nr_indices = nr_indices;
566
567 nr_indices = draw_calc_nr_indices(nr_indices, i915_render->fallback);
568 if (!nr_indices)
569 return;
570
571 if (i915->dirty)
572 i915_update_derived(i915);
573
574 if (i915->hardware_dirty)
575 i915_emit_hardware_state(i915);
576
577 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
578 FLUSH_BATCH(NULL);
579
580 /* Make sure state is re-emitted after a flush:
581 */
582 i915_update_derived(i915);
583 i915_emit_hardware_state(i915);
584 i915->vbo_flushed = 1;
585
586 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
587 assert(0);
588 goto out;
589 }
590 }
591
592 OUT_BATCH(_3DPRIMITIVE |
593 PRIM_INDIRECT |
594 i915_render->hwprim |
595 PRIM_INDIRECT_ELTS |
596 nr_indices);
597 draw_generate_indices(render,
598 indices,
599 save_nr_indices,
600 i915_render->fallback);
601
602 out:
603 return;
604 }
605
606 static void
607 i915_vbuf_render_release_vertices(struct vbuf_render *render)
608 {
609 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
610
611 i915_render->vbo_offset += i915_render->vbo_max_used;
612 i915_render->vbo_max_used = 0;
613
614 /*
615 * Micro optimization, by calling update here we the offset change
616 * will be picked up on the next pipe_context::draw_*.
617 */
618 i915_vbuf_update_vbo_state(render);
619 }
620
621 static void
622 i915_vbuf_render_destroy(struct vbuf_render *render)
623 {
624 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
625 FREE(i915_render);
626 }
627
628 /**
629 * Create a new primitive render.
630 */
631 static struct vbuf_render *
632 i915_vbuf_render_create(struct i915_context *i915)
633 {
634 struct i915_vbuf_render *i915_render = CALLOC_STRUCT(i915_vbuf_render);
635 struct i915_winsys *iws = i915->iws;
636 int i;
637
638 i915_render->i915 = i915;
639
640 i915_render->base.max_vertex_buffer_bytes = 16*4096;
641
642 /* NOTE: it must be such that state and vertices indices fit in a single
643 * batch buffer.
644 */
645 i915_render->base.max_indices = 16*1024;
646
647 i915_render->base.get_vertex_info = i915_vbuf_render_get_vertex_info;
648 i915_render->base.allocate_vertices = i915_vbuf_render_allocate_vertices;
649 i915_render->base.map_vertices = i915_vbuf_render_map_vertices;
650 i915_render->base.unmap_vertices = i915_vbuf_render_unmap_vertices;
651 i915_render->base.set_primitive = i915_vbuf_render_set_primitive;
652 i915_render->base.draw_elements = i915_vbuf_render_draw_elements;
653 i915_render->base.draw_arrays = i915_vbuf_render_draw_arrays;
654 i915_render->base.release_vertices = i915_vbuf_render_release_vertices;
655 i915_render->base.destroy = i915_vbuf_render_destroy;
656
657 #ifndef VBUF_MAP_BUFFER
658 i915_render->map_size = 0;
659 i915_render->map_used_start = 0;
660 i915_render->map_used_end = 0;
661 #endif
662
663 i915_render->vbo = NULL;
664 i915_render->vbo_ptr = NULL;
665 i915_render->vbo_size = 0;
666 i915_render->vbo_offset = 0;
667 i915_render->vbo_alloc_size = i915_render->base.max_vertex_buffer_bytes * 4;
668
669 #ifdef VBUF_USE_POOL
670 i915_render->pool_used = FALSE;
671 i915_render->pool_buffer_size = i915_render->vbo_alloc_size;
672 i915_render->pool_fifo = u_fifo_create(6);
673 for (i = 0; i < 6; i++)
674 u_fifo_add(i915_render->pool_fifo,
675 iws->buffer_create(iws, i915_render->pool_buffer_size, 64,
676 I915_NEW_VERTEX));
677 #else
678 (void)i;
679 (void)iws;
680 #endif
681
682 return &i915_render->base;
683 }
684
685 /**
686 * Create a new primitive vbuf/render stage.
687 */
688 struct draw_stage *i915_draw_vbuf_stage(struct i915_context *i915)
689 {
690 struct vbuf_render *render;
691 struct draw_stage *stage;
692
693 render = i915_vbuf_render_create(i915);
694 if(!render)
695 return NULL;
696
697 stage = draw_vbuf_stage(i915->draw, render);
698 if(!stage) {
699 render->destroy(render);
700 return NULL;
701 }
702 /** TODO JB: this shouldn't be here */
703 draw_set_render(i915->draw, render);
704
705 return stage;
706 }