3 * Mesa 3-D graphics library
6 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * Keith Whitwell <keith@tungstengraphics.com>
29 /* Split indexed primitives with per-vertex copying.
32 #include "main/glheader.h"
33 #include "main/imports.h"
34 #include "main/macros.h"
35 #include "main/enums.h"
36 #include "main/mtypes.h"
38 #include "vbo_split.h"
42 #define ELT_TABLE_SIZE 16
44 /* Used for vertex-level splitting of indexed buffers. Note that
45 * non-indexed primitives may be converted to indexed in some cases
46 * (eg loops, fans) in order to use this splitting path.
51 const struct gl_client_array
**array
;
52 const struct _mesa_prim
*prim
;
54 const struct _mesa_index_buffer
*ib
;
57 const struct split_limits
*limits
;
62 const struct gl_client_array
*array
;
63 const GLubyte
*src_ptr
;
65 struct gl_client_array dstarray
;
67 } varying
[VERT_ATTRIB_MAX
];
70 const struct gl_client_array
*dstarray_ptr
[VERT_ATTRIB_MAX
];
71 struct _mesa_index_buffer dstib
;
73 GLuint
*translated_elt_buf
;
76 /* A baby hash table to avoid re-emitting (some) duplicate
77 * vertices when splitting indexed primitives.
82 } vert_cache
[ELT_TABLE_SIZE
];
87 GLubyte
*dstptr
; /* dstptr == dstbuf + dstelt_max * vertsize */
88 GLuint dstbuf_size
; /* in vertices */
89 GLuint dstbuf_nr
; /* count of emitted vertices, also the
90 * largest value in dstelt. Our
99 struct _mesa_prim dstprim
[MAX_PRIM
];
105 static GLuint
type_size( GLenum type
)
108 case GL_BYTE
: return sizeof(GLbyte
);
109 case GL_UNSIGNED_BYTE
: return sizeof(GLubyte
);
110 case GL_SHORT
: return sizeof(GLshort
);
111 case GL_UNSIGNED_SHORT
: return sizeof(GLushort
);
112 case GL_INT
: return sizeof(GLint
);
113 case GL_UNSIGNED_INT
: return sizeof(GLuint
);
114 case GL_FLOAT
: return sizeof(GLfloat
);
115 case GL_DOUBLE
: return sizeof(GLdouble
);
120 static GLuint
attr_size( const struct gl_client_array
*array
)
122 return array
->Size
* type_size(array
->Type
);
126 /* Starts returning true slightly before the buffer fills, to ensure
127 * that there is sufficient room for any remaining vertices to finish
130 static GLboolean
check_flush( struct copy_context
*copy
)
132 GLenum mode
= copy
->dstprim
[copy
->dstprim_nr
].mode
;
134 if (GL_TRIANGLE_STRIP
== mode
&&
135 copy
->dstelt_nr
& 1) { /* see bug9962 */
139 if (copy
->dstbuf_nr
+ 4 > copy
->dstbuf_size
)
142 if (copy
->dstelt_nr
+ 4 > copy
->dstelt_size
)
148 static void flush( struct copy_context
*copy
)
152 /* Set some counters:
154 copy
->dstib
.count
= copy
->dstelt_nr
;
156 copy
->draw( copy
->ctx
,
164 /* Reset all pointers:
166 copy
->dstprim_nr
= 0;
169 copy
->dstptr
= copy
->dstbuf
;
171 /* Clear the vertex cache:
173 for (i
= 0; i
< ELT_TABLE_SIZE
; i
++)
174 copy
->vert_cache
[i
].in
= ~0;
179 static void begin( struct copy_context
*copy
, GLenum mode
, GLboolean begin_flag
)
181 struct _mesa_prim
*prim
= ©
->dstprim
[copy
->dstprim_nr
];
183 /* _mesa_printf("begin %s (%d)\n", _mesa_lookup_enum_by_nr(mode), begin_flag); */
186 prim
->begin
= begin_flag
;
190 /* Use a hashtable to attempt to identify recently-emitted vertices
191 * and avoid re-emitting them.
193 static GLuint
elt(struct copy_context
*copy
, GLuint elt_idx
)
195 GLuint elt
= copy
->srcelt
[elt_idx
];
196 GLuint slot
= elt
& (ELT_TABLE_SIZE
-1);
198 /* _mesa_printf("elt %d\n", elt); */
200 /* Look up the incoming element in the vertex cache. Re-emit if
203 if (copy
->vert_cache
[slot
].in
!= elt
) {
204 GLubyte
*csr
= copy
->dstptr
;
207 /* _mesa_printf(" --> emit to dstelt %d\n", copy->dstbuf_nr); */
209 for (i
= 0; i
< copy
->nr_varying
; i
++) {
210 const struct gl_client_array
*srcarray
= copy
->varying
[i
].array
;
211 const GLubyte
*srcptr
= copy
->varying
[i
].src_ptr
+ elt
* srcarray
->StrideB
;
213 memcpy(csr
, srcptr
, copy
->varying
[i
].size
);
214 csr
+= copy
->varying
[i
].size
;
218 const GLuint
*f
= (const GLuint
*)srcptr
;
220 _mesa_printf(" varying %d: ", i
);
221 for(j
= 0; j
< copy
->varying
[i
].size
/ 4; j
++)
222 _mesa_printf("%x ", f
[j
]);
228 copy
->vert_cache
[slot
].in
= elt
;
229 copy
->vert_cache
[slot
].out
= copy
->dstbuf_nr
++;
230 copy
->dstptr
+= copy
->vertex_size
;
232 assert(csr
== copy
->dstptr
);
233 assert(copy
->dstptr
== (copy
->dstbuf
+
238 /* _mesa_printf(" --> reuse vertex\n"); */
240 /* _mesa_printf(" --> emit %d\n", copy->vert_cache[slot].out); */
241 copy
->dstelt
[copy
->dstelt_nr
++] = copy
->vert_cache
[slot
].out
;
242 return check_flush(copy
);
245 static void end( struct copy_context
*copy
, GLboolean end_flag
)
247 struct _mesa_prim
*prim
= ©
->dstprim
[copy
->dstprim_nr
];
249 /* _mesa_printf("end (%d)\n", end_flag); */
251 prim
->end
= end_flag
;
252 prim
->count
= copy
->dstelt_nr
- prim
->start
;
254 if (++copy
->dstprim_nr
== MAX_PRIM
||
261 static void replay_elts( struct copy_context
*copy
)
266 for (i
= 0; i
< copy
->nr_prims
; i
++) {
267 const struct _mesa_prim
*prim
= ©
->prim
[i
];
268 const GLuint start
= prim
->start
;
271 switch (prim
->mode
) {
274 /* Convert to linestrip and emit the final vertex explicitly,
275 * but only in the resultant strip that requires it.
278 while (j
!= prim
->count
) {
279 begin(copy
, GL_LINE_STRIP
, prim
->begin
&& j
== 0);
281 for (split
= GL_FALSE
; j
!= prim
->count
&& !split
; j
++)
282 split
= elt(copy
, start
+ j
);
284 if (j
== prim
->count
) {
285 /* Done, emit final line. Split doesn't matter as
286 * it is always raised a bit early so we can emit
287 * the last verts if necessary!
290 (void)elt(copy
, start
+ 0);
292 end(copy
, prim
->end
);
304 case GL_TRIANGLE_FAN
:
307 while (j
!= prim
->count
) {
308 begin(copy
, prim
->mode
, prim
->begin
&& j
== 0);
310 split
= elt(copy
, start
+0);
313 split
= elt(copy
, start
+j
-1);
316 for (; j
!= prim
->count
&& !split
; j
++)
317 split
= elt(copy
, start
+j
);
319 end(copy
, prim
->end
&& j
== prim
->count
);
321 if (j
!= prim
->count
) {
322 /* Wrapped the primitive, need to repeat some vertices:
330 (void)split_prim_inplace(prim
->mode
, &first
, &incr
);
333 while (j
!= prim
->count
) {
335 begin(copy
, prim
->mode
, prim
->begin
&& j
== 0);
338 for (k
= 0; k
< first
; k
++, j
++)
339 split
|= elt(copy
, start
+j
);
343 for (; j
!= prim
->count
&& !split
; )
344 for (k
= 0; k
< incr
; k
++, j
++)
345 split
|= elt(copy
, start
+j
);
347 end(copy
, prim
->end
&& j
== prim
->count
);
349 if (j
!= prim
->count
) {
350 /* Wrapped the primitive, need to repeat some vertices:
352 assert(j
> first
- incr
);
360 if (copy
->dstprim_nr
)
365 static void replay_init( struct copy_context
*copy
)
367 GLcontext
*ctx
= copy
->ctx
;
370 const GLvoid
*srcptr
;
372 /* Make a list of varying attributes and their vbo's. Also
373 * calculate vertex size.
375 copy
->vertex_size
= 0;
376 for (i
= 0; i
< VERT_ATTRIB_MAX
; i
++) {
377 struct gl_buffer_object
*vbo
= copy
->array
[i
]->BufferObj
;
379 if (copy
->array
[i
]->StrideB
== 0) {
380 copy
->dstarray_ptr
[i
] = copy
->array
[i
];
383 GLuint j
= copy
->nr_varying
++;
385 copy
->varying
[j
].attr
= i
;
386 copy
->varying
[j
].array
= copy
->array
[i
];
387 copy
->varying
[j
].size
= attr_size(copy
->array
[i
]);
388 copy
->vertex_size
+= attr_size(copy
->array
[i
]);
390 if (vbo
->Name
&& !vbo
->Pointer
)
391 ctx
->Driver
.MapBuffer(ctx
,
393 GL_WRITE_ONLY
, /* XXX */
396 copy
->varying
[j
].src_ptr
= ADD_POINTERS(vbo
->Pointer
,
397 copy
->array
[i
]->Ptr
);
399 copy
->dstarray_ptr
[i
] = ©
->varying
[j
].dstarray
;
403 /* There must always be an index buffer. Currently require the
404 * caller convert non-indexed prims to indexed. Could alternately
407 if (copy
->ib
->obj
->Name
&& !copy
->ib
->obj
->Pointer
)
408 ctx
->Driver
.MapBuffer(ctx
,
409 GL_ARRAY_BUFFER_ARB
, /* XXX */
410 GL_WRITE_ONLY
, /* XXX */
413 srcptr
= (const GLubyte
*)ADD_POINTERS(copy
->ib
->obj
->Pointer
, copy
->ib
->ptr
);
415 switch (copy
->ib
->type
) {
416 case GL_UNSIGNED_BYTE
:
417 copy
->translated_elt_buf
= _mesa_malloc(sizeof(GLuint
) * copy
->ib
->count
);
418 copy
->srcelt
= copy
->translated_elt_buf
;
420 for (i
= 0; i
< copy
->ib
->count
; i
++)
421 copy
->translated_elt_buf
[i
] = ((const GLubyte
*)srcptr
)[i
];
424 case GL_UNSIGNED_SHORT
:
425 copy
->translated_elt_buf
= _mesa_malloc(sizeof(GLuint
) * copy
->ib
->count
);
426 copy
->srcelt
= copy
->translated_elt_buf
;
428 for (i
= 0; i
< copy
->ib
->count
; i
++)
429 copy
->translated_elt_buf
[i
] = ((const GLushort
*)srcptr
)[i
];
432 case GL_UNSIGNED_INT
:
433 copy
->translated_elt_buf
= NULL
;
434 copy
->srcelt
= (const GLuint
*)srcptr
;
439 /* Figure out the maximum allowed vertex buffer size:
441 if (copy
->vertex_size
* copy
->limits
->max_verts
<= copy
->limits
->max_vb_size
) {
442 copy
->dstbuf_size
= copy
->limits
->max_verts
;
445 copy
->dstbuf_size
= copy
->limits
->max_vb_size
/ copy
->vertex_size
;
448 /* Allocate an output vertex buffer:
450 * XXX: This should be a VBO!
452 copy
->dstbuf
= _mesa_malloc(copy
->dstbuf_size
*
454 copy
->dstptr
= copy
->dstbuf
;
456 /* Setup new vertex arrays to point into the output buffer:
458 for (offset
= 0, i
= 0; i
< copy
->nr_varying
; i
++) {
459 const struct gl_client_array
*src
= copy
->varying
[i
].array
;
460 struct gl_client_array
*dst
= ©
->varying
[i
].dstarray
;
462 dst
->Size
= src
->Size
;
463 dst
->Type
= src
->Type
;
464 dst
->Stride
= copy
->vertex_size
;
465 dst
->StrideB
= copy
->vertex_size
;
466 dst
->Ptr
= copy
->dstbuf
+ offset
;
467 dst
->Enabled
= GL_TRUE
;
468 dst
->Normalized
= src
->Normalized
;
469 dst
->BufferObj
= ctx
->Array
.NullBufferObj
;
470 dst
->_MaxElement
= copy
->dstbuf_size
; /* may be less! */
472 offset
+= copy
->varying
[i
].size
;
475 /* Allocate an output element list:
477 copy
->dstelt_size
= MIN2(65536,
478 copy
->ib
->count
* 2 + 3);
479 copy
->dstelt_size
= MIN2(copy
->dstelt_size
,
480 copy
->limits
->max_indices
);
481 copy
->dstelt
= _mesa_malloc(sizeof(GLuint
) * copy
->dstelt_size
);
484 /* Setup the new index buffer to point to the allocated element
487 copy
->dstib
.count
= 0; /* duplicates dstelt_nr */
488 copy
->dstib
.type
= GL_UNSIGNED_INT
;
489 copy
->dstib
.obj
= ctx
->Array
.NullBufferObj
;
490 copy
->dstib
.ptr
= copy
->dstelt
;
494 static void replay_finish( struct copy_context
*copy
)
496 GLcontext
*ctx
= copy
->ctx
;
499 /* Free our vertex and index buffers:
501 _mesa_free(copy
->translated_elt_buf
);
502 _mesa_free(copy
->dstbuf
);
503 _mesa_free(copy
->dstelt
);
507 for (i
= 0; i
< copy
->nr_varying
; i
++) {
508 struct gl_buffer_object
*vbo
= copy
->varying
[i
].array
->BufferObj
;
510 if (vbo
->Name
&& vbo
->Pointer
)
511 ctx
->Driver
.UnmapBuffer(ctx
, GL_ARRAY_BUFFER_ARB
, vbo
);
514 /* Unmap index buffer:
516 if (copy
->ib
->obj
->Name
&& copy
->ib
->obj
->Pointer
) {
517 ctx
->Driver
.UnmapBuffer(ctx
,
518 GL_ARRAY_BUFFER_ARB
, /* XXX */
523 void vbo_split_copy( GLcontext
*ctx
,
524 const struct gl_client_array
*arrays
[],
525 const struct _mesa_prim
*prim
,
527 const struct _mesa_index_buffer
*ib
,
529 const struct split_limits
*limits
)
531 struct copy_context copy
;
534 memset(©
, 0, sizeof(copy
));
536 /* Require indexed primitives:
543 copy
.nr_prims
= nr_prims
;
546 copy
.limits
= limits
;
549 /* Clear the vertex cache:
551 for (i
= 0; i
< ELT_TABLE_SIZE
; i
++)
552 copy
.vert_cache
[i
].in
= ~0;
557 replay_finish(©
);