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