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/glformats.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    struct gl_context *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 
flush_vertex(struct split_context * split)63 static void flush_vertex( struct split_context *split )
64 {
65    struct gl_context *ctx = split->ctx;
66    const struct gl_client_array **saved_arrays = ctx->Array._DrawArrays;
67    struct _mesa_index_buffer ib;
68    GLuint i;
69 
70    if (!split->dstprim_nr)
71       return;
72 
73    if (split->ib) {
74       ib = *split->ib;
75 
76       ib.count = split->max_index - split->min_index + 1;
77       ib.ptr = (const void *)((const char *)ib.ptr +
78                               split->min_index * _mesa_sizeof_type(ib.type));
79 
80       /* Rebase the primitives to save index buffer entries. */
81       for (i = 0; i < split->dstprim_nr; i++)
82 	 split->dstprim[i].start -= split->min_index;
83    }
84 
85    assert(split->max_index >= split->min_index);
86 
87    ctx->Array._DrawArrays = split->array;
88    ctx->NewDriverState |= ctx->DriverFlags.NewArray;
89 
90    split->draw(ctx,
91 	       split->dstprim,
92 	       split->dstprim_nr,
93 	       split->ib ? &ib : NULL,
94 	       !split->ib,
95 	       split->min_index,
96 	       split->max_index,
97 	       NULL);
98 
99    ctx->Array._DrawArrays = saved_arrays;
100    ctx->NewDriverState |= ctx->DriverFlags.NewArray;
101 
102    split->dstprim_nr = 0;
103    split->min_index = ~0;
104    split->max_index = 0;
105 }
106 
107 
next_outprim(struct split_context * split)108 static struct _mesa_prim *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 
update_index_bounds(struct split_context * split,const struct _mesa_prim * prim)121 static void update_index_bounds(struct split_context *split,
122 				const struct _mesa_prim *prim)
123 {
124    split->min_index = MIN2(split->min_index, prim->start);
125    split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
126 }
127 
128 /* Return the maximum amount of vertices that can be emitted for a
129  * primitive starting at 'prim->start', depending on the previous
130  * index bounds.
131  */
get_max_vertices(struct split_context * split,const struct _mesa_prim * prim)132 static GLuint get_max_vertices(struct split_context *split,
133 			       const struct _mesa_prim *prim)
134 {
135    if ((prim->start > split->min_index &&
136 	prim->start - split->min_index >= split->limit) ||
137        (prim->start < split->max_index &&
138         split->max_index - prim->start >= split->limit))
139       /* "prim" starts too far away from the old range. */
140       return 0;
141 
142    return MIN2(split->min_index, prim->start) + split->limit - prim->start;
143 }
144 
145 /* Break large primitives into smaller ones.  If not possible, convert
146  * the primitive to indexed and pass to split_elts().
147  */
split_prims(struct split_context * split)148 static void split_prims( struct split_context *split)
149 {
150    GLuint i;
151 
152    for (i = 0; i < split->nr_prims; i++) {
153       const struct _mesa_prim *prim = &split->prim[i];
154       GLuint first, incr;
155       GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr);
156       GLuint available = get_max_vertices(split, prim);
157       GLuint count = prim->count - (prim->count - first) % incr;
158 
159       if (prim->count < first)
160 	 continue;
161 
162       if ((available < count && !split_inplace) ||
163 	  (available < first && split_inplace)) {
164 	 flush_vertex(split);
165 	 available = get_max_vertices(split, prim);
166       }
167 
168       if (available >= count) {
169 	 struct _mesa_prim *outprim = next_outprim(split);
170 
171 	 *outprim = *prim;
172 	 update_index_bounds(split, outprim);
173       }
174       else if (split_inplace) {
175 	 GLuint j, nr;
176 
177 	 for (j = 0 ; j < count ; ) {
178 	    GLuint remaining = count - j;
179 	    struct _mesa_prim *outprim = next_outprim(split);
180 
181 	    nr = MIN2( available, remaining );
182 	    nr -= (nr - first) % incr;
183 
184 	    outprim->mode = prim->mode;
185 	    outprim->begin = (j == 0 && prim->begin);
186 	    outprim->end = (nr == remaining && prim->end);
187 	    outprim->start = prim->start + j;
188 	    outprim->count = nr;
189             outprim->num_instances = prim->num_instances;
190             outprim->base_instance = prim->base_instance;
191 
192 	    update_index_bounds(split, outprim);
193 
194 	    if (nr == remaining) {
195 	       /* Finished.
196 		*/
197 	       j += nr;
198 	    }
199 	    else {
200 	       /* Wrapped the primitive:
201 		*/
202 	       j += nr - (first - incr);
203 	       flush_vertex(split);
204 	       available = get_max_vertices(split, prim);
205 	    }
206 	 }
207       }
208       else if (split->ib == NULL) {
209 	 /* XXX: could at least send the first max_verts off from the
210 	  * inplace buffers.
211 	  */
212 
213 	 /* else convert to indexed primitive and pass to split_elts,
214 	  * which will do the necessary copying and turn it back into a
215 	  * vertex primitive for rendering...
216 	  */
217 	 struct _mesa_index_buffer ib;
218 	 struct _mesa_prim tmpprim;
219 	 GLuint *elts = malloc(count * sizeof(GLuint));
220 	 GLuint j;
221 
222 	 for (j = 0; j < count; j++)
223 	    elts[j] = prim->start + j;
224 
225 	 ib.count = count;
226 	 ib.type = GL_UNSIGNED_INT;
227 	 ib.obj = split->ctx->Shared->NullBufferObj;
228 	 ib.ptr = elts;
229 
230 	 tmpprim = *prim;
231 	 tmpprim.indexed = 1;
232 	 tmpprim.start = 0;
233 	 tmpprim.count = count;
234          tmpprim.num_instances = 1;
235          tmpprim.base_instance = 0;
236 
237 	 flush_vertex(split);
238 
239 	 vbo_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 	 vbo_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 
vbo_split_inplace(struct gl_context * ctx,const struct gl_client_array * arrays[],const struct _mesa_prim * prim,GLuint nr_prims,const struct _mesa_index_buffer * ib,GLuint min_index,GLuint max_index,vbo_draw_func draw,const struct split_limits * limits)264 void vbo_split_inplace( struct gl_context *ctx,
265 			const struct gl_client_array *arrays[],
266 			const struct _mesa_prim *prim,
267 			GLuint nr_prims,
268 			const struct _mesa_index_buffer *ib,
269 			GLuint min_index,
270 			GLuint max_index,
271 			vbo_draw_func draw,
272 			const struct split_limits *limits )
273 {
274    struct split_context split;
275 
276    memset(&split, 0, sizeof(split));
277 
278    split.ctx = ctx;
279    split.array = arrays;
280    split.prim = prim;
281    split.nr_prims = nr_prims;
282    split.ib = ib;
283 
284    /* Empty interval, makes calculations simpler. */
285    split.min_index = ~0;
286    split.max_index = 0;
287 
288    split.draw = draw;
289    split.limits = limits;
290    split.limit = ib ? limits->max_indices : limits->max_verts;
291 
292    split_prims( &split );
293 }
294 
295 
296