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