vbo: Fix up in-place splitting for non-contiguous/indexed primitives.
[mesa.git] / src / mesa / vbo / vbo_split_inplace.c
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
30 #include "main/mtypes.h"
31 #include "main/macros.h"
32 #include "main/enums.h"
33 #include "main/image.h"
34 #include "vbo_split.h"
35
36
37 #define MAX_PRIM 32
38
39 /* Used for splitting without copying. No attempt is made to handle
40 * too large indexed vertex buffers: In general you need to copy to do
41 * that.
42 */
43 struct split_context {
44 GLcontext *ctx;
45 const struct gl_client_array **array;
46 const struct _mesa_prim *prim;
47 GLuint nr_prims;
48 const struct _mesa_index_buffer *ib;
49 GLuint min_index;
50 GLuint max_index;
51 vbo_draw_func draw;
52
53 const struct split_limits *limits;
54 GLuint limit;
55
56 struct _mesa_prim dstprim[MAX_PRIM];
57 GLuint dstprim_nr;
58 };
59
60
61
62
63 static void flush_vertex( struct split_context *split )
64 {
65 struct _mesa_index_buffer ib;
66 GLuint i;
67
68 if (!split->dstprim_nr)
69 return;
70
71 if (split->ib) {
72 ib = *split->ib;
73
74 ib.count = split->max_index - split->min_index + 1;
75 ib.ptr += split->min_index * _mesa_sizeof_type(ib.type);
76
77 /* Rebase the primitives to save index buffer entries. */
78 for (i = 0; i < split->dstprim_nr; i++)
79 split->dstprim[i].start -= split->min_index;
80 }
81
82 assert(split->max_index >= split->min_index);
83
84 split->draw(split->ctx,
85 split->array,
86 split->dstprim,
87 split->dstprim_nr,
88 split->ib ? &ib : NULL,
89 !split->ib,
90 split->min_index,
91 split->max_index);
92
93 split->dstprim_nr = 0;
94 split->min_index = ~0;
95 split->max_index = 0;
96 }
97
98
99 static struct _mesa_prim *next_outprim( struct split_context *split )
100 {
101 if (split->dstprim_nr == MAX_PRIM-1) {
102 flush_vertex(split);
103 }
104
105 {
106 struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
107 memset(prim, 0, sizeof(*prim));
108 return prim;
109 }
110 }
111
112 static void update_index_bounds(struct split_context *split,
113 const struct _mesa_prim *prim)
114 {
115 split->min_index = MIN2(split->min_index, prim->start);
116 split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
117 }
118
119 /* Return the maximum amount of vertices that can be emitted for a
120 * primitive starting at 'prim->start', depending on the previous
121 * index bounds.
122 */
123 static GLuint get_max_vertices(struct split_context *split,
124 const struct _mesa_prim *prim)
125 {
126 if ((prim->start > split->min_index &&
127 prim->start - split->min_index >= split->limit) ||
128 (prim->start < split->max_index &&
129 split->max_index - prim->start >= split->limit))
130 /* "prim" starts too far away from the old range. */
131 return 0;
132
133 return MIN2(split->min_index, prim->start) + split->limit - prim->start;
134 }
135
136 /* Break large primitives into smaller ones. If not possible, convert
137 * the primitive to indexed and pass to split_elts().
138 */
139 static void split_prims( struct split_context *split)
140 {
141 GLuint i;
142
143 for (i = 0; i < split->nr_prims; i++) {
144 const struct _mesa_prim *prim = &split->prim[i];
145 GLuint first, incr;
146 GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr);
147 GLuint available = get_max_vertices(split, prim);
148 GLuint count = prim->count - (prim->count - first) % incr;
149
150 if (prim->count < first)
151 continue;
152
153 if ((available < count && !split_inplace) ||
154 (available < first && split_inplace)) {
155 flush_vertex(split);
156 available = get_max_vertices(split, prim);
157 }
158
159 if (available >= count) {
160 struct _mesa_prim *outprim = next_outprim(split);
161
162 *outprim = *prim;
163 update_index_bounds(split, outprim);
164 }
165 else if (split_inplace) {
166 GLuint j, nr;
167
168 for (j = 0 ; j < count ; ) {
169 GLuint remaining = count - j;
170 struct _mesa_prim *outprim = next_outprim(split);
171
172 nr = MIN2( available, remaining );
173 nr -= (nr - first) % incr;
174
175 outprim->mode = prim->mode;
176 outprim->begin = (j == 0 && prim->begin);
177 outprim->end = (nr == remaining && prim->end);
178 outprim->start = prim->start + j;
179 outprim->count = nr;
180
181 update_index_bounds(split, outprim);
182
183 if (nr == remaining) {
184 /* Finished.
185 */
186 j += nr;
187 }
188 else {
189 /* Wrapped the primitive:
190 */
191 j += nr - (first - incr);
192 flush_vertex(split);
193 available = get_max_vertices(split, prim);
194 }
195 }
196 }
197 else if (split->ib == NULL) {
198 /* XXX: could at least send the first max_verts off from the
199 * inplace buffers.
200 */
201
202 /* else convert to indexed primitive and pass to split_elts,
203 * which will do the necessary copying and turn it back into a
204 * vertex primitive for rendering...
205 */
206 struct _mesa_index_buffer ib;
207 struct _mesa_prim tmpprim;
208 GLuint *elts = malloc(count * sizeof(GLuint));
209 GLuint j;
210
211 for (j = 0; j < count; j++)
212 elts[j] = prim->start + j;
213
214 ib.count = count;
215 ib.type = GL_UNSIGNED_INT;
216 ib.obj = split->ctx->Shared->NullBufferObj;
217 ib.ptr = elts;
218
219 tmpprim = *prim;
220 tmpprim.indexed = 1;
221 tmpprim.start = 0;
222 tmpprim.count = count;
223
224 flush_vertex(split);
225
226 vbo_split_copy(split->ctx,
227 split->array,
228 &tmpprim, 1,
229 &ib,
230 split->draw,
231 split->limits);
232
233 free(elts);
234 }
235 else {
236 flush_vertex(split);
237
238 vbo_split_copy(split->ctx,
239 split->array,
240 prim, 1,
241 split->ib,
242 split->draw,
243 split->limits);
244 }
245 }
246
247 flush_vertex(split);
248 }
249
250
251 void vbo_split_inplace( GLcontext *ctx,
252 const struct gl_client_array *arrays[],
253 const struct _mesa_prim *prim,
254 GLuint nr_prims,
255 const struct _mesa_index_buffer *ib,
256 GLuint min_index,
257 GLuint max_index,
258 vbo_draw_func draw,
259 const struct split_limits *limits )
260 {
261 struct split_context split;
262
263 memset(&split, 0, sizeof(split));
264
265 split.ctx = ctx;
266 split.array = arrays;
267 split.prim = prim;
268 split.nr_prims = nr_prims;
269 split.ib = ib;
270
271 /* Empty interval, makes calculations simpler. */
272 split.min_index = ~0;
273 split.max_index = 0;
274
275 split.draw = draw;
276 split.limits = limits;
277 split.limit = ib ? limits->max_indices : limits->max_verts;
278
279 split_prims( &split );
280 }
281
282