/*
* 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;
}