vbo_split_inplace.c revision 452a592ca4b1bac78eee53fb9f2f1deac7832840
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 "vbo_split.h"
34
35
36#define MAX_PRIM 32
37
38/* Used for splitting without copying.
39 */
40struct split_context {
41   GLcontext *ctx;
42   const struct gl_client_array **array;
43   const struct _mesa_prim *prim;
44   GLuint nr_prims;
45   const struct _mesa_index_buffer *ib;
46   GLuint min_index;
47   GLuint max_index;
48   vbo_draw_func draw;
49
50   const struct split_limits *limits;
51
52   struct _mesa_prim dstprim[MAX_PRIM];
53   GLuint dstprim_nr;
54};
55
56
57
58
59static void flush_vertex( struct split_context *split )
60{
61   GLuint min_index, max_index;
62
63   if (!split->dstprim_nr)
64      return;
65
66   if (split->ib) {
67      /* This should basically be multipass rendering over the same
68       * unchanging set of VBO's.  Would like the driver not to
69       * re-upload the data, or swtnl not to re-transform the
70       * vertices.
71       */
72      assert(split->max_index - split->min_index < split->limits->max_verts);
73      min_index = split->min_index;
74      max_index = split->max_index;
75   }
76   else {
77      /* Non-indexed rendering.  Cannot assume that the primitives are
78       * ordered by increasing vertex, because of entrypoints like
79       * MultiDrawArrays.
80       */
81      GLuint i;
82      min_index = split->dstprim[0].start;
83      max_index = min_index + split->dstprim[0].count - 1;
84
85      for (i = 1; i < split->dstprim_nr; i++) {
86	 GLuint tmp_min = split->dstprim[i].start;
87	 GLuint tmp_max = tmp_min + split->dstprim[i].count - 1;
88
89	 if (tmp_min < min_index)
90	    min_index = tmp_min;
91
92	 if (tmp_max > max_index)
93	    max_index = tmp_max;
94      }
95   }
96
97   assert(max_index >= min_index);
98
99   split->draw( split->ctx,
100		split->array,
101		split->dstprim,
102		split->dstprim_nr,
103		NULL,
104		min_index,
105		max_index);
106
107   split->dstprim_nr = 0;
108}
109
110
111static struct _mesa_prim *next_outprim( struct split_context *split )
112{
113   if (split->dstprim_nr == MAX_PRIM-1) {
114      flush_vertex(split);
115   }
116
117   {
118      struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
119      memset(prim, 0, sizeof(*prim));
120      return prim;
121   }
122}
123
124static int align(int value, int alignment)
125{
126   return (value + alignment - 1) & ~(alignment - 1);
127}
128
129
130
131/* Break large primitives into smaller ones.  If not possible, convert
132 * the primitive to indexed and pass to split_elts().
133 */
134static void split_prims( struct split_context *split)
135{
136   GLuint csr = 0;
137   GLuint i;
138
139   for (i = 0; i < split->nr_prims; i++) {
140      const struct _mesa_prim *prim = &split->prim[i];
141      GLuint first, incr;
142      GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr);
143      GLuint count;
144
145      /* Always wrap on an even numbered vertex to avoid problems with
146       * triangle strips.
147       */
148      GLuint available = align(split->limits->max_verts - csr - 1, 2);
149      assert(split->limits->max_verts >= csr);
150
151      if (prim->count < first)
152	 continue;
153
154      count = prim->count - (prim->count - first) % incr;
155
156
157      if ((available < count && !split_inplace) ||
158	  (available < first && split_inplace)) {
159	 flush_vertex(split);
160	 csr = 0;
161	 available = align(split->limits->max_verts - csr - 1, 2);
162      }
163
164      if (available >= count) {
165	 struct _mesa_prim *outprim = next_outprim(split);
166	 *outprim = *prim;
167	 csr += prim->count;
168	 available = align(split->limits->max_verts - csr - 1, 2);
169      }
170      else if (split_inplace) {
171	 GLuint j, nr;
172
173
174	 for (j = 0 ; j < count ; ) {
175	    GLuint remaining = count - j;
176	    struct _mesa_prim *outprim = next_outprim(split);
177
178	    nr = MIN2( available, remaining );
179
180	    nr -= (nr - first) % incr;
181
182	    outprim->mode = prim->mode;
183	    outprim->begin = (j == 0 && prim->begin);
184	    outprim->end = (nr == remaining && prim->end);
185	    outprim->start = prim->start + j;
186	    outprim->count = nr;
187
188	    if (nr == remaining) {
189	       /* Finished.
190		*/
191	       j += nr;
192	       csr += nr;
193	       available = align(split->limits->max_verts - csr - 1, 2);
194	    }
195	    else {
196	       /* Wrapped the primitive:
197		*/
198	       j += nr - (first - incr);
199	       flush_vertex(split);
200	       csr = 0;
201	       available = align(split->limits->max_verts - csr - 1, 2);
202	    }
203	 }
204      }
205      else if (split->ib == NULL) {
206	 /* XXX: could at least send the first max_verts off from the
207	  * inplace buffers.
208	  */
209
210	 /* else convert to indexed primitive and pass to split_elts,
211	  * which will do the necessary copying and turn it back into a
212	  * vertex primitive for rendering...
213	  */
214	 struct _mesa_index_buffer ib;
215	 struct _mesa_prim tmpprim;
216	 GLuint *elts = malloc(count * sizeof(GLuint));
217	 GLuint j;
218
219	 for (j = 0; j < count; j++)
220	    elts[j] = prim->start + j;
221
222	 ib.count = count;
223	 ib.type = GL_UNSIGNED_INT;
224	 ib.obj = split->ctx->Array.NullBufferObj;
225	 ib.ptr = elts;
226
227	 tmpprim = *prim;
228	 tmpprim.indexed = 1;
229	 tmpprim.start = 0;
230	 tmpprim.count = count;
231
232	 flush_vertex(split);
233
234	 vbo_split_copy(split->ctx,
235			split->array,
236			&tmpprim, 1,
237			&ib,
238			split->draw,
239			split->limits);
240
241	 free(elts);
242      }
243      else {
244	 flush_vertex(split);
245
246	 vbo_split_copy(split->ctx,
247			split->array,
248			prim, 1,
249			split->ib,
250			split->draw,
251			split->limits);
252      }
253   }
254
255   flush_vertex(split);
256}
257
258
259void vbo_split_inplace( GLcontext *ctx,
260			const struct gl_client_array *arrays[],
261			const struct _mesa_prim *prim,
262			GLuint nr_prims,
263			const struct _mesa_index_buffer *ib,
264			GLuint min_index,
265			GLuint max_index,
266			vbo_draw_func draw,
267			const struct split_limits *limits )
268{
269   struct split_context split;
270
271   memset(&split, 0, sizeof(split));
272
273   split.ctx = ctx;
274   split.array = arrays;
275   split.prim = prim;
276   split.nr_prims = nr_prims;
277   split.ib = ib;
278   split.min_index = min_index;
279   split.max_index = max_index;
280   split.draw = draw;
281   split.limits = limits;
282
283   split_prims( &split );
284}
285
286
287