1 /**************************************************************************
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
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.
34 * XXX: work in progress
36 * \author José Fonseca <jrfonseca@tungstengraphics.com>
37 * \author Keith Whitwell <keith@tungstengraphics.com>
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"
49 #include "i915_context.h"
51 #include "i915_batch.h"
52 #include "i915_state.h"
55 #define VBUF_MAP_BUFFER
58 * Primitive renderer for i915.
60 struct i915_vbuf_render
{
61 struct vbuf_render base
;
63 struct i915_context
*i915
;
65 /** Vertex size in bytes */
68 /** Software primitive */
71 /** Hardware primitive */
74 /** Genereate a vertex list */
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_hw_offset
; /**< offset that we program the hardware with */
82 size_t vbo_sw_offset
; /**< offset that we work with */
83 size_t vbo_index
; /**< index offset to be added to all indices */
86 size_t vbo_max_index
; /**< index offset to be added to all indices */
88 #ifndef VBUF_MAP_BUFFER
89 size_t map_used_start
;
97 * Basically a cast wrapper.
99 static INLINE
struct i915_vbuf_render
*
100 i915_vbuf_render(struct vbuf_render
*render
)
103 return (struct i915_vbuf_render
*)render
;
107 * If vbo state differs between renderer and context
108 * push state to the context. This function pushes
109 * hw_offset to i915->vbo_offset and vbo to i915->vbo.
112 * May updates context vbo_offset and vbo fields.
115 i915_vbuf_update_vbo_state(struct vbuf_render
*render
)
117 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
118 struct i915_context
*i915
= i915_render
->i915
;
120 if (i915
->vbo
!= i915_render
->vbo
||
121 i915
->vbo_offset
!= i915_render
->vbo_hw_offset
) {
122 i915
->vbo
= i915_render
->vbo
;
123 i915
->vbo_offset
= i915_render
->vbo_hw_offset
;
124 i915
->dirty
|= I915_NEW_VBO
;
129 * Callback exported to the draw module.
130 * Returns the current vertex_info.
133 * If state is dirty update derived state.
135 static const struct vertex_info
*
136 i915_vbuf_render_get_vertex_info(struct vbuf_render
*render
)
138 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
139 struct i915_context
*i915
= i915_render
->i915
;
142 /* make sure we have up to date vertex layout */
143 i915_update_derived(i915
);
146 return &i915
->current
.vertex_info
;
150 * Reserve space in the vbo for vertices.
156 i915_vbuf_render_reserve(struct i915_vbuf_render
*i915_render
, size_t size
)
158 struct i915_context
*i915
= i915_render
->i915
;
160 if (i915_render
->vbo_size
< size
+ i915_render
->vbo_sw_offset
)
163 if (i915
->vbo_flushed
)
170 * Allocate a new vbo buffer should there not be enough space for
171 * the requested number of vertices by the draw module.
174 * Updates hw_offset, sw_offset, index and allocates a new buffer.
175 * Will set i915->vbo to null on buffer allocation.
178 i915_vbuf_render_new_buf(struct i915_vbuf_render
*i915_render
, size_t size
)
180 struct i915_context
*i915
= i915_render
->i915
;
181 struct i915_winsys
*iws
= i915
->iws
;
183 if (i915_render
->vbo
) {
184 iws
->buffer_unmap(iws
, i915_render
->vbo
);
185 iws
->buffer_destroy(iws
, i915_render
->vbo
);
187 * XXX If buffers where referenced then this should be done in
188 * update_vbo_state but since they arn't and malloc likes to reuse
189 * memory we need to set it to null
192 i915_render
->vbo
= NULL
;
195 i915
->vbo_flushed
= 0;
197 i915_render
->vbo_size
= MAX2(size
, i915_render
->vbo_alloc_size
);
198 i915_render
->vbo_hw_offset
= 0;
199 i915_render
->vbo_sw_offset
= 0;
200 i915_render
->vbo_index
= 0;
202 #ifndef VBUF_MAP_BUFFER
203 if (i915_render
->vbo_size
> i915_render
->map_size
) {
204 i915_render
->map_size
= i915_render
->vbo_size
;
205 FREE(i915_render
->vbo_ptr
);
206 i915_render
->vbo_ptr
= MALLOC(i915_render
->map_size
);
210 i915_render
->vbo
= iws
->buffer_create(iws
, i915_render
->vbo_size
,
212 i915_render
->vbo_ptr
= iws
->buffer_map(iws
, i915_render
->vbo
, TRUE
);
216 * Callback exported to the draw module.
219 * Updates hw_offset, sw_offset, index and may allocate
220 * a new buffer. Also updates may update the vbo state
221 * on the i915 context.
224 i915_vbuf_render_allocate_vertices(struct vbuf_render
*render
,
228 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
229 size_t size
= (size_t)vertex_size
* (size_t)nr_vertices
;
233 * Align sw_offset with first multiple of vertex size from hw_offset.
234 * Set index to be the multiples from from hw_offset to sw_offset.
235 * i915_vbuf_render_new_buf will reset index, sw_offset, hw_offset
236 * when it allocates a new buffer this is correct.
239 offset
= i915_render
->vbo_sw_offset
- i915_render
->vbo_hw_offset
;
240 offset
= util_align_npot(offset
, vertex_size
);
241 i915_render
->vbo_sw_offset
= i915_render
->vbo_hw_offset
+ offset
;
242 i915_render
->vbo_index
= offset
/ vertex_size
;
245 if (!i915_vbuf_render_reserve(i915_render
, size
))
246 i915_vbuf_render_new_buf(i915_render
, size
);
249 * If a new buffer has been alocated sw_offset,
250 * hw_offset & index will be reset by new_buf
253 i915_render
->vertex_size
= vertex_size
;
255 i915_vbuf_update_vbo_state(render
);
257 if (!i915_render
->vbo
)
263 i915_vbuf_render_map_vertices(struct vbuf_render
*render
)
265 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
266 struct i915_context
*i915
= i915_render
->i915
;
268 if (i915
->vbo_flushed
)
269 debug_printf("%s bad vbo flush occured stalling on hw\n", __FUNCTION__
);
271 #ifdef VBUF_MAP_BUFFER
272 return (unsigned char *)i915_render
->vbo_ptr
+ i915_render
->vbo_sw_offset
;
274 return (unsigned char *)i915_render
->vbo_ptr
;
279 i915_vbuf_render_unmap_vertices(struct vbuf_render
*render
,
283 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
284 struct i915_context
*i915
= i915_render
->i915
;
285 struct i915_winsys
*iws
= i915
->iws
;
287 i915_render
->vbo_max_index
= max_index
;
288 i915_render
->vbo_max_used
= MAX2(i915_render
->vbo_max_used
, i915_render
->vertex_size
* (max_index
+ 1));
289 #ifdef VBUF_MAP_BUFFER
292 i915_render
->map_used_start
= i915_render
->vertex_size
* min_index
;
293 i915_render
->map_used_end
= i915_render
->vertex_size
* (max_index
+ 1);
294 iws
->buffer_write(iws
, i915_render
->vbo
,
295 i915_render
->map_used_start
+ i915_render
->vbo_sw_offset
,
296 i915_render
->map_used_end
- i915_render
->map_used_start
,
297 (unsigned char *)i915_render
->vbo_ptr
+ i915_render
->map_used_start
);
303 * Ensure that the given max_index given is not larger ushort max.
304 * If it is larger then ushort max it advanced the hw_offset to the
305 * same position in the vbo as sw_offset and set index to zero.
308 * On failure update hw_offset and index.
311 i915_vbuf_ensure_index_bounds(struct vbuf_render
*render
,
314 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
316 if (max_index
+ i915_render
->vbo_index
< ((1 << 17) - 1))
319 i915_render
->vbo_hw_offset
= i915_render
->vbo_sw_offset
;
320 i915_render
->vbo_index
= 0;
322 i915_vbuf_update_vbo_state(render
);
326 i915_vbuf_render_set_primitive(struct vbuf_render
*render
,
329 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
330 i915_render
->prim
= prim
;
333 case PIPE_PRIM_POINTS
:
334 i915_render
->hwprim
= PRIM3D_POINTLIST
;
335 i915_render
->fallback
= 0;
337 case PIPE_PRIM_LINES
:
338 i915_render
->hwprim
= PRIM3D_LINELIST
;
339 i915_render
->fallback
= 0;
341 case PIPE_PRIM_LINE_LOOP
:
342 i915_render
->hwprim
= PRIM3D_LINELIST
;
343 i915_render
->fallback
= PIPE_PRIM_LINE_LOOP
;
345 case PIPE_PRIM_LINE_STRIP
:
346 i915_render
->hwprim
= PRIM3D_LINESTRIP
;
347 i915_render
->fallback
= 0;
349 case PIPE_PRIM_TRIANGLES
:
350 i915_render
->hwprim
= PRIM3D_TRILIST
;
351 i915_render
->fallback
= 0;
353 case PIPE_PRIM_TRIANGLE_STRIP
:
354 i915_render
->hwprim
= PRIM3D_TRISTRIP
;
355 i915_render
->fallback
= 0;
357 case PIPE_PRIM_TRIANGLE_FAN
:
358 i915_render
->hwprim
= PRIM3D_TRIFAN
;
359 i915_render
->fallback
= 0;
361 case PIPE_PRIM_QUADS
:
362 i915_render
->hwprim
= PRIM3D_TRILIST
;
363 i915_render
->fallback
= PIPE_PRIM_QUADS
;
365 case PIPE_PRIM_QUAD_STRIP
:
366 i915_render
->hwprim
= PRIM3D_TRILIST
;
367 i915_render
->fallback
= PIPE_PRIM_QUAD_STRIP
;
369 case PIPE_PRIM_POLYGON
:
370 i915_render
->hwprim
= PRIM3D_POLY
;
371 i915_render
->fallback
= 0;
374 /* FIXME: Actually, can handle a lot more just fine... */
380 * Used for fallbacks in draw_arrays
383 draw_arrays_generate_indices(struct vbuf_render
*render
,
384 unsigned start
, uint nr
,
387 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
388 struct i915_context
*i915
= i915_render
->i915
;
390 unsigned end
= start
+ nr
+ i915_render
->vbo_index
;
391 start
+= i915_render
->vbo_index
;
395 for (i
= start
; i
+1 < end
; i
+= 2)
396 OUT_BATCH((i
+0) | (i
+1) << 16);
400 case PIPE_PRIM_LINE_LOOP
:
402 for (i
= start
+ 1; i
< end
; i
++)
403 OUT_BATCH((i
-1) | (i
+0) << 16);
404 OUT_BATCH((i
-1) | ( start
) << 16);
407 case PIPE_PRIM_QUADS
:
408 for (i
= start
; i
+ 3 < end
; i
+= 4) {
409 OUT_BATCH((i
+0) | (i
+1) << 16);
410 OUT_BATCH((i
+3) | (i
+1) << 16);
411 OUT_BATCH((i
+2) | (i
+3) << 16);
414 case PIPE_PRIM_QUAD_STRIP
:
415 for (i
= start
; i
+ 3 < end
; i
+= 2) {
416 OUT_BATCH((i
+0) | (i
+1) << 16);
417 OUT_BATCH((i
+3) | (i
+2) << 16);
418 OUT_BATCH((i
+0) | (i
+3) << 16);
427 draw_arrays_calc_nr_indices(uint nr
, unsigned type
)
432 case PIPE_PRIM_LINE_LOOP
:
437 case PIPE_PRIM_QUADS
:
439 case PIPE_PRIM_QUAD_STRIP
:
440 return ((nr
- 2) / 2) * 6;
448 draw_arrays_fallback(struct vbuf_render
*render
,
452 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
453 struct i915_context
*i915
= i915_render
->i915
;
456 nr_indices
= draw_arrays_calc_nr_indices(nr
, i915_render
->fallback
);
460 i915_vbuf_ensure_index_bounds(render
, start
+ nr_indices
);
463 i915_update_derived(i915
);
465 if (i915
->hardware_dirty
)
466 i915_emit_hardware_state(i915
);
468 if (!BEGIN_BATCH(1 + (nr_indices
+ 1)/2)) {
471 /* Make sure state is re-emitted after a flush:
473 i915_emit_hardware_state(i915
);
474 i915
->vbo_flushed
= 1;
476 if (!BEGIN_BATCH(1 + (nr_indices
+ 1)/2)) {
482 OUT_BATCH(_3DPRIMITIVE
|
484 i915_render
->hwprim
|
488 draw_arrays_generate_indices(render
, start
, nr
, i915_render
->fallback
);
490 i915_flush_heuristically(i915
, nr_indices
);
496 i915_vbuf_render_draw_arrays(struct vbuf_render
*render
,
500 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
501 struct i915_context
*i915
= i915_render
->i915
;
503 if (i915_render
->fallback
) {
504 draw_arrays_fallback(render
, start
, nr
);
508 i915_vbuf_ensure_index_bounds(render
, start
+ nr
);
509 start
+= i915_render
->vbo_index
;
512 i915_update_derived(i915
);
514 if (i915
->hardware_dirty
)
515 i915_emit_hardware_state(i915
);
517 if (!BEGIN_BATCH(2)) {
520 /* Make sure state is re-emitted after a flush:
522 i915_emit_hardware_state(i915
);
523 i915
->vbo_flushed
= 1;
525 if (!BEGIN_BATCH(2)) {
531 OUT_BATCH(_3DPRIMITIVE
|
533 PRIM_INDIRECT_SEQUENTIAL
|
534 i915_render
->hwprim
|
536 OUT_BATCH(start
); /* Beginning vertex index */
538 i915_flush_heuristically(i915
, nr
);
544 * Used for normal and fallback emitting of indices
545 * If type is zero normal operation assumed.
548 draw_generate_indices(struct vbuf_render
*render
,
549 const ushort
*indices
,
553 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
554 struct i915_context
*i915
= i915_render
->i915
;
556 unsigned o
= i915_render
->vbo_index
;
560 for (i
= 0; i
+ 1 < nr_indices
; i
+= 2) {
561 OUT_BATCH((o
+indices
[i
]) | (o
+indices
[i
+1]) << 16);
563 if (i
< nr_indices
) {
564 OUT_BATCH((o
+indices
[i
]));
567 case PIPE_PRIM_LINE_LOOP
:
568 if (nr_indices
>= 2) {
569 for (i
= 1; i
< nr_indices
; i
++)
570 OUT_BATCH((o
+indices
[i
-1]) | (o
+indices
[i
]) << 16);
571 OUT_BATCH((o
+indices
[i
-1]) | (o
+indices
[0]) << 16);
574 case PIPE_PRIM_QUADS
:
575 for (i
= 0; i
+ 3 < nr_indices
; i
+= 4) {
576 OUT_BATCH((o
+indices
[i
+0]) | (o
+indices
[i
+1]) << 16);
577 OUT_BATCH((o
+indices
[i
+3]) | (o
+indices
[i
+1]) << 16);
578 OUT_BATCH((o
+indices
[i
+2]) | (o
+indices
[i
+3]) << 16);
581 case PIPE_PRIM_QUAD_STRIP
:
582 for (i
= 0; i
+ 3 < nr_indices
; i
+= 2) {
583 OUT_BATCH((o
+indices
[i
+0]) | (o
+indices
[i
+1]) << 16);
584 OUT_BATCH((o
+indices
[i
+3]) | (o
+indices
[i
+2]) << 16);
585 OUT_BATCH((o
+indices
[i
+0]) | (o
+indices
[i
+3]) << 16);
595 draw_calc_nr_indices(uint nr_indices
, unsigned type
)
600 case PIPE_PRIM_LINE_LOOP
:
602 return nr_indices
* 2;
605 case PIPE_PRIM_QUADS
:
606 return (nr_indices
/ 4) * 6;
607 case PIPE_PRIM_QUAD_STRIP
:
608 return ((nr_indices
- 2) / 2) * 6;
616 i915_vbuf_render_draw_elements(struct vbuf_render
*render
,
617 const ushort
*indices
,
620 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
621 struct i915_context
*i915
= i915_render
->i915
;
622 unsigned save_nr_indices
;
624 save_nr_indices
= nr_indices
;
626 nr_indices
= draw_calc_nr_indices(nr_indices
, i915_render
->fallback
);
630 i915_vbuf_ensure_index_bounds(render
, i915_render
->vbo_max_index
);
633 i915_update_derived(i915
);
635 if (i915
->hardware_dirty
)
636 i915_emit_hardware_state(i915
);
638 if (!BEGIN_BATCH(1 + (nr_indices
+ 1)/2)) {
641 /* Make sure state is re-emitted after a flush:
643 i915_emit_hardware_state(i915
);
644 i915
->vbo_flushed
= 1;
646 if (!BEGIN_BATCH(1 + (nr_indices
+ 1)/2)) {
652 OUT_BATCH(_3DPRIMITIVE
|
654 i915_render
->hwprim
|
657 draw_generate_indices(render
,
660 i915_render
->fallback
);
662 i915_flush_heuristically(i915
, nr_indices
);
668 i915_vbuf_render_release_vertices(struct vbuf_render
*render
)
670 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
672 i915_render
->vbo_sw_offset
+= i915_render
->vbo_max_used
;
673 i915_render
->vbo_max_used
= 0;
676 * Micro optimization, by calling update here we the offset change
677 * will be picked up on the next pipe_context::draw_*.
679 i915_vbuf_update_vbo_state(render
);
683 i915_vbuf_render_destroy(struct vbuf_render
*render
)
685 struct i915_vbuf_render
*i915_render
= i915_vbuf_render(render
);
686 struct i915_context
*i915
= i915_render
->i915
;
687 struct i915_winsys
*iws
= i915
->iws
;
689 if (i915_render
->vbo
) {
691 iws
->buffer_unmap(iws
, i915_render
->vbo
);
692 iws
->buffer_destroy(iws
, i915_render
->vbo
);
699 * Create a new primitive render.
701 static struct vbuf_render
*
702 i915_vbuf_render_create(struct i915_context
*i915
)
704 struct i915_vbuf_render
*i915_render
= CALLOC_STRUCT(i915_vbuf_render
);
705 struct i915_winsys
*iws
= i915
->iws
;
708 i915_render
->i915
= i915
;
710 i915_render
->base
.max_vertex_buffer_bytes
= 16*4096;
712 /* NOTE: it must be such that state and vertices indices fit in a single
715 i915_render
->base
.max_indices
= 16*1024;
717 i915_render
->base
.get_vertex_info
= i915_vbuf_render_get_vertex_info
;
718 i915_render
->base
.allocate_vertices
= i915_vbuf_render_allocate_vertices
;
719 i915_render
->base
.map_vertices
= i915_vbuf_render_map_vertices
;
720 i915_render
->base
.unmap_vertices
= i915_vbuf_render_unmap_vertices
;
721 i915_render
->base
.set_primitive
= i915_vbuf_render_set_primitive
;
722 i915_render
->base
.draw_elements
= i915_vbuf_render_draw_elements
;
723 i915_render
->base
.draw_arrays
= i915_vbuf_render_draw_arrays
;
724 i915_render
->base
.release_vertices
= i915_vbuf_render_release_vertices
;
725 i915_render
->base
.destroy
= i915_vbuf_render_destroy
;
727 #ifndef VBUF_MAP_BUFFER
728 i915_render
->map_size
= 0;
729 i915_render
->map_used_start
= 0;
730 i915_render
->map_used_end
= 0;
733 i915_render
->vbo
= NULL
;
734 i915_render
->vbo_ptr
= NULL
;
735 i915_render
->vbo_size
= 0;
736 i915_render
->vbo_hw_offset
= 0;
737 i915_render
->vbo_sw_offset
= 0;
738 i915_render
->vbo_alloc_size
= i915_render
->base
.max_vertex_buffer_bytes
* 4;
741 i915_render
->pool_used
= FALSE
;
742 i915_render
->pool_buffer_size
= i915_render
->vbo_alloc_size
;
743 i915_render
->pool_fifo
= u_fifo_create(6);
744 for (i
= 0; i
< 6; i
++)
745 u_fifo_add(i915_render
->pool_fifo
,
746 iws
->buffer_create(iws
, i915_render
->pool_buffer_size
,
753 return &i915_render
->base
;
757 * Create a new primitive vbuf/render stage.
759 struct draw_stage
*i915_draw_vbuf_stage(struct i915_context
*i915
)
761 struct vbuf_render
*render
;
762 struct draw_stage
*stage
;
764 render
= i915_vbuf_render_create(i915
);
768 stage
= draw_vbuf_stage(i915
->draw
, render
);
770 render
->destroy(render
);
773 /** TODO JB: this shouldn't be here */
774 draw_set_render(i915
->draw
, render
);