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
55static 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
65static 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
93static 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
122static 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
148static 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
199static 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
230static 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
260static 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
290static 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
328static 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
368static 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
403static 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
414static 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 */
432static 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