| /* |
| * Mesa 3-D graphics library |
| * |
| * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: |
| * Keith Whitwell <keithw@vmware.com> |
| */ |
| #include <stdbool.h> |
| |
| /** |
| * \file t_dd_dmatmp.h |
| * Template for render stages which build and emit vertices directly |
| * to fixed-size dma buffers. Useful for rendering strips and other |
| * native primitives where clipping and per-vertex tweaks such as |
| * those in t_dd_tritmp.h are not required. |
| * |
| * Produces code for both inline triangles and indexed triangles. |
| * Where various primitive types are unaccelerated by hardware, the |
| * code attempts to fallback to other primitive types (quadstrips to |
| * tristrips, lineloops to linestrips), or to indexed vertices. |
| */ |
| |
| #if !HAVE_TRIANGLES || !HAVE_LINES || !HAVE_LINE_STRIPS || !HAVE_TRI_STRIPS || !HAVE_TRI_FANS |
| #error "must have lines, line strips, triangles, triangle fans, and triangle strips to use render template" |
| #endif |
| |
| #if HAVE_QUAD_STRIPS || HAVE_QUADS || HAVE_ELTS |
| #error "ELTs, quads, and quad strips not supported by render template" |
| #endif |
| |
| |
| /**********************************************************************/ |
| /* Render whole begin/end objects */ |
| /**********************************************************************/ |
| |
| static inline void *TAG(emit_verts)(struct gl_context *ctx, GLuint start, |
| GLuint count, void *buf) |
| { |
| return EMIT_VERTS(ctx, start, count, buf); |
| } |
| |
| /*********************************************************************** |
| * Render non-indexed primitives. |
| ***********************************************************************/ |
| |
| static void TAG(render_points_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| if (HAVE_POINTS) { |
| LOCAL_VARS; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS(); |
| unsigned currentsz; |
| GLuint j, nr; |
| |
| INIT(GL_POINTS); |
| |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| for (j = 0; j < count; j += nr) { |
| nr = MIN2(currentsz, count - j); |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| } else { |
| unreachable("Cannot draw primitive; validate_render should have " |
| "prevented this"); |
| } |
| } |
| |
| static void TAG(render_lines_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| LOCAL_VARS; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() & ~1; |
| unsigned currentsz; |
| GLuint j, nr; |
| |
| INIT(GL_LINES); |
| |
| /* Emit whole number of lines in total and in each buffer: |
| */ |
| count -= count & 1; |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| currentsz -= currentsz & 1; |
| |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| for (j = 0; j < count; j += nr) { |
| nr = MIN2(currentsz, count - j); |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| } |
| |
| |
| static void TAG(render_line_strip_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| LOCAL_VARS; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS(); |
| unsigned currentsz; |
| GLuint j, nr; |
| |
| INIT(GL_LINE_STRIP); |
| |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| for (j = 0; j + 1 < count; j += nr - 1) { |
| nr = MIN2(currentsz, count - j); |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| |
| FLUSH(); |
| } |
| |
| |
| static void TAG(render_line_loop_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| LOCAL_VARS; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() - 1; |
| unsigned currentsz; |
| GLuint j, nr; |
| |
| INIT(GL_LINE_STRIP); |
| |
| j = (flags & PRIM_BEGIN) ? 0 : 1; |
| |
| /* Ensure last vertex won't wrap buffers: |
| */ |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| currentsz--; |
| |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| if (j + 1 < count) { |
| for (/* empty */; j + 1 < count; j += nr - 1) { |
| nr = MIN2(currentsz, count - j); |
| |
| if (j + nr >= count && |
| count > 1 && |
| (flags & PRIM_END)) { |
| void *tmp; |
| tmp = ALLOC_VERTS(nr+1); |
| tmp = TAG(emit_verts)(ctx, start + j, nr, tmp); |
| tmp = TAG(emit_verts)( ctx, start, 1, tmp ); |
| (void) tmp; |
| } else { |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| } |
| } else if (count > 1 && (flags & PRIM_END)) { |
| void *tmp; |
| tmp = ALLOC_VERTS(2); |
| tmp = TAG(emit_verts)( ctx, start+1, 1, tmp ); |
| tmp = TAG(emit_verts)( ctx, start, 1, tmp ); |
| (void) tmp; |
| } |
| |
| FLUSH(); |
| } |
| |
| |
| static void TAG(render_triangles_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| LOCAL_VARS; |
| const unsigned dmasz = (GET_SUBSEQUENT_VB_MAX_VERTS() / 3) * 3; |
| unsigned currentsz; |
| GLuint j, nr; |
| |
| INIT(GL_TRIANGLES); |
| |
| currentsz = (GET_CURRENT_VB_MAX_VERTS() / 3) * 3; |
| |
| /* Emit whole number of tris in total. dmasz is already a multiple |
| * of 3. |
| */ |
| count -= count % 3; |
| |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| for (j = 0; j < count; j += nr) { |
| nr = MIN2(currentsz, count - j); |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| } |
| |
| |
| |
| static void TAG(render_tri_strip_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| LOCAL_VARS; |
| GLuint j, nr; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() & ~1; |
| unsigned currentsz; |
| |
| INIT(GL_TRIANGLE_STRIP); |
| |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| /* From here on emit even numbers of tris when wrapping over buffers: |
| */ |
| currentsz -= (currentsz & 1); |
| |
| for (j = 0; j + 2 < count; j += nr - 2) { |
| nr = MIN2(currentsz, count - j); |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| |
| FLUSH(); |
| } |
| |
| static void TAG(render_tri_fan_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| LOCAL_VARS; |
| GLuint j, nr; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS(); |
| unsigned currentsz; |
| |
| INIT(GL_TRIANGLE_FAN); |
| |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| for (j = 1; j + 1 < count; j += nr - 2) { |
| void *tmp; |
| nr = MIN2(currentsz, count - j + 1); |
| tmp = ALLOC_VERTS(nr); |
| tmp = TAG(emit_verts)(ctx, start, 1, tmp); |
| tmp = TAG(emit_verts)(ctx, start + j, nr - 1, tmp); |
| (void) tmp; |
| currentsz = dmasz; |
| } |
| |
| FLUSH(); |
| } |
| |
| |
| static void TAG(render_poly_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| if (HAVE_POLYGONS) { |
| LOCAL_VARS; |
| GLuint j, nr; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS(); |
| unsigned currentsz; |
| |
| INIT(GL_POLYGON); |
| |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| if (currentsz < 8) { |
| currentsz = dmasz; |
| } |
| |
| for (j = 1; j + 1 < count; j += nr - 2) { |
| void *tmp; |
| nr = MIN2(currentsz, count - j + 1); |
| tmp = ALLOC_VERTS(nr); |
| tmp = TAG(emit_verts)(ctx, start, 1, tmp); |
| tmp = TAG(emit_verts)(ctx, start + j, nr - 1, tmp); |
| (void) tmp; |
| currentsz = dmasz; |
| } |
| |
| FLUSH(); |
| } else if (ctx->Light.ShadeModel == GL_SMOOTH || |
| ctx->Light.ProvokingVertex == GL_FIRST_VERTEX_CONVENTION) { |
| TAG(render_tri_fan_verts)( ctx, start, count, flags ); |
| } else { |
| unreachable("Cannot draw primitive; validate_render should have " |
| "prevented this"); |
| } |
| } |
| |
| static void TAG(render_quad_strip_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| GLuint j, nr; |
| |
| if (ctx->Light.ShadeModel == GL_SMOOTH) { |
| LOCAL_VARS; |
| const unsigned dmasz = GET_SUBSEQUENT_VB_MAX_VERTS() & ~1; |
| unsigned currentsz; |
| |
| /* Emit smooth-shaded quadstrips as tristrips: |
| */ |
| FLUSH(); |
| INIT(GL_TRIANGLE_STRIP); |
| |
| /* Emit whole number of quads in total, and in each buffer. |
| */ |
| currentsz = GET_CURRENT_VB_MAX_VERTS(); |
| currentsz -= currentsz & 1; |
| count -= count & 1; |
| |
| if (currentsz < 8) |
| currentsz = dmasz; |
| |
| for (j = 0; j + 3 < count; j += nr - 2) { |
| nr = MIN2(currentsz, count - j); |
| TAG(emit_verts)(ctx, start + j, nr, ALLOC_VERTS(nr)); |
| currentsz = dmasz; |
| } |
| |
| FLUSH(); |
| } else { |
| unreachable("Cannot draw primitive; validate_render should have " |
| "prevented this"); |
| } |
| } |
| |
| |
| static void TAG(render_quads_verts)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| if (ctx->Light.ShadeModel == GL_SMOOTH || |
| ctx->Light.ProvokingVertex == GL_LAST_VERTEX_CONVENTION) { |
| LOCAL_VARS; |
| GLuint j; |
| |
| /* Emit whole number of quads in total. */ |
| count -= count & 3; |
| |
| /* Hardware doesn't have a quad primitive type -- try to simulate it using |
| * triangle primitive. This is a win for gears, but is it useful in the |
| * broader world? |
| */ |
| INIT(GL_TRIANGLES); |
| |
| for (j = 0; j + 3 < count; j += 4) { |
| void *tmp = ALLOC_VERTS(6); |
| /* Send v0, v1, v3 |
| */ |
| tmp = EMIT_VERTS(ctx, start + j, 2, tmp); |
| tmp = EMIT_VERTS(ctx, start + j + 3, 1, tmp); |
| /* Send v1, v2, v3 |
| */ |
| tmp = EMIT_VERTS(ctx, start + j + 1, 3, tmp); |
| (void) tmp; |
| } |
| } else { |
| unreachable("Cannot draw primitive"); |
| } |
| } |
| |
| static void TAG(render_noop)(struct gl_context *ctx, |
| GLuint start, |
| GLuint count, |
| GLuint flags) |
| { |
| (void) ctx; |
| (void) start; |
| (void) count; |
| (void) flags; |
| } |
| |
| static const tnl_render_func TAG(render_tab_verts)[GL_POLYGON+2] = |
| { |
| TAG(render_points_verts), |
| TAG(render_lines_verts), |
| TAG(render_line_loop_verts), |
| TAG(render_line_strip_verts), |
| TAG(render_triangles_verts), |
| TAG(render_tri_strip_verts), |
| TAG(render_tri_fan_verts), |
| TAG(render_quads_verts), |
| TAG(render_quad_strip_verts), |
| TAG(render_poly_verts), |
| TAG(render_noop), |
| }; |
| |
| /* Pre-check the primitives in the VB to prevent the need for |
| * fallbacks later on. |
| */ |
| static bool TAG(validate_render)(struct gl_context *ctx, |
| struct vertex_buffer *VB) |
| { |
| GLint i; |
| |
| if (VB->ClipOrMask & ~CLIP_CULL_BIT) |
| return false; |
| |
| if (VB->Elts) |
| return false; |
| |
| for (i = 0 ; i < VB->PrimitiveCount ; i++) { |
| GLuint prim = VB->Primitive[i].mode; |
| GLuint count = VB->Primitive[i].count; |
| bool ok = false; |
| |
| if (!count) |
| continue; |
| |
| switch (prim & PRIM_MODE_MASK) { |
| case GL_POINTS: |
| ok = HAVE_POINTS; |
| break; |
| case GL_LINES: |
| case GL_LINE_STRIP: |
| case GL_LINE_LOOP: |
| ok = !ctx->Line.StippleFlag; |
| break; |
| case GL_TRIANGLES: |
| case GL_TRIANGLE_STRIP: |
| case GL_TRIANGLE_FAN: |
| ok = true; |
| break; |
| case GL_POLYGON: |
| ok = (HAVE_POLYGONS) || ctx->Light.ShadeModel == GL_SMOOTH || |
| ctx->Light.ProvokingVertex == GL_FIRST_VERTEX_CONVENTION; |
| break; |
| case GL_QUAD_STRIP: |
| ok = VB->Elts || ctx->Light.ShadeModel == GL_SMOOTH; |
| break; |
| case GL_QUADS: |
| ok = ctx->Light.ShadeModel == GL_SMOOTH || |
| ctx->Light.ProvokingVertex == GL_LAST_VERTEX_CONVENTION; |
| break; |
| default: |
| break; |
| } |
| |
| if (!ok) { |
| /* fprintf(stderr, "not ok %s\n", _mesa_enum_to_string(prim & PRIM_MODE_MASK)); */ |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |