1 /*
2  * Mesa 3-D graphics library
3  *
4  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Keith Whitwell <keithw@vmware.com>
26  */
27 #include <stdbool.h>
28 
29 /**
30  * \file t_dd_dmatmp.h
31  * Template for render stages which build and emit vertices directly
32  * to fixed-size dma buffers.  Useful for rendering strips and other
33  * native primitives where clipping and per-vertex tweaks such as
34  * those in t_dd_tritmp.h are not required.
35  *
36  * Produces code for both inline triangles and indexed triangles.
37  * Where various primitive types are unaccelerated by hardware, the
38  * code attempts to fallback to other primitive types (quadstrips to
39  * tristrips, lineloops to linestrips), or to indexed vertices.
40  */
41 
42 #if !HAVE_TRIANGLES || !HAVE_LINES || !HAVE_LINE_STRIPS || !HAVE_TRI_STRIPS || !HAVE_TRI_FANS
43 #error "must have lines, line strips, triangles, triangle fans, and triangle strips to use render template"
44 #endif
45 
46 #if HAVE_QUAD_STRIPS || HAVE_QUADS || HAVE_ELTS
47 #error "ELTs, quads, and quad strips not supported by render template"
48 #endif
49 
50 
51 /**********************************************************************/
52 /*                  Render whole begin/end objects                    */
53 /**********************************************************************/
54 
TAG(emit_verts)55 static inline void *TAG(emit_verts)(struct gl_context *ctx, GLuint start,
56                                     GLuint count, void *buf)
57 {
58    return EMIT_VERTS(ctx, start, count, buf);
59 }
60 
61 /***********************************************************************
62  *                    Render non-indexed primitives.
63  ***********************************************************************/
64 
TAG(render_points_verts)65 static void TAG(render_points_verts)(struct gl_context *ctx,
66                                      GLuint start,
67                                      GLuint count,
68                                      GLuint flags)
69 {
70    if (HAVE_POINTS) {
71       LOCAL_VARS;
72       const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS();
73       unsigned currentsz;
74       GLuint j, nr;
75 
76       INIT(GL_POINTS);
77 
78       currentsz = GET_CURRENT_VB_MAX_VERTS();
79       if (currentsz < 8)
80          currentsz = dmasz;
81 
82       for (j = 0; j < count; j += nr) {
83          nr = MIN2(currentsz, count - j);
84          TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
85          currentsz = dmasz;
86       }
87    } else {
88       unreachable("Cannot draw primitive; validate_render should have "
89                   "prevented this");
90    }
91 }
92 
TAG(render_lines_verts)93 static void TAG(render_lines_verts)(struct gl_context *ctx,
94                                     GLuint start,
95                                     GLuint count,
96                                     GLuint flags)
97 {
98    LOCAL_VARS;
99    const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() & ~1;
100    unsigned currentsz;
101    GLuint j, nr;
102 
103    INIT(GL_LINES);
104 
105    /* Emit whole number of lines in total and in each buffer:
106     */
107    count -= count & 1;
108    currentsz = GET_CURRENT_VB_MAX_VERTS();
109    currentsz -= currentsz & 1;
110 
111    if (currentsz < 8)
112       currentsz = dmasz;
113 
114    for (j = 0; j < count; j += nr) {
115       nr = MIN2(currentsz, count - j);
116       TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
117       currentsz = dmasz;
118    }
119 }
120 
121 
TAG(render_line_strip_verts)122 static void TAG(render_line_strip_verts)(struct gl_context *ctx,
123                                          GLuint start,
124                                          GLuint count,
125                                          GLuint flags)
126 {
127    LOCAL_VARS;
128    const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS();
129    unsigned currentsz;
130    GLuint j, nr;
131 
132    INIT(GL_LINE_STRIP);
133 
134    currentsz = GET_CURRENT_VB_MAX_VERTS();
135    if (currentsz < 8)
136       currentsz = dmasz;
137 
138    for (j = 0; j + 1 < count; j += nr - 1) {
139       nr = MIN2(currentsz, count - j);
140       TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
141       currentsz = dmasz;
142    }
143 
144    FLUSH();
145 }
146 
147 
TAG(render_line_loop_verts)148 static void TAG(render_line_loop_verts)(struct gl_context *ctx,
149                                         GLuint start,
150                                         GLuint count,
151                                         GLuint flags)
152 {
153    LOCAL_VARS;
154    const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() - 1;
155    unsigned currentsz;
156    GLuint j, nr;
157 
158    INIT(GL_LINE_STRIP);
159 
160    j = (flags & PRIM_BEGIN) ? 0 : 1;
161 
162    /* Ensure last vertex won't wrap buffers:
163     */
164    currentsz = GET_CURRENT_VB_MAX_VERTS();
165    currentsz--;
166 
167    if (currentsz < 8)
168       currentsz = dmasz;
169 
170    if (j + 1 < count) {
171       for (/* empty */; j + 1 < count; j += nr - 1) {
172          nr = MIN2(currentsz, count - j);
173 
174          if (j + nr >= count &&
175              count > 1 &&
176              (flags & PRIM_END)) {
177             void *tmp;
178             tmp = ALLOC_VERTS(nr+1);
179             tmp = TAG(emit_verts)(ctx, start + j, nr, tmp);
180             tmp = TAG(emit_verts)( ctx, start, 1, tmp );
181             (void) tmp;
182          } else {
183             TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
184             currentsz = dmasz;
185          }
186       }
187    } else if (count > 1 && (flags & PRIM_END)) {
188       void *tmp;
189       tmp = ALLOC_VERTS(2);
190       tmp = TAG(emit_verts)( ctx, start+1, 1, tmp );
191       tmp = TAG(emit_verts)( ctx, start, 1, tmp );
192       (void) tmp;
193    }
194 
195    FLUSH();
196 }
197 
198 
TAG(render_triangles_verts)199 static void TAG(render_triangles_verts)(struct gl_context *ctx,
200                                         GLuint start,
201                                         GLuint count,
202                                         GLuint flags)
203 {
204    LOCAL_VARS;
205    const unsigned dmasz = (GET_SUBSEQUENT_VB_MAX_VERTS() / 3) * 3;
206    unsigned currentsz;
207    GLuint j, nr;
208 
209    INIT(GL_TRIANGLES);
210 
211    currentsz = (GET_CURRENT_VB_MAX_VERTS() / 3) * 3;
212 
213    /* Emit whole number of tris in total.  dmasz is already a multiple
214     * of 3.
215     */
216    count -= count % 3;
217 
218    if (currentsz < 8)
219       currentsz = dmasz;
220 
221    for (j = 0; j < count; j += nr) {
222       nr = MIN2(currentsz, count - j);
223       TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
224       currentsz = dmasz;
225    }
226 }
227 
228 
229 
TAG(render_tri_strip_verts)230 static void TAG(render_tri_strip_verts)(struct gl_context *ctx,
231                                         GLuint start,
232                                         GLuint count,
233                                         GLuint flags)
234 {
235    LOCAL_VARS;
236    GLuint j, nr;
237    const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() & ~1;
238    unsigned currentsz;
239 
240    INIT(GL_TRIANGLE_STRIP);
241 
242    currentsz = GET_CURRENT_VB_MAX_VERTS();
243 
244    if (currentsz < 8)
245       currentsz = dmasz;
246 
247    /* From here on emit even numbers of tris when wrapping over buffers:
248     */
249    currentsz -= (currentsz & 1);
250 
251    for (j = 0; j + 2 < count; j += nr - 2) {
252       nr = MIN2(currentsz, count - j);
253       TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
254       currentsz = dmasz;
255    }
256 
257    FLUSH();
258 }
259 
TAG(render_tri_fan_verts)260 static void TAG(render_tri_fan_verts)(struct gl_context *ctx,
261                                       GLuint start,
262                                       GLuint count,
263                                       GLuint flags)
264 {
265    LOCAL_VARS;
266    GLuint j, nr;
267    const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS();
268    unsigned currentsz;
269 
270    INIT(GL_TRIANGLE_FAN);
271 
272    currentsz = GET_CURRENT_VB_MAX_VERTS();
273    if (currentsz < 8)
274       currentsz = dmasz;
275 
276    for (j = 1; j + 1 < count; j += nr - 2) {
277       void *tmp;
278       nr = MIN2(currentsz, count - j + 1);
279       tmp = ALLOC_VERTS(nr);
280       tmp = TAG(emit_verts)(ctx, start, 1, tmp);
281       tmp = TAG(emit_verts)(ctx, start + j, nr - 1, tmp);
282       (void) tmp;
283       currentsz = dmasz;
284    }
285 
286    FLUSH();
287 }
288 
289 
TAG(render_poly_verts)290 static void TAG(render_poly_verts)(struct gl_context *ctx,
291                                    GLuint start,
292                                    GLuint count,
293                                    GLuint flags)
294 {
295    if (HAVE_POLYGONS) {
296       LOCAL_VARS;
297       GLuint j, nr;
298       const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS();
299       unsigned currentsz;
300 
301       INIT(GL_POLYGON);
302 
303       currentsz = GET_CURRENT_VB_MAX_VERTS();
304       if (currentsz < 8) {
305          currentsz = dmasz;
306       }
307 
308       for (j = 1; j + 1 < count; j += nr - 2) {
309          void *tmp;
310          nr = MIN2(currentsz, count - j + 1);
311          tmp = ALLOC_VERTS(nr);
312          tmp = TAG(emit_verts)(ctx, start, 1, tmp);
313          tmp = TAG(emit_verts)(ctx, start + j, nr - 1, tmp);
314          (void) tmp;
315          currentsz = dmasz;
316       }
317 
318       FLUSH();
319    } else if (ctx->Light.ShadeModel == GL_SMOOTH ||
320               ctx->Light.ProvokingVertex == GL_FIRST_VERTEX_CONVENTION) {
321       TAG(render_tri_fan_verts)( ctx, start, count, flags );
322    } else {
323       unreachable("Cannot draw primitive; validate_render should have "
324                   "prevented this");
325    }
326 }
327 
TAG(render_quad_strip_verts)328 static void TAG(render_quad_strip_verts)(struct gl_context *ctx,
329                                          GLuint start,
330                                          GLuint count,
331                                          GLuint flags)
332 {
333    GLuint j, nr;
334 
335    if (ctx->Light.ShadeModel == GL_SMOOTH) {
336       LOCAL_VARS;
337       const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() & ~1;
338       unsigned currentsz;
339 
340       /* Emit smooth-shaded quadstrips as tristrips:
341        */
342       FLUSH();
343       INIT(GL_TRIANGLE_STRIP);
344 
345       /* Emit whole number of quads in total, and in each buffer.
346        */
347       currentsz = GET_CURRENT_VB_MAX_VERTS();
348       currentsz -= currentsz & 1;
349       count -= count & 1;
350 
351       if (currentsz < 8)
352          currentsz = dmasz;
353 
354       for (j = 0; j + 3 < count; j += nr - 2) {
355          nr = MIN2(currentsz, count - j);
356          TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr));
357          currentsz = dmasz;
358       }
359 
360       FLUSH();
361    } else {
362       unreachable("Cannot draw primitive; validate_render should have "
363                   "prevented this");
364    }
365 }
366 
367 
TAG(render_quads_verts)368 static void TAG(render_quads_verts)(struct gl_context *ctx,
369                                     GLuint start,
370                                     GLuint count,
371                                     GLuint flags)
372 {
373    if (ctx->Light.ShadeModel == GL_SMOOTH ||
374        ctx->Light.ProvokingVertex == GL_LAST_VERTEX_CONVENTION) {
375       LOCAL_VARS;
376       GLuint j;
377 
378       /* Emit whole number of quads in total. */
379       count -= count & 3;
380 
381       /* Hardware doesn't have a quad primitive type -- try to simulate it using
382        * triangle primitive.  This is a win for gears, but is it useful in the
383        * broader world?
384        */
385       INIT(GL_TRIANGLES);
386 
387       for (j = 0; j + 3 < count; j += 4) {
388          void *tmp = ALLOC_VERTS(6);
389          /* Send v0, v1, v3
390           */
391          tmp = EMIT_VERTS(ctx, start + j,     2, tmp);
392          tmp = EMIT_VERTS(ctx, start + j + 3, 1, tmp);
393          /* Send v1, v2, v3
394           */
395          tmp = EMIT_VERTS(ctx, start + j + 1, 3, tmp);
396          (void) tmp;
397       }
398    } else {
399       unreachable("Cannot draw primitive");
400    }
401 }
402 
TAG(render_noop)403 static void TAG(render_noop)(struct gl_context *ctx,
404                              GLuint start,
405                              GLuint count,
406                              GLuint flags)
407 {
408    (void) ctx;
409    (void) start;
410    (void) count;
411    (void) flags;
412 }
413 
414 static const tnl_render_func TAG(render_tab_verts)[GL_POLYGON+2] =
415 {
416    TAG(render_points_verts),
417    TAG(render_lines_verts),
418    TAG(render_line_loop_verts),
419    TAG(render_line_strip_verts),
420    TAG(render_triangles_verts),
421    TAG(render_tri_strip_verts),
422    TAG(render_tri_fan_verts),
423    TAG(render_quads_verts),
424    TAG(render_quad_strip_verts),
425    TAG(render_poly_verts),
426    TAG(render_noop),
427 };
428 
429 /* Pre-check the primitives in the VB to prevent the need for
430  * fallbacks later on.
431  */
TAG(validate_render)432 static bool TAG(validate_render)(struct gl_context *ctx,
433                                  struct vertex_buffer *VB)
434 {
435    GLint i;
436 
437    if (VB->ClipOrMask & ~CLIP_CULL_BIT)
438       return false;
439 
440    if (VB->Elts)
441       return false;
442 
443    for (i = 0 ; i < VB->PrimitiveCount ; i++) {
444       GLuint prim = VB->Primitive[i].mode;
445       GLuint count = VB->Primitive[i].count;
446       bool ok = false;
447 
448       if (!count)
449          continue;
450 
451       switch (prim & PRIM_MODE_MASK) {
452       case GL_POINTS:
453          ok = HAVE_POINTS;
454          break;
455       case GL_LINES:
456       case GL_LINE_STRIP:
457       case GL_LINE_LOOP:
458          ok = !ctx->Line.StippleFlag;
459          break;
460       case GL_TRIANGLES:
461       case GL_TRIANGLE_STRIP:
462       case GL_TRIANGLE_FAN:
463          ok = true;
464          break;
465       case GL_POLYGON:
466          ok = (HAVE_POLYGONS) || ctx->Light.ShadeModel == GL_SMOOTH ||
467               ctx->Light.ProvokingVertex == GL_FIRST_VERTEX_CONVENTION;
468          break;
469       case GL_QUAD_STRIP:
470          ok = VB->Elts || ctx->Light.ShadeModel == GL_SMOOTH;
471          break;
472       case GL_QUADS:
473          ok = ctx->Light.ShadeModel == GL_SMOOTH ||
474               ctx->Light.ProvokingVertex == GL_LAST_VERTEX_CONVENTION;
475          break;
476       default:
477          break;
478       }
479 
480       if (!ok) {
481 /*          fprintf(stderr, "not ok %s\n", _mesa_enum_to_string(prim & PRIM_MODE_MASK)); */
482          return false;
483       }
484    }
485 
486    return true;
487 }
488 
489