21d4a9c91f5984bbec816e2cfd5bebfd2e8ebceb
[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_MAP_BUFFER
56
57 /**
58 * Primitive renderer for i915.
59 */
60 struct i915_vbuf_render {
61 struct vbuf_render base;
62
63 struct i915_context *i915;
64
65 /** Vertex size in bytes */
66 size_t vertex_size;
67
68 /** Software primitive */
69 unsigned prim;
70
71 /** Hardware primitive */
72 unsigned hwprim;
73
74 /** Genereate a vertex list */
75 unsigned fallback;
76
77 /* Stuff for the vbo */
78 struct i915_winsys_buffer *vbo;
79 size_t vbo_size; /**< current size of allocated buffer */
80 size_t vbo_alloc_size; /**< minimum buffer size to allocate */
81 size_t vbo_offset;
82 void *vbo_ptr;
83 size_t vbo_max_used;
84
85 #ifndef VBUF_MAP_BUFFER
86 size_t map_used_start;
87 size_t map_used_end;
88 size_t map_size;
89 #endif
90 };
91
92
93 /**
94 * Basically a cast wrapper.
95 */
96 static INLINE struct i915_vbuf_render *
97 i915_vbuf_render(struct vbuf_render *render)
98 {
99 assert(render);
100 return (struct i915_vbuf_render *)render;
101 }
102
103 static void
104 i915_vbuf_update_vbo_state(struct vbuf_render *render)
105 {
106 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
107 struct i915_context *i915 = i915_render->i915;
108
109 if (i915->vbo != i915_render->vbo ||
110 i915->vbo_offset != i915_render->vbo_offset) {
111 i915->vbo = i915_render->vbo;
112 i915->vbo_offset = i915_render->vbo_offset;
113 i915->dirty |= I915_NEW_VBO;
114 }
115 }
116
117 static const struct vertex_info *
118 i915_vbuf_render_get_vertex_info(struct vbuf_render *render)
119 {
120 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
121 struct i915_context *i915 = i915_render->i915;
122
123 if (i915->dirty) {
124 /* make sure we have up to date vertex layout */
125 i915_update_derived(i915);
126 }
127
128 return &i915->current.vertex_info;
129 }
130
131 static boolean
132 i915_vbuf_render_reserve(struct i915_vbuf_render *i915_render, size_t size)
133 {
134 struct i915_context *i915 = i915_render->i915;
135
136 if (i915_render->vbo_size < size + i915_render->vbo_offset)
137 return FALSE;
138
139 if (i915->vbo_flushed)
140 return FALSE;
141
142 return TRUE;
143 }
144
145 static void
146 i915_vbuf_render_new_buf(struct i915_vbuf_render *i915_render, size_t size)
147 {
148 struct i915_context *i915 = i915_render->i915;
149 struct i915_winsys *iws = i915->iws;
150
151 if (i915_render->vbo)
152 iws->buffer_destroy(iws, i915_render->vbo);
153
154 i915->vbo_flushed = 0;
155
156 i915_render->vbo_size = MAX2(size, i915_render->vbo_alloc_size);
157 i915_render->vbo_offset = 0;
158
159 #ifndef VBUF_MAP_BUFFER
160 if (i915_render->vbo_size > i915_render->map_size) {
161 i915_render->map_size = i915_render->vbo_size;
162 FREE(i915_render->vbo_ptr);
163 i915_render->vbo_ptr = MALLOC(i915_render->map_size);
164 }
165 #endif
166
167 i915_render->vbo = iws->buffer_create(iws, i915_render->vbo_size,
168 64, I915_NEW_VERTEX);
169 }
170
171 static boolean
172 i915_vbuf_render_allocate_vertices(struct vbuf_render *render,
173 ushort vertex_size,
174 ushort nr_vertices)
175 {
176 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
177 size_t size = (size_t)vertex_size * (size_t)nr_vertices;
178
179 if (!i915_vbuf_render_reserve(i915_render, size))
180 i915_vbuf_render_new_buf(i915_render, size);
181
182 i915_render->vertex_size = vertex_size;
183
184 i915_vbuf_update_vbo_state(render);
185
186 if (!i915_render->vbo)
187 return FALSE;
188 return TRUE;
189 }
190
191 static void *
192 i915_vbuf_render_map_vertices(struct vbuf_render *render)
193 {
194 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
195 struct i915_context *i915 = i915_render->i915;
196 struct i915_winsys *iws = i915->iws;
197
198 if (i915->vbo_flushed)
199 debug_printf("%s bad vbo flush occured stalling on hw\n", __FUNCTION__);
200
201 #ifdef VBUF_MAP_BUFFER
202 i915_render->vbo_ptr = iws->buffer_map(iws, i915_render->vbo, TRUE);
203 return (unsigned char *)i915_render->vbo_ptr + i915_render->vbo_offset;
204 #else
205 (void)iws;
206 return (unsigned char *)i915_render->vbo_ptr;
207 #endif
208 }
209
210 static void
211 i915_vbuf_render_unmap_vertices(struct vbuf_render *render,
212 ushort min_index,
213 ushort max_index)
214 {
215 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
216 struct i915_context *i915 = i915_render->i915;
217 struct i915_winsys *iws = i915->iws;
218
219 i915_render->vbo_max_used = MAX2(i915_render->vbo_max_used, i915_render->vertex_size * (max_index + 1));
220 #ifdef VBUF_MAP_BUFFER
221 iws->buffer_unmap(iws, i915_render->vbo);
222 #else
223 i915_render->map_used_start = i915_render->vertex_size * min_index;
224 i915_render->map_used_end = i915_render->vertex_size * (max_index + 1);
225 iws->buffer_write(iws, i915_render->vbo,
226 i915_render->map_used_start + i915_render->vbo_offset,
227 i915_render->map_used_end - i915_render->map_used_start,
228 (unsigned char *)i915_render->vbo_ptr + i915_render->map_used_start);
229
230 #endif
231 }
232
233 static boolean
234 i915_vbuf_render_set_primitive(struct vbuf_render *render,
235 unsigned prim)
236 {
237 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
238 i915_render->prim = prim;
239
240 switch(prim) {
241 case PIPE_PRIM_POINTS:
242 i915_render->hwprim = PRIM3D_POINTLIST;
243 i915_render->fallback = 0;
244 return TRUE;
245 case PIPE_PRIM_LINES:
246 i915_render->hwprim = PRIM3D_LINELIST;
247 i915_render->fallback = 0;
248 return TRUE;
249 case PIPE_PRIM_LINE_LOOP:
250 i915_render->hwprim = PRIM3D_LINELIST;
251 i915_render->fallback = PIPE_PRIM_LINE_LOOP;
252 return TRUE;
253 case PIPE_PRIM_LINE_STRIP:
254 i915_render->hwprim = PRIM3D_LINESTRIP;
255 i915_render->fallback = 0;
256 return TRUE;
257 case PIPE_PRIM_TRIANGLES:
258 i915_render->hwprim = PRIM3D_TRILIST;
259 i915_render->fallback = 0;
260 return TRUE;
261 case PIPE_PRIM_TRIANGLE_STRIP:
262 i915_render->hwprim = PRIM3D_TRISTRIP;
263 i915_render->fallback = 0;
264 return TRUE;
265 case PIPE_PRIM_TRIANGLE_FAN:
266 i915_render->hwprim = PRIM3D_TRIFAN;
267 i915_render->fallback = 0;
268 return TRUE;
269 case PIPE_PRIM_QUADS:
270 i915_render->hwprim = PRIM3D_TRILIST;
271 i915_render->fallback = PIPE_PRIM_QUADS;
272 return TRUE;
273 case PIPE_PRIM_QUAD_STRIP:
274 i915_render->hwprim = PRIM3D_TRILIST;
275 i915_render->fallback = PIPE_PRIM_QUAD_STRIP;
276 return TRUE;
277 case PIPE_PRIM_POLYGON:
278 i915_render->hwprim = PRIM3D_POLY;
279 i915_render->fallback = 0;
280 return TRUE;
281 default:
282 /* FIXME: Actually, can handle a lot more just fine... */
283 return FALSE;
284 }
285 }
286
287 /**
288 * Used for fallbacks in draw_arrays
289 */
290 static void
291 draw_arrays_generate_indices(struct vbuf_render *render,
292 unsigned start, uint nr,
293 unsigned type)
294 {
295 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
296 struct i915_context *i915 = i915_render->i915;
297 unsigned i;
298 unsigned end = start + nr;
299 switch(type) {
300 case 0:
301 for (i = start; i+1 < end; i += 2)
302 OUT_BATCH((i+0) | (i+1) << 16);
303 if (i < end)
304 OUT_BATCH(i);
305 break;
306 case PIPE_PRIM_LINE_LOOP:
307 if (nr >= 2) {
308 for (i = start + 1; i < end; i++)
309 OUT_BATCH((i-0) | (i+0) << 16);
310 OUT_BATCH((i-0) | ( start) << 16);
311 }
312 break;
313 case PIPE_PRIM_QUADS:
314 for (i = start; i + 3 < end; i += 4) {
315 OUT_BATCH((i+0) | (i+1) << 16);
316 OUT_BATCH((i+3) | (i+1) << 16);
317 OUT_BATCH((i+2) | (i+3) << 16);
318 }
319 break;
320 case PIPE_PRIM_QUAD_STRIP:
321 for (i = start; i + 3 < end; i += 2) {
322 OUT_BATCH((i+0) | (i+1) << 16);
323 OUT_BATCH((i+3) | (i+2) << 16);
324 OUT_BATCH((i+0) | (i+3) << 16);
325 }
326 break;
327 default:
328 assert(0);
329 }
330 }
331
332 static unsigned
333 draw_arrays_calc_nr_indices(uint nr, unsigned type)
334 {
335 switch (type) {
336 case 0:
337 return nr;
338 case PIPE_PRIM_LINE_LOOP:
339 if (nr >= 2)
340 return nr * 2;
341 else
342 return 0;
343 case PIPE_PRIM_QUADS:
344 return (nr / 4) * 6;
345 case PIPE_PRIM_QUAD_STRIP:
346 return ((nr - 2) / 2) * 6;
347 default:
348 assert(0);
349 return 0;
350 }
351 }
352
353 static void
354 draw_arrays_fallback(struct vbuf_render *render,
355 unsigned start,
356 uint nr)
357 {
358 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
359 struct i915_context *i915 = i915_render->i915;
360 unsigned nr_indices;
361
362 if (i915->dirty)
363 i915_update_derived(i915);
364
365 if (i915->hardware_dirty)
366 i915_emit_hardware_state(i915);
367
368 nr_indices = draw_arrays_calc_nr_indices(nr, i915_render->fallback);
369 if (!nr_indices)
370 return;
371
372 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
373 FLUSH_BATCH(NULL);
374
375 /* Make sure state is re-emitted after a flush:
376 */
377 i915_update_derived(i915);
378 i915_emit_hardware_state(i915);
379 i915->vbo_flushed = 1;
380
381 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
382 assert(0);
383 goto out;
384 }
385 }
386
387 OUT_BATCH(_3DPRIMITIVE |
388 PRIM_INDIRECT |
389 i915_render->hwprim |
390 PRIM_INDIRECT_ELTS |
391 nr_indices);
392
393 draw_arrays_generate_indices(render, start, nr, i915_render->fallback);
394
395 out:
396 return;
397 }
398
399 static void
400 i915_vbuf_render_draw_arrays(struct vbuf_render *render,
401 unsigned start,
402 uint nr)
403 {
404 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
405 struct i915_context *i915 = i915_render->i915;
406
407 if (i915_render->fallback) {
408 draw_arrays_fallback(render, start, nr);
409 return;
410 }
411
412 if (i915->dirty)
413 i915_update_derived(i915);
414
415 if (i915->hardware_dirty)
416 i915_emit_hardware_state(i915);
417
418 if (!BEGIN_BATCH(2, 0)) {
419 FLUSH_BATCH(NULL);
420
421 /* Make sure state is re-emitted after a flush:
422 */
423 i915_update_derived(i915);
424 i915_emit_hardware_state(i915);
425 i915->vbo_flushed = 1;
426
427 if (!BEGIN_BATCH(2, 0)) {
428 assert(0);
429 goto out;
430 }
431 }
432
433 OUT_BATCH(_3DPRIMITIVE |
434 PRIM_INDIRECT |
435 PRIM_INDIRECT_SEQUENTIAL |
436 i915_render->hwprim |
437 nr);
438 OUT_BATCH(start); /* Beginning vertex index */
439
440 out:
441 return;
442 }
443
444 /**
445 * Used for normal and fallback emitting of indices
446 * If type is zero normal operation assumed.
447 */
448 static void
449 draw_generate_indices(struct vbuf_render *render,
450 const ushort *indices,
451 uint nr_indices,
452 unsigned type)
453 {
454 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
455 struct i915_context *i915 = i915_render->i915;
456 unsigned i;
457
458 switch(type) {
459 case 0:
460 for (i = 0; i + 1 < nr_indices; i += 2) {
461 OUT_BATCH(indices[i] | indices[i+1] << 16);
462 }
463 if (i < nr_indices) {
464 OUT_BATCH(indices[i]);
465 }
466 break;
467 case PIPE_PRIM_LINE_LOOP:
468 if (nr_indices >= 2) {
469 for (i = 1; i < nr_indices; i++)
470 OUT_BATCH(indices[i-1] | indices[i] << 16);
471 OUT_BATCH(indices[i-1] | indices[0] << 16);
472 }
473 break;
474 case PIPE_PRIM_QUADS:
475 for (i = 0; i + 3 < nr_indices; i += 4) {
476 OUT_BATCH(indices[i+0] | indices[i+1] << 16);
477 OUT_BATCH(indices[i+3] | indices[i+1] << 16);
478 OUT_BATCH(indices[i+2] | indices[i+3] << 16);
479 }
480 break;
481 case PIPE_PRIM_QUAD_STRIP:
482 for (i = 0; i + 3 < nr_indices; i += 2) {
483 OUT_BATCH(indices[i+0] | indices[i+1] << 16);
484 OUT_BATCH(indices[i+3] | indices[i+2] << 16);
485 OUT_BATCH(indices[i+0] | indices[i+3] << 16);
486 }
487 break;
488 default:
489 assert(0);
490 break;
491 }
492 }
493
494 static unsigned
495 draw_calc_nr_indices(uint nr_indices, unsigned type)
496 {
497 switch (type) {
498 case 0:
499 return nr_indices;
500 case PIPE_PRIM_LINE_LOOP:
501 if (nr_indices >= 2)
502 return nr_indices * 2;
503 else
504 return 0;
505 case PIPE_PRIM_QUADS:
506 return (nr_indices / 4) * 6;
507 case PIPE_PRIM_QUAD_STRIP:
508 return ((nr_indices - 2) / 2) * 6;
509 default:
510 assert(0);
511 return 0;
512 }
513 }
514
515 static void
516 i915_vbuf_render_draw_elements(struct vbuf_render *render,
517 const ushort *indices,
518 uint nr_indices)
519 {
520 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
521 struct i915_context *i915 = i915_render->i915;
522 unsigned save_nr_indices;
523
524 save_nr_indices = nr_indices;
525
526 nr_indices = draw_calc_nr_indices(nr_indices, i915_render->fallback);
527 if (!nr_indices)
528 return;
529
530 if (i915->dirty)
531 i915_update_derived(i915);
532
533 if (i915->hardware_dirty)
534 i915_emit_hardware_state(i915);
535
536 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
537 FLUSH_BATCH(NULL);
538
539 /* Make sure state is re-emitted after a flush:
540 */
541 i915_update_derived(i915);
542 i915_emit_hardware_state(i915);
543 i915->vbo_flushed = 1;
544
545 if (!BEGIN_BATCH(1 + (nr_indices + 1)/2, 1)) {
546 assert(0);
547 goto out;
548 }
549 }
550
551 OUT_BATCH(_3DPRIMITIVE |
552 PRIM_INDIRECT |
553 i915_render->hwprim |
554 PRIM_INDIRECT_ELTS |
555 nr_indices);
556 draw_generate_indices(render,
557 indices,
558 save_nr_indices,
559 i915_render->fallback);
560
561 out:
562 return;
563 }
564
565 static void
566 i915_vbuf_render_release_vertices(struct vbuf_render *render)
567 {
568 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
569
570 i915_render->vbo_offset += i915_render->vbo_max_used;
571 i915_render->vbo_max_used = 0;
572
573 /*
574 * Micro optimization, by calling update here we the offset change
575 * will be picked up on the next pipe_context::draw_*.
576 */
577 i915_vbuf_update_vbo_state(render);
578 }
579
580 static void
581 i915_vbuf_render_destroy(struct vbuf_render *render)
582 {
583 struct i915_vbuf_render *i915_render = i915_vbuf_render(render);
584 FREE(i915_render);
585 }
586
587 /**
588 * Create a new primitive render.
589 */
590 static struct vbuf_render *
591 i915_vbuf_render_create(struct i915_context *i915)
592 {
593 struct i915_vbuf_render *i915_render = CALLOC_STRUCT(i915_vbuf_render);
594 struct i915_winsys *iws = i915->iws;
595 int i;
596
597 i915_render->i915 = i915;
598
599 i915_render->base.max_vertex_buffer_bytes = 16*4096;
600
601 /* NOTE: it must be such that state and vertices indices fit in a single
602 * batch buffer.
603 */
604 i915_render->base.max_indices = 16*1024;
605
606 i915_render->base.get_vertex_info = i915_vbuf_render_get_vertex_info;
607 i915_render->base.allocate_vertices = i915_vbuf_render_allocate_vertices;
608 i915_render->base.map_vertices = i915_vbuf_render_map_vertices;
609 i915_render->base.unmap_vertices = i915_vbuf_render_unmap_vertices;
610 i915_render->base.set_primitive = i915_vbuf_render_set_primitive;
611 i915_render->base.draw_elements = i915_vbuf_render_draw_elements;
612 i915_render->base.draw_arrays = i915_vbuf_render_draw_arrays;
613 i915_render->base.release_vertices = i915_vbuf_render_release_vertices;
614 i915_render->base.destroy = i915_vbuf_render_destroy;
615
616 #ifndef VBUF_MAP_BUFFER
617 i915_render->map_size = 0;
618 i915_render->map_used_start = 0;
619 i915_render->map_used_end = 0;
620 #endif
621
622 i915_render->vbo = NULL;
623 i915_render->vbo_ptr = NULL;
624 i915_render->vbo_size = 0;
625 i915_render->vbo_offset = 0;
626 i915_render->vbo_alloc_size = i915_render->base.max_vertex_buffer_bytes * 4;
627
628 #ifdef VBUF_USE_POOL
629 i915_render->pool_used = FALSE;
630 i915_render->pool_buffer_size = i915_render->vbo_alloc_size;
631 i915_render->pool_fifo = u_fifo_create(6);
632 for (i = 0; i < 6; i++)
633 u_fifo_add(i915_render->pool_fifo,
634 iws->buffer_create(iws, i915_render->pool_buffer_size, 64,
635 I915_NEW_VERTEX));
636 #else
637 (void)i;
638 (void)iws;
639 #endif
640
641 return &i915_render->base;
642 }
643
644 /**
645 * Create a new primitive vbuf/render stage.
646 */
647 struct draw_stage *i915_draw_vbuf_stage(struct i915_context *i915)
648 {
649 struct vbuf_render *render;
650 struct draw_stage *stage;
651
652 render = i915_vbuf_render_create(i915);
653 if(!render)
654 return NULL;
655
656 stage = draw_vbuf_stage(i915->draw, render);
657 if(!stage) {
658 render->destroy(render);
659 return NULL;
660 }
661 /** TODO JB: this shouldn't be here */
662 draw_set_render(i915->draw, render);
663
664 return stage;
665 }