1
2 /*
3 * Mesa 3-D graphics library
4 * Version: 6.5
5 *
6 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions 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 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.
24 *
25 * Authors:
26 * Keith Whitwell <keith@tungstengraphics.com>
27 */
28
29 /* Split indexed primitives with per-vertex copying.
30 */
31
32 #include "main/glheader.h"
33 #include "main/bufferobj.h"
34 #include "main/imports.h"
35 #include "main/glformats.h"
36 #include "main/macros.h"
37 #include "main/mtypes.h"
38
39 #include "vbo_split.h"
40 #include "vbo.h"
41
42
43 #define ELT_TABLE_SIZE 16
44
45 /**
46 * Used for vertex-level splitting of indexed buffers. Note that
47 * non-indexed primitives may be converted to indexed in some cases
48 * (eg loops, fans) in order to use this splitting path.
49 */
50 struct copy_context {
51
52 struct gl_context *ctx;
53 const struct gl_client_array **array;
54 const struct _mesa_prim *prim;
55 GLuint nr_prims;
56 const struct _mesa_index_buffer *ib;
57 vbo_draw_func draw;
58
59 const struct split_limits *limits;
60
61 struct {
62 GLuint attr;
63 GLuint size;
64 const struct gl_client_array *array;
65 const GLubyte *src_ptr;
66
67 struct gl_client_array dstarray;
68
69 } varying[VERT_ATTRIB_MAX];
70 GLuint nr_varying;
71
72 const struct gl_client_array *dstarray_ptr[VERT_ATTRIB_MAX];
73 struct _mesa_index_buffer dstib;
74
75 GLuint *translated_elt_buf;
76 const GLuint *srcelt;
77
78 /** A baby hash table to avoid re-emitting (some) duplicate
79 * vertices when splitting indexed primitives.
80 */
81 struct {
82 GLuint in;
83 GLuint out;
84 } vert_cache[ELT_TABLE_SIZE];
85
86 GLuint vertex_size;
87 GLubyte *dstbuf;
88 GLubyte *dstptr; /**< dstptr == dstbuf + dstelt_max * vertsize */
89 GLuint dstbuf_size; /**< in vertices */
90 GLuint dstbuf_nr; /**< count of emitted vertices, also the largest value
91 * in dstelt. Our MaxIndex.
92 */
93
94 GLuint *dstelt;
95 GLuint dstelt_nr;
96 GLuint dstelt_size;
97
98 #define MAX_PRIM 32
99 struct _mesa_prim dstprim[MAX_PRIM];
100 GLuint dstprim_nr;
101
102 };
103
104
attr_size(const struct gl_client_array * array)105 static GLuint attr_size( const struct gl_client_array *array )
106 {
107 return array->Size * _mesa_sizeof_type(array->Type);
108 }
109
110
111 /**
112 * Starts returning true slightly before the buffer fills, to ensure
113 * that there is sufficient room for any remaining vertices to finish
114 * off the prim:
115 */
116 static GLboolean
check_flush(struct copy_context * copy)117 check_flush( struct copy_context *copy )
118 {
119 GLenum mode = copy->dstprim[copy->dstprim_nr].mode;
120
121 if (GL_TRIANGLE_STRIP == mode &&
122 copy->dstelt_nr & 1) { /* see bug9962 */
123 return GL_FALSE;
124 }
125
126 if (copy->dstbuf_nr + 4 > copy->dstbuf_size)
127 return GL_TRUE;
128
129 if (copy->dstelt_nr + 4 > copy->dstelt_size)
130 return GL_TRUE;
131
132 return GL_FALSE;
133 }
134
135
136 /**
137 * Dump the parameters/info for a vbo->draw() call.
138 */
139 static void
dump_draw_info(struct gl_context * ctx,const struct gl_client_array ** arrays,const struct _mesa_prim * prims,GLuint nr_prims,const struct _mesa_index_buffer * ib,GLuint min_index,GLuint max_index)140 dump_draw_info(struct gl_context *ctx,
141 const struct gl_client_array **arrays,
142 const struct _mesa_prim *prims,
143 GLuint nr_prims,
144 const struct _mesa_index_buffer *ib,
145 GLuint min_index,
146 GLuint max_index)
147 {
148 GLuint i, j;
149
150 printf("VBO Draw:\n");
151 for (i = 0; i < nr_prims; i++) {
152 printf("Prim %u of %u\n", i, nr_prims);
153 printf(" Prim mode 0x%x\n", prims[i].mode);
154 printf(" IB: %p\n", (void*) ib);
155 for (j = 0; j < VERT_ATTRIB_MAX; j++) {
156 printf(" array %d at %p:\n", j, (void*) arrays[j]);
157 printf(" enabled %d, ptr %p, size %d, type 0x%x, stride %d\n",
158 arrays[j]->Enabled, arrays[j]->Ptr,
159 arrays[j]->Size, arrays[j]->Type, arrays[j]->StrideB);
160 if (0) {
161 GLint k = prims[i].start + prims[i].count - 1;
162 GLfloat *last = (GLfloat *) (arrays[j]->Ptr + arrays[j]->Stride * k);
163 printf(" last: %f %f %f\n",
164 last[0], last[1], last[2]);
165 }
166 }
167 }
168 }
169
170
171 static void
flush(struct copy_context * copy)172 flush( struct copy_context *copy )
173 {
174 struct gl_context *ctx = copy->ctx;
175 const struct gl_client_array **saved_arrays = ctx->Array._DrawArrays;
176 GLuint i;
177
178 /* Set some counters:
179 */
180 copy->dstib.count = copy->dstelt_nr;
181
182 #if 0
183 dump_draw_info(copy->ctx,
184 copy->dstarray_ptr,
185 copy->dstprim,
186 copy->dstprim_nr,
187 ©->dstib,
188 0,
189 copy->dstbuf_nr);
190 #else
191 (void) dump_draw_info;
192 #endif
193
194 ctx->Array._DrawArrays = copy->dstarray_ptr;
195 ctx->NewDriverState |= ctx->DriverFlags.NewArray;
196
197 copy->draw( ctx,
198 copy->dstprim,
199 copy->dstprim_nr,
200 ©->dstib,
201 GL_TRUE,
202 0,
203 copy->dstbuf_nr - 1,
204 NULL );
205
206 ctx->Array._DrawArrays = saved_arrays;
207 ctx->NewDriverState |= ctx->DriverFlags.NewArray;
208
209 /* Reset all pointers:
210 */
211 copy->dstprim_nr = 0;
212 copy->dstelt_nr = 0;
213 copy->dstbuf_nr = 0;
214 copy->dstptr = copy->dstbuf;
215
216 /* Clear the vertex cache:
217 */
218 for (i = 0; i < ELT_TABLE_SIZE; i++)
219 copy->vert_cache[i].in = ~0;
220 }
221
222
223 /**
224 * Called at begin of each primitive during replay.
225 */
226 static void
begin(struct copy_context * copy,GLenum mode,GLboolean begin_flag)227 begin( struct copy_context *copy, GLenum mode, GLboolean begin_flag )
228 {
229 struct _mesa_prim *prim = ©->dstprim[copy->dstprim_nr];
230
231 prim->mode = mode;
232 prim->begin = begin_flag;
233 prim->num_instances = 1;
234 }
235
236
237 /**
238 * Use a hashtable to attempt to identify recently-emitted vertices
239 * and avoid re-emitting them.
240 */
241 static GLuint
elt(struct copy_context * copy,GLuint elt_idx)242 elt(struct copy_context *copy, GLuint elt_idx)
243 {
244 GLuint elt = copy->srcelt[elt_idx];
245 GLuint slot = elt & (ELT_TABLE_SIZE-1);
246
247 /* printf("elt %d\n", elt); */
248
249 /* Look up the incoming element in the vertex cache. Re-emit if
250 * necessary.
251 */
252 if (copy->vert_cache[slot].in != elt) {
253 GLubyte *csr = copy->dstptr;
254 GLuint i;
255
256 /* printf(" --> emit to dstelt %d\n", copy->dstbuf_nr); */
257
258 for (i = 0; i < copy->nr_varying; i++) {
259 const struct gl_client_array *srcarray = copy->varying[i].array;
260 const GLubyte *srcptr = copy->varying[i].src_ptr + elt * srcarray->StrideB;
261
262 memcpy(csr, srcptr, copy->varying[i].size);
263 csr += copy->varying[i].size;
264
265 #ifdef NAN_CHECK
266 if (srcarray->Type == GL_FLOAT) {
267 GLuint k;
268 GLfloat *f = (GLfloat *) srcptr;
269 for (k = 0; k < srcarray->Size; k++) {
270 assert(!IS_INF_OR_NAN(f[k]));
271 assert(f[k] <= 1.0e20 && f[k] >= -1.0e20);
272 }
273 }
274 #endif
275
276 if (0)
277 {
278 const GLuint *f = (const GLuint *)srcptr;
279 GLuint j;
280 printf(" varying %d: ", i);
281 for(j = 0; j < copy->varying[i].size / 4; j++)
282 printf("%x ", f[j]);
283 printf("\n");
284 }
285 }
286
287 copy->vert_cache[slot].in = elt;
288 copy->vert_cache[slot].out = copy->dstbuf_nr++;
289 copy->dstptr += copy->vertex_size;
290
291 assert(csr == copy->dstptr);
292 assert(copy->dstptr == (copy->dstbuf +
293 copy->dstbuf_nr * copy->vertex_size));
294 }
295 /* else */
296 /* printf(" --> reuse vertex\n"); */
297
298 /* printf(" --> emit %d\n", copy->vert_cache[slot].out); */
299 copy->dstelt[copy->dstelt_nr++] = copy->vert_cache[slot].out;
300 return check_flush(copy);
301 }
302
303
304 /**
305 * Called at end of each primitive during replay.
306 */
307 static void
end(struct copy_context * copy,GLboolean end_flag)308 end( struct copy_context *copy, GLboolean end_flag )
309 {
310 struct _mesa_prim *prim = ©->dstprim[copy->dstprim_nr];
311
312 /* printf("end (%d)\n", end_flag); */
313
314 prim->end = end_flag;
315 prim->count = copy->dstelt_nr - prim->start;
316
317 if (++copy->dstprim_nr == MAX_PRIM ||
318 check_flush(copy))
319 flush(copy);
320 }
321
322
323 static void
replay_elts(struct copy_context * copy)324 replay_elts( struct copy_context *copy )
325 {
326 GLuint i, j, k;
327 GLboolean split;
328
329 for (i = 0; i < copy->nr_prims; i++) {
330 const struct _mesa_prim *prim = ©->prim[i];
331 const GLuint start = prim->start;
332 GLuint first, incr;
333
334 switch (prim->mode) {
335
336 case GL_LINE_LOOP:
337 /* Convert to linestrip and emit the final vertex explicitly,
338 * but only in the resultant strip that requires it.
339 */
340 j = 0;
341 while (j != prim->count) {
342 begin(copy, GL_LINE_STRIP, prim->begin && j == 0);
343
344 for (split = GL_FALSE; j != prim->count && !split; j++)
345 split = elt(copy, start + j);
346
347 if (j == prim->count) {
348 /* Done, emit final line. Split doesn't matter as
349 * it is always raised a bit early so we can emit
350 * the last verts if necessary!
351 */
352 if (prim->end)
353 (void)elt(copy, start + 0);
354
355 end(copy, prim->end);
356 }
357 else {
358 /* Wrap
359 */
360 assert(split);
361 end(copy, 0);
362 j--;
363 }
364 }
365 break;
366
367 case GL_TRIANGLE_FAN:
368 case GL_POLYGON:
369 j = 2;
370 while (j != prim->count) {
371 begin(copy, prim->mode, prim->begin && j == 0);
372
373 split = elt(copy, start+0);
374 assert(!split);
375
376 split = elt(copy, start+j-1);
377 assert(!split);
378
379 for (; j != prim->count && !split; j++)
380 split = elt(copy, start+j);
381
382 end(copy, prim->end && j == prim->count);
383
384 if (j != prim->count) {
385 /* Wrapped the primitive, need to repeat some vertices:
386 */
387 j -= 1;
388 }
389 }
390 break;
391
392 default:
393 (void)split_prim_inplace(prim->mode, &first, &incr);
394
395 j = 0;
396 while (j != prim->count) {
397
398 begin(copy, prim->mode, prim->begin && j == 0);
399
400 split = 0;
401 for (k = 0; k < first; k++, j++)
402 split |= elt(copy, start+j);
403
404 assert(!split);
405
406 for (; j != prim->count && !split; )
407 for (k = 0; k < incr; k++, j++)
408 split |= elt(copy, start+j);
409
410 end(copy, prim->end && j == prim->count);
411
412 if (j != prim->count) {
413 /* Wrapped the primitive, need to repeat some vertices:
414 */
415 assert(j > first - incr);
416 j -= (first - incr);
417 }
418 }
419 break;
420 }
421 }
422
423 if (copy->dstprim_nr)
424 flush(copy);
425 }
426
427
428 static void
replay_init(struct copy_context * copy)429 replay_init( struct copy_context *copy )
430 {
431 struct gl_context *ctx = copy->ctx;
432 GLuint i;
433 GLuint offset;
434 const GLvoid *srcptr;
435
436 /* Make a list of varying attributes and their vbo's. Also
437 * calculate vertex size.
438 */
439 copy->vertex_size = 0;
440 for (i = 0; i < VERT_ATTRIB_MAX; i++) {
441 struct gl_buffer_object *vbo = copy->array[i]->BufferObj;
442
443 if (copy->array[i]->StrideB == 0) {
444 copy->dstarray_ptr[i] = copy->array[i];
445 }
446 else {
447 GLuint j = copy->nr_varying++;
448
449 copy->varying[j].attr = i;
450 copy->varying[j].array = copy->array[i];
451 copy->varying[j].size = attr_size(copy->array[i]);
452 copy->vertex_size += attr_size(copy->array[i]);
453
454 if (_mesa_is_bufferobj(vbo) && !_mesa_bufferobj_mapped(vbo))
455 ctx->Driver.MapBufferRange(ctx, 0, vbo->Size, GL_MAP_READ_BIT, vbo);
456
457 copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer,
458 copy->array[i]->Ptr);
459
460 copy->dstarray_ptr[i] = ©->varying[j].dstarray;
461 }
462 }
463
464 /* There must always be an index buffer. Currently require the
465 * caller convert non-indexed prims to indexed. Could alternately
466 * do it internally.
467 */
468 if (_mesa_is_bufferobj(copy->ib->obj) &&
469 !_mesa_bufferobj_mapped(copy->ib->obj))
470 ctx->Driver.MapBufferRange(ctx, 0, copy->ib->obj->Size, GL_MAP_READ_BIT,
471 copy->ib->obj);
472
473 srcptr = (const GLubyte *) ADD_POINTERS(copy->ib->obj->Pointer,
474 copy->ib->ptr);
475
476 switch (copy->ib->type) {
477 case GL_UNSIGNED_BYTE:
478 copy->translated_elt_buf = malloc(sizeof(GLuint) * copy->ib->count);
479 copy->srcelt = copy->translated_elt_buf;
480
481 for (i = 0; i < copy->ib->count; i++)
482 copy->translated_elt_buf[i] = ((const GLubyte *)srcptr)[i];
483 break;
484
485 case GL_UNSIGNED_SHORT:
486 copy->translated_elt_buf = malloc(sizeof(GLuint) * copy->ib->count);
487 copy->srcelt = copy->translated_elt_buf;
488
489 for (i = 0; i < copy->ib->count; i++)
490 copy->translated_elt_buf[i] = ((const GLushort *)srcptr)[i];
491 break;
492
493 case GL_UNSIGNED_INT:
494 copy->translated_elt_buf = NULL;
495 copy->srcelt = (const GLuint *)srcptr;
496 break;
497 }
498
499 /* Figure out the maximum allowed vertex buffer size:
500 */
501 if (copy->vertex_size * copy->limits->max_verts <= copy->limits->max_vb_size) {
502 copy->dstbuf_size = copy->limits->max_verts;
503 }
504 else {
505 copy->dstbuf_size = copy->limits->max_vb_size / copy->vertex_size;
506 }
507
508 /* Allocate an output vertex buffer:
509 *
510 * XXX: This should be a VBO!
511 */
512 copy->dstbuf = malloc(copy->dstbuf_size * copy->vertex_size);
513 copy->dstptr = copy->dstbuf;
514
515 /* Setup new vertex arrays to point into the output buffer:
516 */
517 for (offset = 0, i = 0; i < copy->nr_varying; i++) {
518 const struct gl_client_array *src = copy->varying[i].array;
519 struct gl_client_array *dst = ©->varying[i].dstarray;
520
521 dst->Size = src->Size;
522 dst->Type = src->Type;
523 dst->Format = GL_RGBA;
524 dst->Stride = copy->vertex_size;
525 dst->StrideB = copy->vertex_size;
526 dst->Ptr = copy->dstbuf + offset;
527 dst->Enabled = GL_TRUE;
528 dst->Normalized = src->Normalized;
529 dst->Integer = src->Integer;
530 dst->BufferObj = ctx->Shared->NullBufferObj;
531 dst->_ElementSize = src->_ElementSize;
532 dst->_MaxElement = copy->dstbuf_size; /* may be less! */
533
534 offset += copy->varying[i].size;
535 }
536
537 /* Allocate an output element list:
538 */
539 copy->dstelt_size = MIN2(65536,
540 copy->ib->count * 2 + 3);
541 copy->dstelt_size = MIN2(copy->dstelt_size,
542 copy->limits->max_indices);
543 copy->dstelt = malloc(sizeof(GLuint) * copy->dstelt_size);
544 copy->dstelt_nr = 0;
545
546 /* Setup the new index buffer to point to the allocated element
547 * list:
548 */
549 copy->dstib.count = 0; /* duplicates dstelt_nr */
550 copy->dstib.type = GL_UNSIGNED_INT;
551 copy->dstib.obj = ctx->Shared->NullBufferObj;
552 copy->dstib.ptr = copy->dstelt;
553 }
554
555
556 /**
557 * Free up everything allocated during split/replay.
558 */
559 static void
replay_finish(struct copy_context * copy)560 replay_finish( struct copy_context *copy )
561 {
562 struct gl_context *ctx = copy->ctx;
563 GLuint i;
564
565 /* Free our vertex and index buffers:
566 */
567 free(copy->translated_elt_buf);
568 free(copy->dstbuf);
569 free(copy->dstelt);
570
571 /* Unmap VBO's
572 */
573 for (i = 0; i < copy->nr_varying; i++) {
574 struct gl_buffer_object *vbo = copy->varying[i].array->BufferObj;
575 if (_mesa_is_bufferobj(vbo) && _mesa_bufferobj_mapped(vbo))
576 ctx->Driver.UnmapBuffer(ctx, vbo);
577 }
578
579 /* Unmap index buffer:
580 */
581 if (_mesa_is_bufferobj(copy->ib->obj) &&
582 _mesa_bufferobj_mapped(copy->ib->obj)) {
583 ctx->Driver.UnmapBuffer(ctx, copy->ib->obj);
584 }
585 }
586
587
588 /**
589 * Split VBO into smaller pieces, draw the pieces.
590 */
vbo_split_copy(struct gl_context * ctx,const struct gl_client_array * arrays[],const struct _mesa_prim * prim,GLuint nr_prims,const struct _mesa_index_buffer * ib,vbo_draw_func draw,const struct split_limits * limits)591 void vbo_split_copy( struct gl_context *ctx,
592 const struct gl_client_array *arrays[],
593 const struct _mesa_prim *prim,
594 GLuint nr_prims,
595 const struct _mesa_index_buffer *ib,
596 vbo_draw_func draw,
597 const struct split_limits *limits )
598 {
599 struct copy_context copy;
600 GLuint i, this_nr_prims;
601
602 for (i = 0; i < nr_prims;) {
603 /* Our SW TNL pipeline doesn't handle basevertex yet, so bind_indices
604 * will rebase the elements to the basevertex, and we'll only
605 * emit strings of prims with the same basevertex in one draw call.
606 */
607 for (this_nr_prims = 1; i + this_nr_prims < nr_prims;
608 this_nr_prims++) {
609 if (prim[i].basevertex != prim[i + this_nr_prims].basevertex)
610 break;
611 }
612
613 memset(©, 0, sizeof(copy));
614
615 /* Require indexed primitives:
616 */
617 assert(ib);
618
619 copy.ctx = ctx;
620 copy.array = arrays;
621 copy.prim = &prim[i];
622 copy.nr_prims = this_nr_prims;
623 copy.ib = ib;
624 copy.draw = draw;
625 copy.limits = limits;
626
627 /* Clear the vertex cache:
628 */
629 for (i = 0; i < ELT_TABLE_SIZE; i++)
630 copy.vert_cache[i].in = ~0;
631
632 replay_init(©);
633 replay_elts(©);
634 replay_finish(©);
635 }
636 }
637