mesa: add EXT_dsa glEnableVertexArrayEXT / glDisableVertexArrayEXT
[mesa.git] / src / mesa / tnl / t_split_inplace.c
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 tnl_draw_func draw;
53
54 const struct split_limits *limits;
55 GLuint limit;
56
57 struct _mesa_prim dstprim[MAX_PRIM];
58 GLuint dstprim_nr;
59 };
60
61
62
63
64 static void
65 flush_vertex( struct split_context *split)
66 {
67 struct gl_context *ctx = split->ctx;
68 struct _mesa_index_buffer ib;
69 GLuint i;
70
71 if (!split->dstprim_nr)
72 return;
73
74 if (split->ib) {
75 ib = *split->ib;
76
77 ib.count = split->max_index - split->min_index + 1;
78 ib.ptr = (const void *)((const char *)ib.ptr +
79 split->min_index * ib.index_size);
80
81 /* Rebase the primitives to save index buffer entries. */
82 for (i = 0; i < split->dstprim_nr; i++)
83 split->dstprim[i].start -= split->min_index;
84 }
85
86 assert(split->max_index >= split->min_index);
87
88 split->draw(ctx,
89 split->array,
90 split->dstprim,
91 split->dstprim_nr,
92 split->ib ? &ib : NULL,
93 !split->ib,
94 split->min_index,
95 split->max_index,
96 NULL, 0, NULL);
97
98 split->dstprim_nr = 0;
99 split->min_index = ~0;
100 split->max_index = 0;
101 }
102
103
104 static struct _mesa_prim *
105 next_outprim(struct split_context *split)
106 {
107 if (split->dstprim_nr == MAX_PRIM-1) {
108 flush_vertex(split);
109 }
110
111 {
112 struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
113 memset(prim, 0, sizeof(*prim));
114 return prim;
115 }
116 }
117
118
119 static void
120 update_index_bounds(struct split_context *split,
121 const struct _mesa_prim *prim)
122 {
123 split->min_index = MIN2(split->min_index, prim->start);
124 split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
125 }
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 */
132 static GLuint
133 get_max_vertices(struct split_context *split,
134 const struct _mesa_prim *prim)
135 {
136 if ((prim->start > split->min_index &&
137 prim->start - split->min_index >= split->limit) ||
138 (prim->start < split->max_index &&
139 split->max_index - prim->start >= split->limit))
140 /* "prim" starts too far away from the old range. */
141 return 0;
142
143 return MIN2(split->min_index, prim->start) + split->limit - prim->start;
144 }
145
146
147 /* Break large primitives into smaller ones. If not possible, convert
148 * the primitive to indexed and pass to split_elts().
149 */
150 static void
151 split_prims(struct split_context *split)
152 {
153 GLuint i;
154
155 for (i = 0; i < split->nr_prims; i++) {
156 const struct _mesa_prim *prim = &split->prim[i];
157 GLuint first, incr;
158 GLboolean split_inplace =
159 _tnl_split_prim_inplace(prim->mode, &first, &incr);
160 GLuint available = get_max_vertices(split, prim);
161 GLuint count = prim->count - (prim->count - first) % incr;
162
163 if (prim->count < first)
164 continue;
165
166 if ((available < count && !split_inplace) ||
167 (available < first && split_inplace)) {
168 flush_vertex(split);
169 available = get_max_vertices(split, prim);
170 }
171
172 if (available >= count) {
173 struct _mesa_prim *outprim = next_outprim(split);
174
175 *outprim = *prim;
176 update_index_bounds(split, outprim);
177 }
178 else if (split_inplace) {
179 GLuint j, nr;
180
181 for (j = 0 ; j < count ;) {
182 GLuint remaining = count - j;
183 struct _mesa_prim *outprim = next_outprim(split);
184
185 nr = MIN2(available, remaining);
186 nr -= (nr - first) % incr;
187
188 outprim->mode = prim->mode;
189 outprim->begin = (j == 0 && prim->begin);
190 outprim->end = (nr == remaining && prim->end);
191 outprim->start = prim->start + j;
192 outprim->count = nr;
193 outprim->num_instances = prim->num_instances;
194 outprim->base_instance = prim->base_instance;
195
196 update_index_bounds(split, outprim);
197
198 if (nr == remaining) {
199 /* Finished */
200 j += nr;
201 }
202 else {
203 /* Wrapped the primitive */
204 j += nr - (first - incr);
205 flush_vertex(split);
206 available = get_max_vertices(split, prim);
207 }
208 }
209 }
210 else if (split->ib == NULL) {
211 /* XXX: could at least send the first max_verts off from the
212 * inplace buffers.
213 */
214
215 /* else convert to indexed primitive and pass to split_elts,
216 * which will do the necessary copying and turn it back into a
217 * vertex primitive for rendering...
218 */
219 struct _mesa_index_buffer ib;
220 struct _mesa_prim tmpprim;
221 GLuint *elts = malloc(count * sizeof(GLuint));
222 GLuint j;
223
224 for (j = 0; j < count; j++)
225 elts[j] = prim->start + j;
226
227 ib.count = count;
228 ib.index_size = 4;
229 ib.obj = split->ctx->Shared->NullBufferObj;
230 ib.ptr = elts;
231
232 tmpprim = *prim;
233 tmpprim.indexed = 1;
234 tmpprim.start = 0;
235 tmpprim.count = count;
236 tmpprim.num_instances = 1;
237 tmpprim.base_instance = 0;
238
239 flush_vertex(split);
240
241 _tnl_split_copy(split->ctx,
242 split->array,
243 &tmpprim, 1,
244 &ib,
245 split->draw,
246 split->limits);
247
248 free(elts);
249 }
250 else {
251 flush_vertex(split);
252
253 _tnl_split_copy(split->ctx,
254 split->array,
255 prim, 1,
256 split->ib,
257 split->draw,
258 split->limits);
259 }
260 }
261
262 flush_vertex(split);
263 }
264
265
266 void
267 _tnl_split_inplace(struct gl_context *ctx,
268 const struct tnl_vertex_array *arrays,
269 const struct _mesa_prim *prim,
270 GLuint nr_prims,
271 const struct _mesa_index_buffer *ib,
272 GLuint min_index,
273 GLuint max_index,
274 tnl_draw_func draw,
275 const struct split_limits *limits)
276 {
277 struct split_context split;
278
279 memset(&split, 0, sizeof(split));
280
281 split.ctx = ctx;
282 split.array = arrays;
283 split.prim = prim;
284 split.nr_prims = nr_prims;
285 split.ib = ib;
286
287 /* Empty interval, makes calculations simpler. */
288 split.min_index = ~0;
289 split.max_index = 0;
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
298