1
2 /*
3 * Mesa 3-D graphics library
4 *
5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 * Keith Whitwell <keithw@vmware.com>
27 */
28
29
30 #include "main/mtypes.h"
31 #include "main/macros.h"
32 #include "main/enums.h"
33 #include "vbo/vbo.h"
34
35 #include "t_split.h"
36
37
38 #define MAX_PRIM 32
39
40 /* Used for splitting without copying. No attempt is made to handle
41 * too large indexed vertex buffers: In general you need to copy to do
42 * that.
43 */
44 struct split_context {
45 struct gl_context *ctx;
46 const struct tnl_vertex_array *array;
47 const struct _mesa_prim *prim;
48 GLuint nr_prims;
49 const struct _mesa_index_buffer *ib;
50 GLuint min_index;
51 GLuint max_index;
52 GLuint num_instances;
53 GLuint base_instance;
54 tnl_draw_func draw;
55
56 const struct split_limits *limits;
57 GLuint limit;
58
59 struct _mesa_prim dstprim[MAX_PRIM];
60 GLuint dstprim_nr;
61 };
62
63
64
65
66 static void
flush_vertex(struct split_context * split)67 flush_vertex( struct split_context *split)
68 {
69 struct gl_context *ctx = split->ctx;
70 struct _mesa_index_buffer ib;
71 GLuint i;
72
73 if (!split->dstprim_nr)
74 return;
75
76 if (split->ib) {
77 ib = *split->ib;
78
79 ib.count = split->max_index - split->min_index + 1;
80 ib.ptr = (const void *)((const char *)ib.ptr +
81 (split->min_index << ib.index_size_shift));
82
83 /* Rebase the primitives to save index buffer entries. */
84 for (i = 0; i < split->dstprim_nr; i++)
85 split->dstprim[i].start -= split->min_index;
86 }
87
88 assert(split->max_index >= split->min_index);
89
90 split->draw(ctx,
91 split->array,
92 split->dstprim,
93 split->dstprim_nr,
94 split->ib ? &ib : NULL,
95 !split->ib,
96 split->min_index,
97 split->max_index,
98 split->num_instances,
99 split->base_instance);
100
101 split->dstprim_nr = 0;
102 split->min_index = ~0;
103 split->max_index = 0;
104 }
105
106
107 static struct _mesa_prim *
next_outprim(struct split_context * split)108 next_outprim(struct split_context *split)
109 {
110 if (split->dstprim_nr == MAX_PRIM-1) {
111 flush_vertex(split);
112 }
113
114 {
115 struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
116 memset(prim, 0, sizeof(*prim));
117 return prim;
118 }
119 }
120
121
122 static void
update_index_bounds(struct split_context * split,const struct _mesa_prim * prim)123 update_index_bounds(struct split_context *split,
124 const struct _mesa_prim *prim)
125 {
126 split->min_index = MIN2(split->min_index, prim->start);
127 split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
128 }
129
130
131 /* Return the maximum amount of vertices that can be emitted for a
132 * primitive starting at 'prim->start', depending on the previous
133 * index bounds.
134 */
135 static GLuint
get_max_vertices(struct split_context * split,const struct _mesa_prim * prim)136 get_max_vertices(struct split_context *split,
137 const struct _mesa_prim *prim)
138 {
139 if ((prim->start > split->min_index &&
140 prim->start - split->min_index >= split->limit) ||
141 (prim->start < split->max_index &&
142 split->max_index - prim->start >= split->limit))
143 /* "prim" starts too far away from the old range. */
144 return 0;
145
146 return MIN2(split->min_index, prim->start) + split->limit - prim->start;
147 }
148
149
150 /* Break large primitives into smaller ones. If not possible, convert
151 * the primitive to indexed and pass to split_elts().
152 */
153 static void
split_prims(struct split_context * split)154 split_prims(struct split_context *split)
155 {
156 GLuint i;
157
158 for (i = 0; i < split->nr_prims; i++) {
159 const struct _mesa_prim *prim = &split->prim[i];
160 GLuint first, incr;
161 GLboolean split_inplace =
162 _tnl_split_prim_inplace(prim->mode, &first, &incr);
163 GLuint available = get_max_vertices(split, prim);
164 GLuint count = prim->count - (prim->count - first) % incr;
165
166 if (prim->count < first)
167 continue;
168
169 if ((available < count && !split_inplace) ||
170 (available < first && split_inplace)) {
171 flush_vertex(split);
172 available = get_max_vertices(split, prim);
173 }
174
175 if (available >= count) {
176 struct _mesa_prim *outprim = next_outprim(split);
177
178 *outprim = *prim;
179 update_index_bounds(split, outprim);
180 }
181 else if (split_inplace) {
182 GLuint j, nr;
183
184 for (j = 0 ; j < count ;) {
185 GLuint remaining = count - j;
186 struct _mesa_prim *outprim = next_outprim(split);
187
188 nr = MIN2(available, remaining);
189 nr -= (nr - first) % incr;
190
191 outprim->mode = prim->mode;
192 outprim->begin = (j == 0 && prim->begin);
193 outprim->end = (nr == remaining && prim->end);
194 outprim->start = prim->start + j;
195 outprim->count = nr;
196
197 update_index_bounds(split, outprim);
198
199 if (nr == remaining) {
200 /* Finished */
201 j += nr;
202 }
203 else {
204 /* Wrapped the primitive */
205 j += nr - (first - incr);
206 flush_vertex(split);
207 available = get_max_vertices(split, prim);
208 }
209 }
210 }
211 else if (split->ib == NULL) {
212 /* XXX: could at least send the first max_verts off from the
213 * inplace buffers.
214 */
215
216 /* else convert to indexed primitive and pass to split_elts,
217 * which will do the necessary copying and turn it back into a
218 * vertex primitive for rendering...
219 */
220 struct _mesa_index_buffer ib;
221 struct _mesa_prim tmpprim;
222 GLuint *elts = malloc(count * sizeof(GLuint));
223 GLuint j;
224
225 for (j = 0; j < count; j++)
226 elts[j] = prim->start + j;
227
228 ib.count = count;
229 ib.index_size_shift = 2;
230 ib.obj = NULL;
231 ib.ptr = elts;
232
233 tmpprim = *prim;
234 tmpprim.start = 0;
235 tmpprim.count = count;
236
237 flush_vertex(split);
238
239 _tnl_split_copy(split->ctx,
240 split->array,
241 &tmpprim, 1,
242 &ib,
243 split->draw,
244 split->limits);
245
246 free(elts);
247 }
248 else {
249 flush_vertex(split);
250
251 _tnl_split_copy(split->ctx,
252 split->array,
253 prim, 1,
254 split->ib,
255 split->draw,
256 split->limits);
257 }
258 }
259
260 flush_vertex(split);
261 }
262
263
264 void
_tnl_split_inplace(struct gl_context * ctx,const struct tnl_vertex_array * arrays,const struct _mesa_prim * prim,GLuint nr_prims,const struct _mesa_index_buffer * ib,GLuint num_instances,GLuint base_instance,tnl_draw_func draw,const struct split_limits * limits)265 _tnl_split_inplace(struct gl_context *ctx,
266 const struct tnl_vertex_array *arrays,
267 const struct _mesa_prim *prim,
268 GLuint nr_prims,
269 const struct _mesa_index_buffer *ib,
270 GLuint num_instances,
271 GLuint base_instance,
272 tnl_draw_func draw,
273 const struct split_limits *limits)
274 {
275 struct split_context split;
276
277 memset(&split, 0, sizeof(split));
278
279 split.ctx = ctx;
280 split.array = arrays;
281 split.prim = prim;
282 split.nr_prims = nr_prims;
283 split.ib = ib;
284
285 /* Empty interval, makes calculations simpler. */
286 split.min_index = ~0;
287 split.max_index = 0;
288 split.num_instances = num_instances;
289 split.base_instance = base_instance;
290
291 split.draw = draw;
292 split.limits = limits;
293 split.limit = ib ? limits->max_indices : limits->max_verts;
294
295 split_prims(&split);
296 }
297